HILINK在运行的时候,有时会出现界面弹框消失的时候,程序崩溃现象,LOG如下,
FATAL EXCEPTION: main
Process: com.huawei.mw, PID: 10185
java.lang.IllegalArgumentException: View not attached to window manager
at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:389)
at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:318)
at android.view.WindowManagerImpl.removeViewImmediate(WindowManagerImpl.java:84)
at android.app.Dialog.dismissDialog(Dialog.java:331)
at android.app.Dialog.dismiss(Dialog.java:314)
at com.huawei.mw.plugin.app.activity.AppManagerActivity$1.handleMessage(AppManagerActivity.java:171)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5356)
原因是当Dialog调用dismiss方法的时候,WindowManager检查发现Dialog所属的Activity因为某种原因已经被杀掉,在依赖的activity上removeView的时候就会报上面的异常。
Android源码如下
void dismissDialog() {
if (mDecor == null || !mShowing) {
return;
}
if (mWindow.isDestroyed()) {
return;
}
try {
//此时Dialog所依赖的activity已经销毁,执行到此句就会出现崩溃
mWindowManager.removeViewImmediate(mDecor);
}
。。。。。。
。。。。。。
}
修改此问题的第一个方法是,在调用dialog.dismiss()的是时候对当前的activity进行判断,如果isfinish为true则不调用dialog.dismiss(),在activity的onDestroy()中对弹框资源回收。
重点介绍第二种方法:Activity有相应的操作对话框的回调,比如showDialog,onCreateDialog(),dimissDialog(),removeDialog()等等,这些都是Activity的方法,用起来比较方便,一切都由框架操作,相对来说比较安全。
另外就是一定要让对话框对象在Activity的可控制范围之内和生命周期之内,比如一定要是它的成员变量,并且在让对话框变量活跃在Activity的onCreate()和onDestroy()之间。
举个简单的例子如下:
Class DemoDialog {
final int DIALOG_ID = 0;
private Dialog demoDialog;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button = (Button) this.findViewById(R.id.Button01);
button.setOnClickListener (new View.OnClickListener()
{
@Override
public void onClick(View v)
{
showDialog(final int DIALOG_ID);
}
});
}
@Override
public Dialog onCreateDialog(int id)
{
if(id == DIALOG_ID)
{
demoDialog = new ProgressDialog(this);
demoDialog.setTitle(R.string.title);//设置标题
demoDialog.setCancelable(false);
}
return demoDialog;
}
Android SDK 源代码如下:
public final void showDialog(int id) {
showDialog(id, null);
}
public final boolean showDialog(int id, Bundle args) {
if (mManagedDialogs == null) {
mManagedDialogs = new SparseArray<ManagedDialog>();
}
ManagedDialog md = mManagedDialogs.get(id);
if (md == null) {
md = new ManagedDialog();
md.mDialog = createDialog(id, null, args);
if (md.mDialog == null) {
return false;
}
mManagedDialogs.put(id, md);
}
md.mArgs = args;
onPrepareDialog(id, md.mDialog, args);
md.mDialog.show();
return true;
}
private Dialog createDialog(Integer dialogId, Bundle state, Bundle args) {
final Dialog dialog = onCreateDialog(dialogId, args);
if (dialog == null) {
return null;
}
dialog.dispatchOnCreate(state);
return dialog;
}
由上面的代码可以看出,我们自己创建的demoDialog的对象引用值传递给了Activity中自带的md.mDialog变量,当Activity销毁的时候,会调用onDestroy方法,android源码如下:
protected void onDestroy() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
mCalled = true;
// dismiss any dialogs we are managing.
if (mManagedDialogs != null) {
final int numDialogs = mManagedDialogs.size();
for (int i = 0; i < numDialogs; i++) {
final ManagedDialog md = mManagedDialogs.valueAt(i);
if (md.mDialog.isShowing()) {
/*当activity销毁的时候,因为demoDialog和md.mDialog指向内存中的同一对象,此时demoDialog也会在当前的activity中dismiss()掉,当前弹框的isShowing值变为false*/
md.mDialog.dismiss();
}
}
mManagedDialogs = null;
}
}
我们回过头来看之前的dismissDialog()函数
void dismissDialog() {
/*当弹框在activity中自带的onDestroy中dimiss后,mShowing为false,如果开发人员再次调用demoDialog.dimiss()方法的时候直接return,就不会出现之前的崩溃问题。*/
if (mDecor == null || !mShowing) {
return;
}
。。。。。。
。。。。。。
}