android-內部類導致的內存泄漏實戰解析

參考: 
Android 如何有效的解決內存泄漏的問題 
Java-內部類的相關知識 
Android-LeakCanary檢測內存泄漏

非靜態內部類: 成員內部類, 局部內部類、 匿名內部類。 會有對外部類的引用。這樣內部類中耗時操作在用戶頻繁退出重啓APP相關Activity時很容易導致內存泄漏。
一、匿名內部類:Runnable
1、泄漏版
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //模擬耗時操作
                    Thread.sleep(15000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
1
2
3
4
5
6
7
8
9
10
11
連續多次退出重啓後發現:

2、優化版:
將 非靜態內部類 改爲 靜態非匿名內部類

  new Thread(new MyRunnable()).start();


  private static class MyRunnable implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
二、成員內部類:Handler
1、泄漏版:
   private final static int MESSAGECODE = 1;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("mmmmmmmm", "handler " + msg.what);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                handler.sendEmptyMessage(MESSAGECODE);
                try {
                    Thread.sleep(8000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.sendEmptyMessage(MESSAGECODE);
            }
        }).start();


    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
連續多次退出重啓後發現:

2、優化版:
2.1、使用靜態內部類 
2.2、使用弱引用 
2.3、在onDestroy() 裏面取消異步任務。(注意:單純的取消還是會內存泄漏)

    private final static int MESSAGECODE = 1;
    private static Handler handler;//靜態

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //創建Handler
        handler = new MyHandler(this);

        //創建線程並且啓動線程
        new Thread(new MyRunnable()).start();
    }


    //1、避免Handler引用activity造成的內存泄漏:使用靜態內部類+ 使用弱引用
    private static class MyHandler extends Handler {
        WeakReference<HandlerActivity> weakReference;

        public MyHandler(HandlerActivity activity) {
            weakReference = new WeakReference<HandlerActivity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (weakReference.get() != null) {
                // update android ui
                Log.d("mmmmmmmm", "handler " + msg.what);
            }
        }
    }

    //2、避免非靜態Runnable內部類引用activity造成的內存泄漏
    private static class MyRunnable implements Runnable {

        @Override
        public void run() {
            handler.sendEmptyMessage(MESSAGECODE);
            try {
                Thread.sleep(8000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler.sendEmptyMessage(MESSAGECODE);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //3、如果參數爲null的話,會將所有的Callbacks和Messages全部清除掉。
        handler.removeCallbacksAndMessages(null);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
三、匿名內部類:TimerTask
1、泄漏版:
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                while (true) ;
            }
        }, 1000);  // 1秒後啓動一個任務
1
2
3
4
5
6
連續多次退出重啓後發現:

2、優化版:
1、在適當的時機進行Cancel。 
2、TimerTask用靜態內部類

 private TimerTask timerTask ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        timerTask = new MyTimerTask() ;
        new Timer().schedule( timerTask ,1000 );  // 1秒後啓動一個任務
    }


    private static class MyTimerTask extends TimerTask {

        @Override
        public void run() {
            while(true){
                Log.d( "ttttttttt" , "timerTask" ) ;
            }
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消定時任務
        if ( timerTask != null ){
            timerTask.cancel() ;
        }

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
四、匿名內部類:AsyncTask
1、泄露版:
new AsyncTask<String,Integer,String>(){

            @Override
            protected String doInBackground(String... params) {
                try {
                    Thread.sleep( 6000 );
                } catch (InterruptedException e) {

                }
                return "ssss";
            }


            @Override
            protected void onPostExecute(String s) {
                super.onPostExecute(s);
                Log.d( "mmmmmm activity2 " , "" + s ) ;
            }

}.execute();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
連續多次退出重啓後發現:

2、優化版
1、自定義靜態AsyncTask類 
2、AsyncTask的週期和Activity週期應該保持一致。也就是在Activity生命週期結束時要將AsyncTask cancel掉。

    private static MyTask myTask;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myTask = new MyTask();
        myTask.execute();
    }

    //1、創建靜態內部類
    private static class MyTask extends AsyncTask {

        @Override
        protected Object doInBackground(Object[] params) {
            try {
                //模擬耗時操作
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "";
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //2、取消異步任務
        if (myTask != null) {
            myTask.cancel(true);
        }
    }
--------------------- 
作者:Mars-xq 
來源:CSDN 
原文:https://blog.csdn.net/sinat_31057219/article/details/74533647 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章