从异步线程访问 UI 线程

Posted

技术标签:

【中文标题】从异步线程访问 UI 线程【英文标题】:Accessing UI Thread from Async Thread 【发布时间】:2015-06-02 16:17:51 【问题描述】:

快速提问:我一直在使用生成工作线程的框架来执行异步任务,一个很好的例子是 Retrofit。在成功/失败部分中,我可能会弹出一个对话框,该对话框需要位于 UI 线程上。我一直在访问底层 在 Retrofit 的成功/失败部分中以这种方式的 Activity/UI 线程:

Dialog dialog = new Dialog(LoginActivity.this, R.style.ThemeDialogCustom);

这在 99.9% 的情况下运行良好,但每隔一段时间,我在创建对话框时会收到以下错误:

android.view.WindowManager$BadTokenException
LoginActivity.java line 343 in LoginActivity$6.success()
Unable to add window -- token android.os.BinderProxy@41662138 is not valid;
is your activity running?

那么,我的方法是从工作线程访问 Activity 上下文/UI 线程的最稳定方法,还是我需要其他方法?

【问题讨论】:

【参考方案1】:

如果您使用线程而不使用 Asynctasks,请始终像这样运行 runOnUIThread 中更改 UI 的所有内容

activity.runOnUiThread(new Runnable() 
     @Override
     public void run() 
         //change UI
     
);

更通用的方法是这样,几乎相同

new Handler(Looper.getMainLooper()).post(new Runnable() 
         @Override
         public void run() 
             //change UI
         
    )

See here the minimal difference between runOnUIThread and MainLooper

如果你想检查你是否在 main/ui 线程上

if(Thread.currentThread() == Looper.getMainLooper().getThread()) 
   //you are on the main thread

【讨论】:

这没有多大意义,因为我必须像这样编写代码: LoginActivity.this.runOnUiThread(new Runnable() @Override public void run() Dialog dialog = new Dialog( LoginActivity.this, R.style.ThemeDialogCustom);); 为什么这没有意义?这几乎就是 asynctask 中的 onPostExecute() 的作用 那么我的问题是:LoginActivity.this.runOnUiThread(new Runnable ....) 和只调用 Dialog dialog = new Dialog(LoginActivity.this, R.style.ThemeDialogCustom) 有什么区别)? 我担心的是从工作线程调用 LoginActivity.this 的适当性,在 LoginActivity.this.ruOnUiThread(new Runnable ...) 的示例中,我仍然坚持调用 LoginActivity.this 然后只需使用 new Handler(Looper.getMainLooper()) 忽略对任何活动的依赖【参考方案2】:

AFAIK,您使用的方法没有任何问题。出现问题是因为当工作线程完成并且您尝试显示对话框时,Activity 的实例已经完成。因此,崩溃完全取决于线程完成所需的时间。似乎在您的情况下,线程大多在 Activity 仍处于活动状态时完成;因此大多数情况下您不会收到错误消息。

您需要做的是在尝试显示 Dialog 之前检查 Activity 是否仍在运行。最简单的方法之一是

if(!((Activity) LoginActivity.this).isFinishing())

    //safe to show your dialog

【讨论】:

请注意,这与我们过去在 Android AsyncTasks 中遇到的问题完全相同。有两个问题:1)当您返回到它时,LoginActivity 可能处于错误状态(已销毁),以及 2)您已经泄露了对 LoginActivity 的引用:在任务完成之前无法对它进行 GC。跨度> 就是这样,在这种情况下,Activity 没有完成。当这个特定错误发生时,我只是弹出一个对话框通知用户一些事情。

以上是关于从异步线程访问 UI 线程的主要内容,如果未能解决你的问题,请参考以下文章

异步数据处理Handler

用于运行异步请求的 Python Celery 与线程库 [关闭]

Android异步任务AsyncTask

C++ 的非线程异步 IO 简介?

异步任务(AsyncTask)

多线程更新UI的常用方法