徹底搞懂Java內(nèi)存泄露

本文已授權(quán)微信公眾號(hào):鴻洋(hongyangAndroid)原創(chuàng)首發(fā)瞒斩。
簡(jiǎn)書 編程之樂
轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處剩岳,謝謝!

Java內(nèi)存回收方式

Java判斷對(duì)象是否可以回收使用的而是可達(dá)性分析算法军拟。

在主流的商用程序語言中(Java和C#)拣宰,都是使用可達(dá)性分析算法判斷對(duì)象是否存活的。這個(gè)算法的基本思路就是通過一系列名為"GC Roots"的對(duì)象作為起始點(diǎn)放典,從這些節(jié)點(diǎn)開始向下搜索逝变,搜索所走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈相連時(shí)奋构,則證明此對(duì)象是不可用的壳影,下圖對(duì)象object5, object6, object7雖然有互相判斷,但它們到GC Roots是不可達(dá)的弥臼,所以它們將會(huì)判定為是可回收對(duì)象宴咧。

Paste_Image.png

在Java語言里,可作為GC Roots對(duì)象的包括如下幾種:

    a.虛擬機(jī)棧(棧楨中的本地變量表)中的引用的對(duì)象
    b.方法區(qū)中的類靜態(tài)屬性引用的對(duì)象
    c.方法區(qū)中的常量引用的對(duì)象
    d.本地方法棧中JNI的引用的對(duì)象 

摘自《深入理解Java虛擬機(jī)》

使用leakcanary檢測(cè)泄漏

關(guān)于LeakCanary使用參考以下文章:

  1. LeakCanary: 讓內(nèi)存泄露無所遁形
  2. LeakCanary 中文使用說明

LeakCanary的內(nèi)存泄露提示一般會(huì)包含三個(gè)部分:
第一部分(LeakSingle類的sInstance變量)
引用第二部分(LeakSingle類的mContext變量),
導(dǎo)致第三部分(MainActivity類的實(shí)例instance)泄露.

Paste_Image.png

leakcanary使用注意

即使是空的Activity径缅,如果檢測(cè)泄露時(shí)候遇到了如下這樣的泄露掺栅,注意,把refWatcher.watct()放在onDestroy里面即可解決纳猪,或者忽略這樣的提示氧卧。
由于文章已寫很多,下面的就不再修改氏堤,忽略這種錯(cuò)誤即可沙绝。

* com.less.demo.TestActivity has leaked:
* GC ROOT static android.app.ActivityThread.sCurrentActivityThread
* references android.app.ActivityThread.mActivities
* references android.util.ArrayMap.mArray
* references array java.lang.Object[].[1]
* references android.app.ActivityThread$ActivityClientRecord.activity
* leaks com.less.demo.TestActivity instance
protected void onDestroy() {
    super.onDestroy();
    RefWatcher refWatcher = App.getRefWatcher(this);
    refWatcher.watch(this);
}

leakcanary和代碼示例說明內(nèi)存泄露

案例一(靜態(tài)成員引起的內(nèi)存泄露)

public class App extends Application {
    private RefWatcher refWatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        refWatcher = LeakCanary.install(this);
    }

    public static RefWatcher getRefWatcher(Context context) {
        App application = (App) context.getApplicationContext();
        return application.refWatcher;
    }
}

測(cè)試內(nèi)部類持有外部類引用,內(nèi)部類是靜態(tài)的(GC-ROOT,將一直連著這個(gè)外部類實(shí)例)鼠锈,靜態(tài)的會(huì)和Application一個(gè)生命周期闪檬,這會(huì)導(dǎo)致一直持有外部類引用(內(nèi)部類隱含了一個(gè)成員變量$0), 即使外部類制空= null,也無法釋放购笆。

OutterClass

public class OutterClass {
    private String name;

    class Inner{
        public void list(){
            System.out.println("outter name is " + name);
        }
    }
}

TestActivity

public class TestActivity extends Activity {
    // 靜態(tài)的內(nèi)部類實(shí)例
    private static OutterClass.Inner innerClass;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        OutterClass outterClass = new OutterClass();
        innerClass = outterClass.new Inner();

        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(outterClass);// 監(jiān)控的對(duì)象

        outterClass = null;
    }

Paste_Image.png

案例二(單例模式引起的內(nèi)存泄露)

DownloadManager

public class DownloadManager {
    private static DownloadManager instance;
    private Task task ;

    public static DownloadManager getInstance(){
        if (instance == null) {
            instance = new DownloadManager();
        }
        return instance;
    }
    public Task newTask(){
        this.task = new Task();
        return task;
    }
}

Task

public class Task {
    private Call call;
    public Call newCall(){
        this.call = new Call();
        return call;
    }
}

Call

public class Call {
    public void execute(){
        System.out.println("=========> execute call");
    }
}

TestActivity

public class TestActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        RefWatcher refWatcher = App.getRefWatcher(this);

        Task task = DownloadManager.getInstance().newTask();
        Call call = task.newCall();
        call.execute();
        refWatcher.watch(call);// 監(jiān)控的對(duì)象
        call = null; // 無法回收粗悯,DownloadManager是靜態(tài)單例,引用task同欠,task引用了call样傍,即使call置為空横缔,也無法回收,切斷GC_ROOT 聯(lián)系即可避免內(nèi)存泄露铭乾,即置task為空剪廉。
    }
}
Paste_Image.png

部分日志打印如下:當(dāng)前的GC_ROOT是DownloadManager的instance實(shí)例。

 In com.leakcanary.demo:1.0:1.
* com.less.demo.Call has leaked:
* GC ROOT static com.less.demo.DownloadManager.instance
* references com.less.demo.DownloadManager.task
* references com.less.demo.Task.call
* leaks com.less.demo.Call instance

關(guān)于上面兩種方式導(dǎo)致的內(nèi)存泄露問題炕檩,這里再舉兩個(gè)案例說明以加強(qiáng)理解斗蒋。

案例三(靜態(tài)變量導(dǎo)致的內(nèi)存泄露)

public class TestActivity extends Activity {
    private static Context sContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sContext = this;

        RefWatcher refWatcher = App.getRefWatcher(this);

        refWatcher.watch(this);// 監(jiān)控的對(duì)象
    }

Paste_Image.png

打印日志如下:

 In com.leakcanary.demo:1.0:1.
com.less.demo.TestActivity has leaked:
GC ROOT static com.less.demo.TestActivity.sContext
leaks com.less.demo.TestActivity instance

從這段日志可以分析出:聲明static后,sContext的生命周期將和Application一樣長(zhǎng)笛质,Activity即使退出到桌面泉沾,Application依然存在->sContext依然存在,GC此時(shí)想回收Activity卻發(fā)現(xiàn)Activity仍然被sContext(GC-ROOT連接著)妇押,導(dǎo)致死活回收不了跷究,內(nèi)存泄露。

上面的代碼改造一下敲霍,如下俊马。

public class TestActivity extends Activity {
    private static View sView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sView = new View(this);

        RefWatcher refWatcher = App.getRefWatcher(this);

        refWatcher.watch(this);
    }
}
Paste_Image.png

日志如下

In com.leakcanary.demo:1.0:1.
com.less.demo.TestActivity has leaked:
GC ROOT static com.less.demo.TestActivity.sView
references android.view.View.mContext
leaks com.less.demo.TestActivity instance

案例四(單例模式導(dǎo)致的內(nèi)存泄露)
DownloadManager

public class DownloadManager {
    private static DownloadManager instance;
    private List<DownloadListener> mListeners = new ArrayList<>();

    public interface DownloadListener {
        void done();
    }

    public static DownloadManager getInstance(){
        if (instance == null) {
            instance = new DownloadManager();
        }
        return instance;
    }

    public void register(DownloadListener downloadListener){
        if (!mListeners.contains(downloadListener)) {
            mListeners.add(downloadListener);
        }
    }

    public void unregister(DownloadListener downloadListener){
        if (mListeners.contains(downloadListener)) {
            mListeners.remove(downloadListener);
        }
    }
}

TestActivity

public class TestActivity extends Activity implements DownloadManager.DownloadListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        DownloadManager.getInstance().register(this);

        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 忘記 unregister
        // DownloadManager.getInstance().unregister(this);
    }

    @Override
    public void done() {
        System.out.println("done!");
    }
}
Paste_Image.png
 In com.leakcanary.demo:1.0:1.
* com.less.demo.TestActivity has leaked:
* GC ROOT static com.less.demo.DownloadManager.instance
* references com.less.demo.DownloadManager.mListeners
* references java.util.ArrayList.array
* references array java.lang.Object[].[0]
* leaks com.less.demo.TestActivity instance

錯(cuò)誤寫法一定導(dǎo)致內(nèi)存泄露嗎?

答案是:否定的肩杈。
如下案例柴我,有限時(shí)間內(nèi)是可以挽救內(nèi)存泄露發(fā)生的,所以控制好生命周期扩然,其他情況:如單例對(duì)象(靜態(tài)變量)的生命周期比其持有的sContext
的生命周期更長(zhǎng)時(shí) ->內(nèi)存泄露艘儒,更短時(shí)->躲過內(nèi)存泄露。

public class TestActivity extends Activity {
    private static Context sContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        sContext = this;

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                sContext = null; 
            }
        },1000);// 分別測(cè)試1s和12s,前者不會(huì)內(nèi)存泄露夫偶,后者一定泄露界睁。所以如果趕在GC之前切斷GC_ROOT是可以避免內(nèi)存泄露的。
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

Handler 引起的內(nèi)存泄露詳細(xì)分析

handler導(dǎo)致的內(nèi)存泄露可能我們大多數(shù)都犯過.
注意以下代碼中的注釋,非靜態(tài)內(nèi)部類雖然持有外部類引用兵拢,但是持有并不代表一定泄露翻斟,而是看gc時(shí)誰的命長(zhǎng)。經(jīng)過測(cè)試 情況(1)始終沒有內(nèi)存泄露说铃。

為什么會(huì)這樣杨赤, 很早閱讀Handler源碼時(shí)候記得這幾個(gè)貨都是互相引用來引用去的,Message有個(gè)target字段, message.target = handler;
handler.post(message);又把這個(gè)message推入了MessageQueue中截汪,而MessageQueue是在一個(gè)Looper線程中不斷輪詢處理消息,而有時(shí)候message還是個(gè)老不死植捎,能夠重復(fù)利用衙解。如果當(dāng)Activity退出時(shí)候,還有消息未處理或正在處理焰枢,由于message引用handler,handler又引用Activity蚓峦,此時(shí)將引發(fā)內(nèi)存泄露舌剂。

public class TestActivity extends Activity {
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            System.out.println("===== handle message ====");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        // (1) 不會(huì)導(dǎo)致內(nèi)存泄露
        handler.sendEmptyMessageDelayed(0x123,0);

        // (2) 會(huì)導(dǎo)致內(nèi)存泄露,非靜態(tài)內(nèi)部類(包括匿名內(nèi)部類暑椰,比如這個(gè) Handler 匿名內(nèi)部類)會(huì)引用外部類對(duì)象 this(比如 Activity)
        // 當(dāng)它使用了 postDelayed 的時(shí)候霍转,如果 Activity 已經(jīng) finish 了,而這個(gè) handler 仍然引用著這個(gè) Activity 就會(huì)致使內(nèi)存泄漏
        // 因?yàn)檫@個(gè) handler 會(huì)在一段時(shí)間內(nèi)繼續(xù)被 main Looper 持有一汽,導(dǎo)致引用仍然存在.
        handler.sendEmptyMessageDelayed(0x123, 12000);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}
Paste_Image.png
com.less.demo.TestActivity has leaked:
* GC ROOT android.view.inputmethod.InputMethodManager$ControlledInputConnectionWrapper.mH
* references com.android.internal.view.IInputConnectionWrapper$MyHandler.mQueue
* references android.os.MessageQueue.mMessages
* references android.os.Message.target , matching exclusion field android.os.Message#target
* references com.less.demo.TestActivity$1.this$0 (anonymous subclass of android.os.Handler)
* leaks com.less.demo.TestActivity instance

知道了原理后避消,即使寫出易于內(nèi)存泄露的代碼也能夠避免內(nèi)存泄露啦。
上述代碼只需在onDestroy()函數(shù)中調(diào)用mHandler.removeCallbacksAndMessages(null);
在Activity退出的時(shí)候的移除消息隊(duì)列中所有消息和所有的Runnable召夹。

內(nèi)部類引起的內(nèi)存泄露

內(nèi)部類種類大致如下:

  1. 非靜態(tài)內(nèi)部類(成員內(nèi)部類)
  2. 靜態(tài)內(nèi)部類(嵌套內(nèi)部類)
  3. 局部?jī)?nèi)部類(定義在方法內(nèi)或者作用域內(nèi)的類,好似局部變量,所以不能有訪問控制符和static等修飾)
  4. 匿名內(nèi)部類(沒有名字,僅使用一次new個(gè)對(duì)象即扔掉類的定義)

為什么非靜態(tài)內(nèi)部類持有外部類引用,靜態(tài)內(nèi)部類不持有外部引用岩喷。

這個(gè)問題非常簡(jiǎn)單,就像 static的方法只能調(diào)用static的東西监憎,非static可以調(diào)用非static和static的一樣纱意。static--> 針對(duì)class, 非static->針對(duì) 對(duì)象,我是這么簡(jiǎn)單理解的鲸阔⊥得梗看圖:

Paste_Image.png

匿名內(nèi)部類
將局部?jī)?nèi)部類的使用再深入一步,假如只創(chuàng)建某個(gè)局部?jī)?nèi)部類的一個(gè)對(duì)象褐筛,就不必命名了类少。

匿名內(nèi)部類的類型可以是如下幾種方式。

  1. 接口匿名內(nèi)部類
  2. 抽象類匿名內(nèi)部類
  3. 類匿名內(nèi)部類

匿名內(nèi)部類總結(jié):

  1. 其實(shí)主要就是類定義一次就失效了死讹,主要使用的是這個(gè)類(不知道名字)的實(shí)例瞒滴。根據(jù)內(nèi)部類的特性,能夠?qū)崿F(xiàn)回調(diào)和閉包赞警。
  2. JavaScript和Python的回調(diào)傳遞的是fuction,Java傳遞的是object妓忍。
    Java中常常用到回調(diào),而回調(diào)的本質(zhì)就是傳遞一個(gè)對(duì)象愧旦,JavaScript或其他語言則是傳遞一個(gè)函數(shù)(如Python世剖,或者C,使用函數(shù)指針的方式),由于傳遞一個(gè)對(duì)象可以攜帶其他的一些信息,所以Java認(rèn)為傳遞一個(gè)對(duì)象比傳遞一個(gè)函數(shù)要靈活的多(當(dāng)然java也可以用Method反射對(duì)象傳遞函數(shù))笤虫。參考《Java核心技術(shù)》

非靜態(tài)內(nèi)部類導(dǎo)致內(nèi)存泄露(前提dog的命長(zhǎng))
下面的案例將導(dǎo)致內(nèi)存泄露
其中private static Dog dog ; 導(dǎo)致Dog的命比TestActivity長(zhǎng)旁瘫,這就糟糕了,但是注意琼蚯,如果改為private Dog dog ; 即使Dog是非靜態(tài)內(nèi)部類酬凳,也不會(huì)導(dǎo)致內(nèi)存泄露,要死也是Dog先死,畢竟Dog是TestActivity的家庭成員遭庶,開掛也得看主人宁仔。

public class TestActivity extends Activity {
    private static Dog dog ;

    class Dog {
        public void say(){
            System.out.println("I am lovely dog!");
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        dog = new Dog();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}
Paste_Image.png
In com.leakcanary.demo:1.0:1.
* com.less.demo.TestActivity has leaked:
* GC ROOT static com.less.demo.TestActivity.dog
* references com.less.demo.TestActivity$Dog.this$0
* leaks com.less.demo.TestActivity instance

哪些內(nèi)部類或者回調(diào)函數(shù)是否持有外部類對(duì)象?
一個(gè)反射案例說明一切

/**
 * 作者: limitless
 * 描述: 一個(gè)案例測(cè)試所有類型內(nèi)部類對(duì)外部類對(duì)象的持有情況
 * 網(wǎng)站: https://github.com/wangli0
 */
public class Main {

    /* 持有外部類引用 */
    private IAppListener mAppListener = new IAppListener() {
        private String name;
        @Override
        public void done() {
            System.out.println("匿名內(nèi)部類對(duì)象作為成員變量");
        }
    };
    /* 未持有 */
    private static IAppListener sAppListener = new IAppListener() {
        private String name;
        @Override
        public void done() {
            System.out.println("匿名內(nèi)部類對(duì)象作為static成員變量");
        }
    };

    public static void main(String args[]) {
        Main main = new Main();
        main.test1();
        main.test2();
        main.test3();// test3 《=》test4
        main.test4();
        main.test5();
        main.test6();
    }

    class Dog {
        private String name;
    }

    /* 持有外部類引用 */
    public void test1(){
        Dog dog = new Dog();
        getAllFieldName(dog.getClass());
    }

    static class Cat {
        private String name;
    }
    /* 未持有 */
    private void test2() {
        Cat cat = new Cat();
        getAllFieldName(cat.getClass());
    }

    /* 持有外部類引用 */
    private void test3() {
        class Monkey{
            String name;
        }
        Monkey monkey = new Monkey();
        getAllFieldName(monkey.getClass());
    }

    /* 持有外部類引用 */
    private void test4() {
        // 常用作事件回調(diào)的地方(有可能引起內(nèi)存泄露)
        IAppListener iAppListener = new IAppListener() {
            private String name;
            @Override
            public void done() {
                System.out.println("匿名內(nèi)部類");
            }
        };
        getAllFieldName(iAppListener.getClass());
    }

    /* 持有外部類引用 */
    private void test5() {
        getAllFieldName(mAppListener.getClass());
    }

    /* 未持有 */
    private void test6() {
        getAllFieldName(sAppListener.getClass());
    }

    private void getAllFieldName(Class<?> clazz) {
        System.out.println("className: ======> " + clazz.getSimpleName());
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName());
        }
    }
}

Paste_Image.png

上述結(jié)果足夠說明峦睡,即使是方法內(nèi)的回調(diào)對(duì)象也是持有外部類引用的翎苫,那么雖然作用域是局部的权埠,也有存在內(nèi)存泄露的可能。我多次強(qiáng)調(diào) 持有某對(duì)象 不代表一定泄露煎谍,看的是誰命長(zhǎng)攘蔽。回調(diào)在Android開發(fā)過程中幾乎處處可見呐粘,如果持有就會(huì)內(nèi)存泄露的話满俗,那幾乎就沒法玩了。
一般情況下事哭,我們常常設(shè)置某個(gè)方法內(nèi)的xx.execute(new Listener(){xx});是不會(huì)導(dǎo)致內(nèi)存泄露的漫雷,這個(gè)方法執(zhí)行完,局部作用域就失效了鳍咱。但是如果在execute(listener);過程中降盹,某個(gè)單例模式的對(duì)象 突然引用了這個(gè)listener對(duì)象,那么就會(huì)導(dǎo)致內(nèi)存泄露谤辜。

下面用實(shí)例證明我的想法
TestActivity

public class TestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        Task task = new Task();
        task.execute(new ICallback() {
            @Override
            public void done() {
                System.out.println("下載完成蓄坏!");
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}

Task

public class Task {
    public void execute(ICallback iCallback) {
        DownloadManager.getInstance().execute(iCallback);
    }
}

DownloadManager

public class DownloadManager {
    public static DownloadManager instance;
    private ICallback mICallback;
    public static DownloadManager getInstance(){
        if (instance == null) {
            instance = new DownloadManager();
        }
        return instance;
    }

    public void execute(ICallback iCallback) {
        this.mICallback = iCallback;// 反例,千萬不要這么做丑念,將導(dǎo)致內(nèi)存泄露,如果注釋掉這行涡戳,內(nèi)存泄露不會(huì)發(fā)生
        iCallback.done();
    }
Paste_Image.png

這足以證明我的想法是正確的,Callback的不巧當(dāng)使用同樣會(huì)導(dǎo)致內(nèi)存泄露 的發(fā)送脯倚。

總結(jié)

  1. 如果某些單例需要使用到Context對(duì)象渔彰,推薦使用Application的context,不要使用Activity的context推正,否則容易導(dǎo)致內(nèi)存泄露恍涂。單例對(duì)象的生命周期和Application一致,這樣Application和單例對(duì)象就一起銷毀植榕。
  2. 優(yōu)先使用靜態(tài)內(nèi)部類而不是非靜態(tài)的,因?yàn)榉庆o態(tài)內(nèi)部類持有外部類引用可能導(dǎo)致垃圾回收失敗再沧。如果你的靜態(tài)內(nèi)部類需要宿主Activity的引用來執(zhí)行某些東西,你要將這個(gè)引用封裝在一個(gè)WeakReference中尊残,避免意外導(dǎo)致Activity泄露,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次垃圾收集發(fā)生之前炒瘸。當(dāng)垃圾收集器工作時(shí),無論當(dāng)前內(nèi)存是否足夠寝衫,都會(huì)回收 只被弱引用關(guān)聯(lián) 的對(duì)象顷扩,只被 說明這個(gè)對(duì)象本身已經(jīng)沒有用處了。
public class TestActivity extends Activity {
    private MyHandler myHandler = new MyHandler(this);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        
    }

    static class MyHandler extends Handler {
        private WeakReference<Activity> mWeakReference;

        public MyHandler(Activity activity){
            mWeakReference = new WeakReference<Activity>(activity);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Toast.makeText(mWeakReference.get(), "xxxx", Toast.LENGTH_LONG).show();
            Log.d("xx", mWeakReference.get().getPackageName());
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        RefWatcher refWatcher = App.getRefWatcher(this);
        refWatcher.watch(this);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末慰毅,一起剝皮案震驚了整個(gè)濱河市屎即,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖技俐,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異统台,居然都是意外死亡雕擂,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門贱勃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來井赌,“玉大人,你說我怎么就攤上這事贵扰〕鹚耄” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵戚绕,是天一觀的道長(zhǎng)纹坐。 經(jīng)常有香客問我,道長(zhǎng)舞丛,這世上最難降的妖魔是什么耘子? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮球切,結(jié)果婚禮上谷誓,老公的妹妹穿的比我還像新娘。我一直安慰自己吨凑,他們只是感情好捍歪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸵钝,像睡著了一般糙臼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蒋伦,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天弓摘,我揣著相機(jī)與錄音,去河邊找鬼痕届。 笑死韧献,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的研叫。 我是一名探鬼主播锤窑,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嚷炉!你這毒婦竟也來了渊啰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎绘证,沒想到半個(gè)月后隧膏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡嚷那,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年胞枕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魏宽。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡腐泻,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出队询,到底是詐尸還是另有隱情派桩,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布蚌斩,位于F島的核電站铆惑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凳寺。R本人自食惡果不足惜鸭津,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肠缨。 院中可真熱鬧逆趋,春花似錦、人聲如沸晒奕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脑慧。三九已至魄眉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闷袒,已是汗流浹背坑律。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留囊骤,地道東北人晃择。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像也物,于是被迫代替她去往敵國(guó)和親宫屠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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