Can't create handler inside thread that has not called Looper.prepare()类型的错误及修改方法

来源:互联网 发布:阿里云服务器免费 编辑:程序博客网 时间:2024/06/17 03:14

首先将错误帖出来,(大概很多朋友都碰到过吧):

E/AndroidRuntime( 9874): FATAL EXCEPTION: Thread-1034

E/AndroidRuntime( 9874): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
E/AndroidRuntime( 9874):        at android.os.Handler.<init>(Handler.java:205)
E/AndroidRuntime( 9874):        at android.os.Handler.<init>(Handler.java:119)
E/AndroidRuntime( 9874):        at android.app.Dialog.<init>(Dialog.java:107)
E/AndroidRuntime( 9874):        at android.app.AlertDialog.<init>(AlertDialog.java:119)
E/AndroidRuntime( 9874):        at android.app.AlertDialog$Builder.create(AlertDialog.java:994)
E/AndroidRuntime( 9874):        at com.android.camera.ActivityBase.updateStorageHint(ActivityBase.java:871)
E/AndroidRuntime( 9874):        at com.android.camera.PhotoModule.updateCameraParametersPreference(PhotoModule.java:6274)
E/AndroidRuntime( 9874):        at com.android.camera.PhotoModule.setCameraParameters(PhotoModule.java:7059)
E/AndroidRuntime( 9874):        at com.android.camera.PhotoModule.startPreview(PhotoModule.java:5605)
E/AndroidRuntime( 9874):        at com.android.camera.PhotoModule.access$1400(PhotoModule.java:190)

E/AndroidRuntime( 9874):        at com.android.camera.PhotoModule$CameraStartUpThread.run(PhotoModule.java:627)

从上述调用栈上来看,就知道是一个 CameraStartUpThread 线程中跑了关于UI显示方面的东东导致的。Android app层规定的比较死板,

一般在非 UI 的线程中不允许调弹出框,对话框之类的东东。而在上述错误中正是由于 ActivityBase.java 类的 updateStorageHint() 函数中创建了一个对话框导致的,而这个函数又被非 UI 线程 CameraStartUpThread 间接调用 。

要解决此问题也非常简单:

方法一,将对话框创建的地方放在 UiThread 中跑。

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. AlertDialog.Builder builder = new AlertDialog.Builder(this);  
  2. builder.setCancelable(false)  
  3.         .setTitle(getString(R.string.not_enough_dialog_title))  
  4.         .setMessage(message)  
  5.         .setNeutralButton(R.string.not_enough_dialog_clear,  
  6.                 new DialogInterface.OnClickListener() {  
  7.                     @Override  
  8.                     public void onClick(DialogInterface dialog, int which) {  
  9.                         startActivity(clearIntent);  
  10.                     }  
  11.                 })  
  12.         .setNegativeButton(android.R.string.cancel,  
  13.                 new DialogInterface.OnClickListener() {  
  14.                     @Override  
  15.                     public void onClick(DialogInterface dialog, int which) {  
  16.                         finish();  
  17.                     }  
  18.                 });  

源代码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. if (mNeedClearRotateDialog == null || !mNeedClearRotateDialog.isShowing()) {  
  2.                 mNeedClearRotateDialog = builder.create();  
  3.             }  
改为:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. if (mNeedClearRotateDialog == null || !mNeedClearRotateDialog.isShowing()) {  
  2.                 mActivity.runOnUiThread(new Runnable() {  
  3.                     public void run() {  
  4.                         mNeedClearRotateDialog = builder.create();  
  5.                     }  
  6.                 });  
  7.             }  

注:如果当前类不是 Activity 类的话,还需要在 runOnUiThread 前加上 mActivity 类对象来调用,如上所示。另外该 Runnable() 是一个匿名线程,该线程中变量需要是 final 类型的。这里还需要将 builder 变量改为 final 类型的。

方法二:在 CameraStartupThread() 一开始调用的地方就加上runOnUiThread

源码:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. checkStoragePathPreference(mPreferences);  
  2. if (mActivity != null) {  
  3.     Long leftSpace = Storage.getAvailableSpace();  
  4. vity.updateStorageHint(leftSpace);  
  5. }  

改为:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1.     checkStoragePathPreference(mPreferences);  
  2.     if (mActivity != null) {  
  3.         final Long leftSpace = Storage.getAvailableSpace();  
  4.         mActivity.runOnUiThread(new Runnable() {  
  5. public void run() {           
  6.     mActivity.updateStorageHint(leftSpace);  
  7. }  
  8.         });  
  9.     }  
再次重申匿名线程中的变量需要改为 final 类型的,不然会报错。

相对而言,推荐方法一,这样影响的地方比较小,如果在 mAcitivity.updateStorageHint(leftSpace) 函数调用中没有什么复杂的逻辑的话方法二也是可行的。

UiThread 一般都是用于界面显示的一些东西,不要把一些复杂的计算或者耗电的逻辑放到其中。如果有比较耗时的操作的话就建议新开线程来在后台做了。

很多 ANR 都是来源于主线程中跑了一些比较耗时耗资源的操作,导致按键响应不及时,或者等待过久 发生的。

0 0
原创粉丝点击