看log信息 按字面了解,Window Leaked大概就是说一个窗体泄漏了,也就是我们常说的内存泄漏,为什么窗体会泄漏呢?
我们知道Android的每一个Activity都有个WindowManager窗体管理器,同样,构建在某个Activity之上的对话框、PopupWindow也有相应的WindowManager窗体管理器。因为对话框、PopupWindown不能脱离Activity而单独存在着,所以当某个Dialog或者某个PopupWindow正在显示的时候我们去finish()了承载该Dialog(或PopupWindow)的Activity时,就会抛Window Leaked异常了,因为这个Dialog(或PopupWindow)的WindowManager已经没有谁可以附属了,所以它的窗体管理器已经泄漏了。
简单点概述就是:因为dialog或PopupWindow自身还没有dismiss,而承载它的activity就已经finish掉了,所以没有context了,所以泄露了,
解决方法:
关闭(finish)某个Activity前,要确保附属在上面的Dialog或PopupWindow已经关闭(dismiss)了。
@Override
public void onPause(){
super.onPause();
if(pw != null) {
pw.dismiss();
}
}
网络中的场景代码说明:转载声明, 下文转自:http://www.blogjava.net/wzhongyu/archive/2011/12/20/366845.html
场景描述:进入一个界面需要先去网络加载内容,浮出一个进度框提示正在加载中,这时如果按下back键返回,本意是返回上一个界面,而系统默认只是dismiss掉了进度框,需要再按一次back键才能返回上一个界面。现在想只按一次back键就返回上一个界面,催生了本次实验。具体实现参考代码:
首先继承ProgressDialog类,实现自己的进度框类,这里主要是为了在进度框处于焦点时,捕获其按键事件。具体代码如下:
public class MyDialog extends ProgressDialog { private Activity parentActivity; public MyDialog(Context context) { super(context); parentActivity = (Activity) context; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(parentActivity != null) { return parentActivity.onKeyDown(keyCode, event); } return super.onKeyDown(keyCode, event); } }
这里在进度框处于焦点状态按下back键时,调用了打开进度框的activity的按键监听事件,将相关处理交给activity进行处理。Activity的实现代码如下:
public class DialogTestActivity extends Activity { private MyDialog pDialog; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); showLoadingDialog(); } private void showLoadingDialog() { pDialog = new MyDialog(this); pDialog.setMessage("正在加载中..."); pDialog.setCancelable(true); pDialog.show(); } private void dismissLoadingDialog() { if(pDialog != null && pDialog.isShowing()) { pDialog.dismiss(); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { finish(); return super.onKeyDown(keyCode, event); } @Override protected void onDestroy() { dismissLoadingDialog(); super.onDestroy(); } }
这里在onKeyDown实现里直接调用了Activity的finish()方法来结束Activity,如果不调用此方法就不能实现想要的效果;并在onDestroy()方法里调用了关掉进度框的操作,这里是非常必要的。虽然不调用程序一样会正常返回,但是在logcat中会看到这里有问题,具体信息如下:
12-20 15:24:51.315: E/WindowManager(6759): Activity com.jade.dialog.DialogTestActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@44f493c8 that was originally added here 12-20 15:24:51.315: E/WindowManager(6759): android.view.WindowLeaked: Activity com.jade.dialog.DialogTestActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@44f493c8 that was originally added here 12-20 15:24:51.315: E/WindowManager(6759): at android.view.ViewRoot.(ViewRoot.java:247) 12-20 15:24:51.315: E/WindowManager(6759): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:148) 12-20 15:24:51.315: E/WindowManager(6759): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91) 12-20 15:24:51.315: E/WindowManager(6759): at android.view.Window$LocalWindowManager.addView(Window.java:424) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.Dialog.show(Dialog.java:241) 12-20 15:24:51.315: E/WindowManager(6759): at com.jade.dialog.DialogTestActivity.showLoadingDialog(DialogTestActivity.java:23) 12-20 15:24:51.315: E/WindowManager(6759): at com.jade.dialog.DialogTestActivity.onCreate(DialogTestActivity.java:15) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.ActivityThread.access$2300(ActivityThread.java:125) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2033) 12-20 15:24:51.315: E/WindowManager(6759): at android.os.Handler.dispatchMessage(Handler.java:99) 12-20 15:24:51.315: E/WindowManager(6759): at android.os.Looper.loop(Looper.java:123) 12-20 15:24:51.315: E/WindowManager(6759): at android.app.ActivityThread.main(ActivityThread.java:4627) 12-20 15:24:51.315: E/WindowManager(6759): at java.lang.reflect.Method.invokeNative(Native Method) 12-20 15:24:51.315: E/WindowManager(6759): at java.lang.reflect.Method.invoke(Method.java:521) 12-20 15:24:51.315: E/WindowManager(6759): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 12-20 15:24:51.315: E/WindowManager(6759): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 12-20 15:24:51.315: E/WindowManager(6759): at dalvik.system.NativeStart.main(Native Method)
出现上述问题的原因是在Activity调用finish()方法结束自己的时候,进度框尚未关闭。当然也有另外一种方案,就是修改MyDialog中按键处理部分,首先dismiss()自己,然后再调用activity的按键事件处理方法。具体代码如下:
public boolean onKeyDown(int keyCode, KeyEvent event) { if(parentActivity != null) { dismiss(); //在这里自己关掉自己喽~ return parentActivity.onKeyDown(keyCode, event); } return super.onKeyDown(keyCode, event); }