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。

  未完,待续.

原文地址:https://www.cnblogs.com/enjoy-coding/p/4790144.html