在使用Dialog时,因为线程问题,在调用dismiss方法时出现了CalledFromWrongThreadException的Crash,如下:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
抛出异常为CalledFromWrongThreadException
,很明显第一反应就是出现了非ui线程进行了ui操作造成了此异常。通过分析工程代码,发现本质上是因为在非ui线程中创建了Dialog,而在主线程(即ui线程)中调用了show()
以及dismiss()
方法,我把问题模型写成测试代码如下:
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity test";
private ProgressDialog dialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
//子线程中创建Dialog
dialog = new ProgressDialog(MainActivity.this);
dialog.setCanceledOnTouchOutside(true);
dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Log.d(TAG, "Dialog onCancel thread: " + getThreadInfo());
}
});
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
Log.d(TAG, "Dialog onDismiss thread: " + getThreadInfo());
}
});
dialog.setMessage("正在加载...");
Log.d(TAG, "Dialog create thread: " + getThreadInfo());
Looper.loop();
}
}).start();
Button btn = (Button) findViewById(R.id.btn_helloworld);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//UI主线程中show,然后点击空白区域dismiss
dialog.show();
Log.d(TAG, "Dialog show thread: " + getThreadInfo());
}
});
}
/**
* 输出线程信息
*/
private String getThreadInfo(){
return "[" + Thread.currentThread().getId() + "]" +
((Looper.myLooper() == Looper.getMainLooper())? " is UI-Thread" : "");
}
}
就是Activity打开的时候,使用work子线程创建了一个Dialog,然后手动点击按钮的时候,显示Dialog。再点击空白处,dialog本应该dismiss的,但是直接crash了。抛出了CalledFromWrongThreadException
的异常。
在上面的代码中,我顺便输出了Dialog每个操作的线程ID,同时会判定是不是ui主线程。我们来看看log:
可以看到,以上出现的问题中执行Dialog操作的线程信息如下: