487

从在 UI 线程中运行代码的角度来看,两者之间有什么区别:

MainActivity.this.runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

或者

MainActivity.this.myView.post(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

private class BackgroundTask extends AsyncTask<String, Void, Bitmap> {
    protected void onPostExecute(Bitmap result) {
        Log.d("UI thread", "I am the UI thread");
    }
}
4

8 回答 8

309

这些都不是完全相同的,尽管它们都会产生相同的净效应。

第一个和第二个的区别在于,如果您在执行代码时恰好主应用程序线程上,第一个(runOnUiThread())将Runnable立即执行。第二个 ( post()) 始终将 放在Runnable事件队列的末尾,即使您已经在主应用程序线程上。

第三个,假设您创建并执行 的实例BackgroundTask,将浪费大量时间从线程池中获取线程,执行默认的 no-op doInBackground(),然后最终执行相当于 a 的操作post()。这是迄今为止三者中效率最低的。如果AsyncTask您在后台线程中确实有工作要做,请使用,而不仅仅是为了使用onPostExecute().

于 2012-10-11T23:33:44.123 回答
281

我喜欢来自HPP 的评论,它可以在任何地方使用,无需任何参数:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});
于 2014-08-11T19:01:03.300 回答
64

还有第四种使用方法Handler

new Handler().post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});
于 2012-10-11T23:36:59.190 回答
18

Pomber 的回答是可以接受的,但是我不喜欢重复创建新对象。最好的解决方案总是那些试图减轻内存占用的解决方案。是的,有自动垃圾收集,但移动设备中的内存保护属于最佳实践范围。下面的代码更新服务中的 TextView。

TextViewUpdater textViewUpdater = new TextViewUpdater();
Handler textViewUpdaterHandler = new Handler(Looper.getMainLooper());
private class TextViewUpdater implements Runnable{
    private String txt;
    @Override
    public void run() {
        searchResultTextView.setText(txt);
    }
    public void setText(String txt){
        this.txt = txt;
    }

}

它可以在任何地方使用,如下所示:

textViewUpdater.setText("Hello");
        textViewUpdaterHandler.post(textViewUpdater);
于 2015-01-05T09:24:54.150 回答
13

从 Android P 开始,您可以使用getMainExecutor()

getMainExecutor().execute(new Runnable() {
  @Override public void run() {
    // Code will run on the main thread
  }
});

来自Android 开发者文档

返回一个 Executor,它将在与此上下文关联的主线程上运行排队的任务。这是用于分派对应用程序组件(活动、服务等)的调用的线程。

来自CommonsBlog

您可以在 Context 上调用 getMainExecutor() 来获取将在主应用程序线程上执行其作业的 Executor。还有其他方法可以实现这一点,使用 Looper 和自定义 Executor 实现,但这更简单。

于 2018-03-09T03:54:48.497 回答
12

如果你需要在 Fragment 中使用,你应该使用

private Context context;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.context = context;
    }


    ((MainActivity)context).runOnUiThread(new Runnable() {
        public void run() {
            Log.d("UI thread", "I am the UI thread");
        }
    });

代替

getActivity().runOnUiThread(new Runnable() {
    public void run() {
        Log.d("UI thread", "I am the UI thread");
    }
});

因为在某些情况下会出现空指针异常,例如寻呼机片段

于 2018-04-28T14:32:10.443 回答
5

使用处理程序

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // Code here will run in UI thread
    }
});
于 2018-06-01T04:50:25.057 回答
0

科特林版本:

Handler(Looper.getMainLooper()).post {
   Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}

或者,如果您使用的是 Kotlin 协程:在协程范围内添加:

withContext(Dispatchers.Main) {
   Toast.makeText(context, "Running on UI(Main) thread.", Toast.LENGTH_LONG).show()
}
于 2021-12-08T13:35:04.307 回答