Android常见问题1:窗体泄露(1)
今天学习对话框AlertDialog,写一个Demo,需求是:只有一个Activitty,在这个Activity中只有一个按钮Button,当点击按钮Button时,弹出对话框,提示是否关闭该Activity,退出程序(只有一个界面).
MainActivity源码:
1 package com.my.day22_my_dialog1; 2 3 import android.os.Bundle; 4 import android.view.KeyEvent; 5 import android.view.View; 6 import android.view.View.OnClickListener; 7 import android.widget.Button; 8 import android.app.Activity; 9 import android.app.AlertDialog; 10 import android.app.Dialog; 11 import android.content.DialogInterface; 12 13 public class MainActivity extends Activity { 14 private Button bt; 15 private AlertDialog dialog; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 22 initDialog(); 23 24 bt = (Button) findViewById(R.id.bt); 25 bt.setOnClickListener(new OnClickListener() { 26 27 @Override 28 public void onClick(View v) { 29 dialog.show(); 30 } 31 }); 32 } 33 34 private void initDialog() { 35 //创建对话框创建器对象 36 AlertDialog.Builder builder = new AlertDialog.Builder(this); 37 38 builder.setTitle("关闭?"); 39 builder.setIcon(R.drawable.ic_launcher); 40 builder.setMessage("关闭该Activity?"); 41 builder.setCancelable(false);//设置点击对话框之外的内容是否取消对话框 42 builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { 43 44 @Override 45 public void onClick(DialogInterface dialog, int which) { 46 finish(); 47 } 48 }); 49 50 builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { 51 52 @Override 53 public void onClick(DialogInterface dialog, int which) { 54 dialog.dismiss(); 55 } 56 }); 57 58 59 dialog = builder.create(); 60 } 61 }
activity_man.xml布局文件:
1 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:paddingBottom="@dimen/activity_vertical_margin" 6 android:paddingLeft="@dimen/activity_horizontal_margin" 7 android:paddingRight="@dimen/activity_horizontal_margin" 8 android:paddingTop="@dimen/activity_vertical_margin" 9 tools:context=".MainActivity" > 10 11 <Button 12 android:id="@+id/bt" 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content" 15 android:text="对话框"/> 16 17 </RelativeLayout>
现在增加需求:要求点击返回键时,关闭该Activity。
方式1:
重写public boolean onKeyDown(int keyCode, KeyEvent event)方法,适合各种按键事件,可以说比较通用。
修改后的MainActivity源码:
1 package com.my.day22_my_dialog1; 2 3 import android.os.Bundle; 4 import android.view.KeyEvent; 5 import android.view.View; 6 import android.view.View.OnClickListener; 7 import android.widget.Button; 8 import android.app.Activity; 9 import android.app.AlertDialog; 10 import android.app.Dialog; 11 import android.content.DialogInterface; 12 13 public class MainActivity extends Activity { 14 private Button bt; 15 private AlertDialog dialog; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 22 initDialog(); 23 24 bt = (Button) findViewById(R.id.bt); 25 bt.setOnClickListener(new OnClickListener() { 26 27 @Override 28 public void onClick(View v) { 29 dialog.show(); 30 } 31 }); 32 } 33 34 private void initDialog() { 35 //创建对话框创建器对象 36 AlertDialog.Builder builder = new AlertDialog.Builder(this); 37 38 builder.setTitle("关闭?"); 39 builder.setIcon(R.drawable.ic_launcher); 40 builder.setMessage("关闭该Activity?"); 41 builder.setCancelable(false);//设置点击对话框之外的内容是否取消对话框 42 builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { 43 44 @Override 45 public void onClick(DialogInterface dialog, int which) { 46 finish(); 47 } 48 }); 49 50 builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { 51 52 @Override 53 public void onClick(DialogInterface dialog, int which) { 54 dialog.dismiss(); 55 } 56 }); 57 58 59 dialog = builder.create(); 60 } 61 62 //下面为增加的方法 63 @Override 64 public boolean onKeyDown(int keyCode, KeyEvent event) { 65 // TODO Auto-generated method stub 66 if(keyCode == KeyEvent.KEYCODE_BACK) 67 dialog.show(); 68 return super.onKeyDown(keyCode, event); 69 } 70 71 }
上述方法没什么太大的问题。
方法二:Activity中有一个回调方法public void onBackPressed(),从名字可以知道是当按下返回键执行的方法,那么依然需求是当按下返回键就弹出对话框,用户选择是否退出Activity,所以很自然的可以重写这个方法,让Activity被销毁,所以自然而然的有了下面的MainActivity源代码:
1 package com.my.day22_my_dialog1; 2 3 import android.os.Bundle; 4 import android.view.KeyEvent; 5 import android.view.View; 6 import android.view.View.OnClickListener; 7 import android.widget.Button; 8 import android.app.Activity; 9 import android.app.AlertDialog; 10 import android.app.Dialog; 11 import android.content.DialogInterface; 12 13 public class MainActivity extends Activity { 14 private Button bt; 15 private AlertDialog dialog; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 22 initDialog(); 23 24 bt = (Button) findViewById(R.id.bt); 25 bt.setOnClickListener(new OnClickListener() { 26 27 @Override 28 public void onClick(View v) { 29 dialog.show(); 30 } 31 }); 32 } 33 34 private void initDialog() { 35 //创建对话框创建器对象 36 AlertDialog.Builder builder = new AlertDialog.Builder(this); 37 38 builder.setTitle("关闭?"); 39 builder.setIcon(R.drawable.ic_launcher); 40 builder.setMessage("关闭该Activity?"); 41 builder.setCancelable(false);//设置点击对话框之外的内容是否取消对话框 42 builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { 43 44 @Override 45 public void onClick(DialogInterface dialog, int which) { 46 finish(); 47 } 48 }); 49 50 builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { 51 52 @Override 53 public void onClick(DialogInterface dialog, int which) { 54 dialog.dismiss(); 55 } 56 }); 57 58 59 dialog = builder.create(); 60 } 61 62 //下面为增加的方法 63 @Override 64 public void onBackPressed() { 65 // TODO Auto-generated method stub 66 super.onBackPressed(); 67 dialog.show(); 68 } 69 }
然而执行的结果是程序直接崩溃,LogCat捕获的信息如下图:
以上出错信息第一行可以得到关键信息:***Activity has leaked Window .... (leak:泄露)
原因是:系统执行onBackPressed()方法后,会继续回调Activity的onPause()、onStop()、onDestroy()等生命周期方法,当最后执行onDestroy()方法会,系统会销毁Activity,而此时Activity界面上显示的对话框依然存在,而对话框依赖的Activity对象已经不存在了,就会出现这种情况应用已经没有了Activity,而这个应用却存在一个对话框,且这个对话框是依赖之前存在的Activity,此时就出现了窗体泄露,程序崩溃。
在网上搜了很长时间,解决方式是:既然Activity最后肯定会执行onDestroy()方法,那么可以在onDestroy()中强制将对话框关闭,这样就解决了问题。
因此第二种方式MainActivity的完整源码:
package com.my.day22_my_dialog1; import android.os.Bundle; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; public class MainActivity extends Activity { private Button bt; private AlertDialog dialog; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initDialog(); bt = (Button) findViewById(R.id.bt); bt.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { dialog.show(); } }); } private void initDialog() { //创建对话框创建器对象 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("关闭?"); builder.setIcon(R.drawable.ic_launcher); builder.setMessage("关闭该Activity?"); builder.setCancelable(false);//设置点击对话框之外的内容是否取消对话框 builder.setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); dialog = builder.create(); } //下面为增加的代码 @Override protected void onDestroy() { // TODO Auto-generated method stub if(dialog!=null) dialog.dismiss(); super.onDestroy(); } @Override public void onBackPressed() { // TODO Auto-generated method stub super.onBackPressed(); dialog.show(); } }
思考:如果采用这种方法,那么在onBackPressed()方法中的方法体不起作用,有没有对话框都无所谓,反正也不起作用,也没有达到需求:“当按下返回键时,出现对话框,选择确定后才会退出当前Activity,该方法只是解决了窗体泄露,而没有解决需求”,因为当你按下返回键,不管你有没有在出现的对话框上操作,系统会自动退出当前Activity。想要想一个办法,重写onBackPressed()等方法,出现对话框,用户做出选择,然后系统才决定是否退出该Activity。
未完,待续.