【问题标题】:Thread in Fragment only called once片段中的线程只调用一次
【发布时间】:2018-08-26 18:52:03
【问题描述】:

我正在尝试在Fragment 中实现一个更改 UI 上的某些内容的线程。因此我需要参考主线程。 根据我的研究,我发现以下代码应该可以解决问题:

new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(menuActivity, "HELLO", Toast.LENGTH_SHORT).show();
        }
    });

这只会执行一次,即使Looper 通常应该保持线程处于活动状态。尝试在Handler 中调用Looper.prepare() 将导致RuntimeException,因为每个线程只允许一个Looper编辑:我的目标是每秒永久更新一个 TextView。

我还尝试了以下方法:

Thread t = new Thread() {
        @Override
        public void run() {
            menuActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("-----------TEST");
                }

            }); 
        }
    };
t.start();

但这也只会执行一次。

我也读过this article,但我猜我的第一个sn-p 代码只是文章中显示的代码的较短版本。

这些sn-ps代码中我的错误可能在哪里?

这个问题不是重复的,因为我提出了一个完全不同的 sn-p 代码,这是我遇到的问题的基础。此外,Looper 在此线程中进行了更深入的解释。

【问题讨论】:

标签: android multithreading ui-thread


【解决方案1】:

我了解到您想在 1 秒后重复更新文本视图。这是我刚刚写的一个简单的演示。

样机屏幕

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/text_view_money"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:textColor="@android:color/black"
        android:textSize="20sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="30dp"
        android:layout_marginTop="30dp"
        android:orientation="horizontal">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="startUpdateTextViewMoney"
            android:text="Start" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="stopUpdateTextViewMoney"
            android:text="Stop" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/edit_text_money"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="setMoney"
            android:text="SET MONEY" />

    </LinearLayout>

</LinearLayout>

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private static final int UPDATE_TEXT_MONEY_INTERVAL = 1000;

    private Handler mMainHandler;

    private TextView mTextViewMoney;
    private TextView mEditTextMoney;

    private String money;

    private Runnable mUpdateTextViewMoneyTask = new Runnable() {
        @Override
        public void run() {
            if (TextUtils.isEmpty(money)) {
                mTextViewMoney.setText(String.valueOf(SystemClock.elapsedRealtime()));
            } else {
                mTextViewMoney.setText(money);
                money = null;
            }
            mMainHandler.postDelayed(this, UPDATE_TEXT_MONEY_INTERVAL);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextViewMoney = findViewById(R.id.text_view_money);
        mEditTextMoney = findViewById(R.id.edit_text_money);

        mMainHandler = new Handler(Looper.getMainLooper());
    }

    @Override
    protected void onStop() {
        super.onStop();
        stopUpdateTextViewMoney(null);
    }

    public void startUpdateTextViewMoney(View view) {
        mMainHandler.post(mUpdateTextViewMoneyTask);
    }

    public void stopUpdateTextViewMoney(View view) {
        mMainHandler.removeCallbacks(mUpdateTextViewMoneyTask);
    }

    public void setMoney(View view) {
        String money = mEditTextMoney.getText().toString();
        this.money = !TextUtils.isEmpty(money) ? money : "";
    }
}
  • 当用户按下开始按钮时,应用将开始每秒更新文本视图
  • 当用户按下停止按钮时,应用程序将停止更新文本视图。
  • 如果用户想设置下次显示的新货币,只需在编辑文本中输入,然后按设置货币即可。

【讨论】:

  • 您是否建议使用 postDelayed 而不是 @Coeus.D 建议的那样使用 Timer?
  • 我的建议是使用 Handler。你可以在这里找到详细信息androidtrainningcenter.blogspot.com/2013/12/…
  • 我现在已经实现了您的代码,但一个明显的问题是,每当您重新打开片段时,您都会一次又一次地发布多个可运行文件。
  • 基本上你总是创建一个类似的runnable并发布它。如何在不取消可运行文件的情况下防止这种情况发生?它应该在后台继续。
  • 你说“应该在后台继续”是什么意思?背景意味着您按下主页按钮或从最近的屏幕中删除应用程序?
【解决方案2】:

这只会执行一次,即使Looper 应该正常 保持线程活跃。

您似乎对Looper 的用途/功能感到困惑。 Looper 保持线程活着。如果主线程没有保持活动状态,您的应用程序将退出。但是,Looper 确实提供重复执行发布到与其关联的Handler/ThreadRunnables。每个Runnable 只执行一次。如果要多次执行相同的Runnable,则必须多次发布。例如:

Handler mainThreadHandler = new Handler(Looper.getMainLooper());
Runnable doToast = new Runnable() {
        @Override
        public void run() {
            Toast.makeText(menuActivity, "HELLO", Toast.LENGTH_SHORT).show();
        }
    });
mainThreadHandler.post(doToast); // Queue the first execution of the code in the Runnable's run() method. 
mainThreadHandler.post(doToast); // Queue the second execution of the code in the Runnable's run() method.

【讨论】:

  • 想象一个显示钱的 TextView,它必须无限次更新,使用 Thread.sleep(1000),你将如何使用给定的代码实现这一点?我确信您不能简单地将 .post() 包装在 while 循环中。
  • @ProgFroz 好吧,你并没有真正说出你最终想要做什么:)。我解释了为什么你没有做你所说的预期:)。
  • 是的,这就是为什么我在你这么说之后早些时候添加了我的目标^^
  • 根据编辑,这是stackoverflow.com/q/14814714/1214974的副本
猜你喜欢
  • 2021-04-14
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 2014-03-04
  • 1970-01-01
  • 1970-01-01
  • 2016-01-27
  • 1970-01-01
相关资源
最近更新 更多