首先将错误帖出来,(大概很多朋友都碰到过吧):
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 中跑。
AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false) .setTitle(getString(R.string.not_enough_dialog_title)) .setMessage(message) .setNeutralButton(R.string.not_enough_dialog_clear, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity(clearIntent); } }) .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } });
源代码:
if (mNeedClearRotateDialog == null || !mNeedClearRotateDialog.isShowing()) { mNeedClearRotateDialog = builder.create(); }改为:
if (mNeedClearRotateDialog == null || !mNeedClearRotateDialog.isShowing()) { mActivity.runOnUiThread(new Runnable() { public void run() { mNeedClearRotateDialog = builder.create(); } }); }
注:如果当前类不是 Activity 类的话,还需要在 runOnUiThread 前加上 mActivity 类对象来调用,如上所示。另外该 Runnable() 是一个匿名线程,该线程中变量需要是 final 类型的。这里还需要将 builder 变量改为 final 类型的。
方法二:在 CameraStartupThread() 一开始调用的地方就加上runOnUiThread
源码:
checkStoragePathPreference(mPreferences); if (mActivity != null) { Long leftSpace = Storage.getAvailableSpace(); mActivity.updateStorageHint(leftSpace); }
改为:
checkStoragePathPreference(mPreferences); if (mActivity != null) { final Long leftSpace = Storage.getAvailableSpace(); mActivity.runOnUiThread(new Runnable() { public void run() { mActivity.updateStorageHint(leftSpace); } }); }再次重申匿名线程中的变量需要改为 final 类型的,不然会报错。
相对而言,推荐方法一,这样影响的地方比较小,如果在 mAcitivity.updateStorageHint(leftSpace) 函数调用中没有什么复杂的逻辑的话方法二也是可行的。
UiThread 一般都是用于界面显示的一些东西,不要把一些复杂的计算或者耗电的逻辑放到其中。如果有比较耗时的操作的话就建议新开线程来在后台做了。
很多 ANR 都是来源于主线程中跑了一些比较耗时耗资源的操作,导致按键响应不及时,或者等待过久 发生的。