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;
}
。。。。。。
。。。。。。
}