我们在Android中如何使用runOnUiThread?

182

我试图使用UI线程,所以我编写了一个简单的测试Activity。但我认为我误解了某些东西,因为在点击按钮后,应用程序不再响应。

public class TestActivity extends Activity {

    Button btn;
    int i = 0;
     
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        btn = (Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                runThread();
            }
        });
    }
    
    private void runThread(){
        runOnUiThread (new Thread(new Runnable() {  
            public void run() {
                while(i++ < 1000){
                    btn.setText("#"+i);
                    try {
                        Thread.sleep(300);
                    } 
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
             }
        }));
    }
}
13个回答

238

以下是已经纠正的runThread函数的代码片段。

private void runThread() {

    new Thread() {
        public void run() {
            while (i++ < 1000) {
                try {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            btn.setText("#" + i);
                        }
                    });
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }.start();
}

这个不是应该立即进行垃圾回收吗?可能你需要保留一些对Thread()的引用。 - Nick
18
垃圾收集器也监视栈,即当线程正在运行时,它不会被垃圾回收。 - Miro Kropacek
@Vipul,我有一个关于手机旋转的问题:我希望一旦我旋转手机,这个线程就会运行,而不会创建新的线程。你能提供一些提示,如何防止在手机旋转时创建新的线程吗? - user1836957
我猜想,在完成后保存一个引用并将其设置为null,如果它不是null,则不创建新的引用。 - cmak

104
只需将其包装为一个函数,然后从您的后台线程调用此函数即可。
public void debugMsg(String msg) {
    final String str = msg;
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mInfo.setText(str);
        }
    });
}

5
因为展示如何访问外部作用域(final)中的数据,所以我给它点了赞。 - mariotomo

28
你的做法是颠倒了。你的按钮点击会调用runOnUiThread(),但这并不必要,因为点击处理器已经在UI线程上运行。然后,你在runOnUiThread()中的代码启动了一个新的后台线程,尝试执行UI操作,但这些操作最终失败了。
相反,直接从你的点击处理器中启动后台线程。然后,将对btn.setText()的调用包装在runOnUiThread()的调用内。

1
虽然点击处理程序已经在UI线程中,但调用runOnUiThread()是不必要的,但应该是无害的。该方法的Javadoc说:“在UI线程上运行指定的操作。如果当前线程是UI线程,则立即执行该操作。如果当前线程不是UI线程,则将该操作发布到UI线程的事件队列中。” - k2col

15
runOnUiThread(new Runnable() {
                public void run() {
                //Do something on UiThread
            }
        });

14

有几种技术可以使用 runOnUiThread(),让我们来看看所有的技术。

这是我的主线程(UI 线程),称为 AndroidBasicThreadActivity,我将以各种方式从工作线程更新它 -

public class AndroidBasicThreadActivity extends AppCompatActivity
{
    public static TextView textView;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_android_basic_thread);

        textView = (TextView) findViewById(R.id.textview);

        MyAndroidThread myTask = new MyAndroidThread(AndroidBasicThreadActivity.this);
        Thread t1 = new Thread(myTask, "Bajrang");
        t1.start();
    }
}

1.) 通过将Activity实例作为参数传递到工作者线程中

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {

        //perform heavy task here and finally update the UI with result this way - 
        activity.runOnUiThread(new Runnable()
        {
            @Override
            public void run()
            {
                AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
            }
        });
    }
}

2.) 在工作线程中使用 View 的 post(Runnable runnable) 方法

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
    public void run()
    {
     //perform heavy task here and finally update the UI with result this way - 
       AndroidBasicThreadActivity.textView.post(new Runnable()
      { 
        @Override
        public void run()
        {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });

    }
}

3.) 通过使用来自android.os包的Handler类 如果我们没有上下文(this/ getApplicationContext())或Activity的实例(AndroidBasicThreadActivity.this),那么我们必须像下面这样使用Handler类 -

class MyAndroidThread implements Runnable
{
    Activity activity;
    public MyAndroidThread(Activity activity)
    {
        this.activity = activity;
    }
    @Override
   public void run()
  {
  //perform heavy task here and finally update the UI with result this way - 
  new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
            AndroidBasicThreadActivity.textView.setText("Hello!! Android Team :-) From child thread.");
        }
    });
  }
}

谢谢。您不仅重复了有关activity的runOnUIThread的内容,还提到了所有可能调用它的方式。 - Arundale Ramanathan
谢谢。您不仅重复了有关activity的runOnUIThread的内容,还提到了所有可能调用它的方式。 - Arundale Ramanathan

13
如果在片段中使用,则简单写下。
getActivity().runOnUiThread(new Runnable() {
    @Override
    public void run() {
        // Do something on UiThread
    }
});

4

对于fragment,请使用以下内容:

requireActivity().runOnUiThread(() -> {
//your code logic
});

要使用activity,请使用以下内容:

runOnUiThread(() -> {
//your code logic
});

runOnUiThread被用于在后台线程更新用户界面。更多信息请参见:https://www.tutorialspoint.com/how-do-we-use-runonuithread-in-android


实际上,Android Studio会建议使用这个lambda版本。不需要编写run方法,也不需要加上@ Override。 - eos1d3

3
我们使用工作线程使应用更加流畅,避免ANR的出现。在工作线程中进行繁重处理后,我们可能需要更新UI。 UI只能从UI线程更新。在这种情况下,我们使用Handler或runOnUiThread,它们都有一个Runnable运行方法在UI线程中执行。 onClick方法在UI线程中运行,因此不需要在此处使用runOnUiThread。 使用Kotlin 在Activity中,
this.runOnUiThread {
      // Do stuff
}

来自Fragment,

activity?.runOnUiThread {
      // Do stuff
}

使用Java

this.runOnUiThread(new Runnable() {
     void run() {
         // Do stuff
     }
});

1

thy this:

@UiThread
    public void logMsg(final String msg) {
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                Log.d("UI thread", "I am the UI thread");


            }
        });
    }

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

        gifImageView = (GifImageView) findViewById(R.id.GifImageView);
        gifImageView.setGifImageResource(R.drawable.success1);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //dummy delay for 2 second
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //update ui on UI thread
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        gifImageView.setGifImageResource(R.drawable.success);
                    }
                });

            }
        }).start();

    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接