java 內(nèi)部類(lèi)的好處和缺點(diǎn)(下)

上一篇講了關(guān)于靜態(tài)內(nèi)部類(lèi)的作用和分類(lèi),這一篇來(lái)講一下關(guān)于非靜態(tài)內(nèi)部類(lèi)的缺點(diǎn):容易造成內(nèi)存泄露,這一篇幾乎照搬人家的博客啦,想去看原篇稍刀,可以直接點(diǎn)擊文章最后的超鏈接啦。

非靜態(tài)內(nèi)部類(lèi): 成員內(nèi)部類(lèi)敞曹, 局部?jī)?nèi)部類(lèi)账月、 匿名內(nèi)部類(lèi)。 會(huì)有對(duì)外部類(lèi)的引用澳迫。這樣內(nèi)部類(lèi)中耗時(shí)操作在用戶(hù)頻繁退出重啟APP相關(guān)Activity時(shí)很容易導(dǎo)致內(nèi)存泄漏局齿。

一、匿名內(nèi)部類(lèi):Runnable

1橄登、泄漏版

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //模擬耗時(shí)操作
                    Thread.sleep(15000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

連續(xù)多次退出重啟后發(fā)現(xiàn):

image.png

為什么抓歼?
上面代碼在 activity 中創(chuàng)建了一個(gè)匿名類(lèi) Runnable,匿名類(lèi)和非靜態(tài)內(nèi)部類(lèi)相同拢锹,會(huì)持有外部類(lèi)對(duì)象谣妻,這里也就是 activity,因此如果你在 Activity 里聲明且實(shí)例化一個(gè)匿名的 Runnable 對(duì)象面褐,則可能會(huì)發(fā)生內(nèi)存泄漏拌禾,如果這個(gè)線(xiàn)程在Activity銷(xiāo)毀后還一直在后臺(tái)執(zhí)行,那這個(gè)線(xiàn)程會(huì)繼續(xù)持有這個(gè) Activity 的引用從而不會(huì)被 GC 回收展哭,直到線(xiàn)程執(zhí)行完成湃窍。
2、優(yōu)化版:
將 非靜態(tài)內(nèi)部類(lèi) 改為 靜態(tài)非匿名內(nèi)部類(lèi)

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


  private static class MyRunnable implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

二匪傍、成員內(nèi)部類(lèi):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();


    }

連續(xù)多次退出重啟后發(fā)現(xiàn):

image.png

為什么?
當(dāng) Android Application 啟動(dòng)以后役衡,framework 會(huì)首先幫助我們完成UI線(xiàn)程的消息循環(huán)茵休,也就是在 UI 線(xiàn)程中,LoopMessageQueue榕莺、Message 等等這些實(shí)例已經(jīng)由 framework 幫我們實(shí)現(xiàn)了俐芯。所有的 Application 主要事件,比如 Activity 的生命周期方法钉鸯、Button 的點(diǎn)擊事件都包含在這個(gè) Message 里面吧史,這些 Message 都會(huì)加入到 MessageQueue 中去,所以唠雕,UI 線(xiàn)程的消息循環(huán)貫穿于整個(gè) Application 生命周期贸营,所以當(dāng)你在UI線(xiàn)程中生成 Handler 的實(shí)例,就會(huì)持有 Loop 以及 MessageQueue 的引用岩睁。并且在 Java 中非靜態(tài)內(nèi)部類(lèi)和匿名內(nèi)持有外部類(lèi)的引用钞脂,而靜態(tài)內(nèi)部類(lèi)則不會(huì)持有外部類(lèi)的引用。

2捕儒、優(yōu)化版:

使用靜態(tài)內(nèi)部類(lèi) 
使用弱引用 
在onDestroy() 里面取消異步任務(wù)冰啃。(注意:?jiǎn)渭兊娜∠€是會(huì)內(nèi)存泄漏)

 private final static int MESSAGECODE = 1;
    private static Handler handler;//靜態(tài)

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

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

        //創(chuàng)建線(xiàn)程并且啟動(dòng)線(xiàn)程
        new Thread(new MyRunnable()).start();
    }


    //1、避免Handler引用activity造成的內(nèi)存泄漏:使用靜態(tài)內(nèi)部類(lèi)+ 使用弱引用
    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肋层、避免非靜態(tài)Runnable內(nèi)部類(lèi)引用activity造成的內(nèi)存泄漏
    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亿笤、如果參數(shù)為null的話(huà),會(huì)將所有的Callbacks和Messages全部清除掉栋猖。
        handler.removeCallbacksAndMessages(null);
    }

三净薛、匿名內(nèi)部類(lèi):TimerTask

1、泄漏版:

        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                while (true) ;
            }
        }, 1000);  // 1秒后啟動(dòng)一個(gè)任務(wù)

連續(xù)多次退出重啟后發(fā)現(xiàn):

image.png

為什么蒲拉?
這里內(nèi)存泄漏在于 TimerTimerTask 沒(méi)有進(jìn)行 Cancel肃拜,從而導(dǎo)致 TimerTimerTask 一直引用外部類(lèi) Activity

2雌团、優(yōu)化版:

1燃领、在適當(dāng)?shù)臅r(shí)機(jī)進(jìn)行Cancel。 
2锦援、TimerTask用靜態(tài)內(nèi)部類(lèi)
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秒后啟動(dòng)一個(gè)任務(wù)
    }


    private static class MyTimerTask extends TimerTask {

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


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

    }

四猛蔽、匿名內(nèi)部類(lèi):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();

連續(xù)多次退出重啟后發(fā)現(xiàn):

image.png

為什么灵寺?
上面代碼在 activity 中創(chuàng)建了一個(gè)匿名類(lèi) AsyncTask曼库,匿名類(lèi)和非靜態(tài)內(nèi)部類(lèi)相同,會(huì)持有外部類(lèi)對(duì)象略板,這里也就是 activity毁枯,因此如果你在 Activity 里聲明且實(shí)例化一個(gè)匿名的 AsyncTask 對(duì)象,則可能會(huì)發(fā)生內(nèi)存泄漏叮称,如果這個(gè)線(xiàn)程在 Activity 銷(xiāo)毀后還一直在后臺(tái)執(zhí)行种玛,那這個(gè)線(xiàn)程會(huì)繼續(xù)持有這個(gè) Activity 的引用從而不會(huì)被 GC 回收藐鹤,直到線(xiàn)程執(zhí)行完成。
2赂韵、優(yōu)化版

1娱节、自定義靜態(tài)AsyncTask類(lèi) 
2、AsyncTask的周期和Activity周期應(yīng)該保持一致右锨。也就是在Activity生命周期結(jié)束時(shí)要將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碌秸、創(chuàng)建靜態(tài)內(nèi)部類(lèi)
    private static class MyTask extends AsyncTask {

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


    @Override
    protected void onDestroy() {
        super.onDestroy();
        //2绍移、取消異步任務(wù)
        if (myTask != null) {
            myTask.cancel(true);
        }
    }

最后

你們要的原篇地址,參考鏈接:
android-內(nèi)部類(lèi)導(dǎo)致的內(nèi)存泄漏實(shí)戰(zhàn)解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讥电,一起剝皮案震驚了整個(gè)濱河市蹂窖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恩敌,老刑警劉巖瞬测,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異纠炮,居然都是意外死亡月趟,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)恢口,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)孝宗,“玉大人,你說(shuō)我怎么就攤上這事耕肩∫蚋荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵猿诸,是天一觀的道長(zhǎng)婚被。 經(jīng)常有香客問(wèn)我,道長(zhǎng)梳虽,這世上最難降的妖魔是什么址芯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮窜觉,結(jié)果婚禮上谷炸,老公的妹妹穿的比我還像新娘。我一直安慰自己竖螃,他們只是感情好淑廊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著特咆,像睡著了一般季惩。 火紅的嫁衣襯著肌膚如雪录粱。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天画拾,我揣著相機(jī)與錄音啥繁,去河邊找鬼。 笑死青抛,一個(gè)胖子當(dāng)著我的面吹牛旗闽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜜另,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼适室,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了举瑰?” 一聲冷哼從身側(cè)響起捣辆,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎此迅,沒(méi)想到半個(gè)月后汽畴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡耸序,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年忍些,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坎怪。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡罢坝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出芋忿,到底是詐尸還是另有隱情炸客,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布戈钢,位于F島的核電站痹仙,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏殉了。R本人自食惡果不足惜开仰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望薪铜。 院中可真熱鬧众弓,春花似錦、人聲如沸隔箍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蜒滩。三九已至滨达,卻和暖如春奶稠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背捡遍。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工锌订, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人画株。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓辆飘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親谓传。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蜈项,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容