使用LeakCanary分析并解决Android内存泄露

来源:互联网 发布:淘宝切片 编辑:程序博客网 时间:2024/05/21 06:40

使用LeakCanary分析并解决Android内存泄露

  • 使用LeakCanary分析并解决Android内存泄露
    • Andorid app查找内存泄露的一般步骤
    • 一些常见的Android内存泄露分析和解决方案

LeakCanary是一款内存泄露分析工具,至于什么是内存泄露网上有很多帖子,这里就不废话了,改工具是在app里植入一个新进程的和一个新入口的工具组件,也就是说使用了leakcanary的app安装后会在桌面看到两个app入口,卸载其中一个另外一个也会被卸载,并且两个入口运行在不同的进程。而在leakcanary入口里可以看到主app里内存泄露的所有信息。这里要说一下,当主app发生内存后,需要一段时间才能在leakcanary里看到,而不是马上就有,因为hprof文件的导出和分析需要很长时间,直到在notifycation出现通知才在leakcanary里能看到细节信息,LeakCanary托管在github上:点击进入

这里写图片描述

这里写图片描述

这里写图片描述


Andorid app查找内存泄露的一般步骤

  • 运行app,先用DDMS内存管理点击GC按钮触发GC或者在adb shell下使用dumpsys meminfo 来观察app是否有内存泄露的嫌疑,这里一定要在app上多操作然后不断的观察内存变化的情况,如果是一直增加的话就可以怀疑有内存泄露的情况,有了leakcanary此步骤可以说在某种程度下能忽略
    这里写图片描述

这里写图片描述

  • 在项目里加入leakcanary,如果用的是AndroidStudio的话在gradle里加入如下代码:
dependencies {    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'}
  • 然后在自定义application并在manifest里申明:
public class TestMemApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        Logger.addLogAdapter(new AndroidLogAdapter());        if (LeakCanary.isInAnalyzerProcess(this)) {            // This process is dedicated to LeakCanary for heap analysis.            // You should not init your app in this process.            return;        }        LeakCanary.install(this);        // Normal app init code...    }}
  • 不断的在app里操作,如果有内存泄露的地方会有提示,然后查看,Leakcanary集成以后会在你的app进程之外开启一个新进程,也就是说如果你安装了包含leakcanary的app会发现桌面多出了两个图标,其中之一就是leakcanary,另外一个就是你自己的app了,可以通过点击leakcanary的图标进去查看内存泄露的相关信息。

一些常见的Android内存泄露分析和解决方案

  • 匿名内部runnable持有当前activity实力释放不了,造成内存泄露,这是我们为了省事经常写的代码。
public class Activity1 extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_1);        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(100000);                    // 耗时运算 例如网络请求                    // 拿到资源后更新UI                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }).start();    }}

通过打断点可以发现runnable内部this持有activity实例

这里写图片描述

当从此activity1里退出的时候,如果线程没有结束就会造成activity无法释放而出现内存泄露

这里写图片描述

正确的用法是

public class Activity2 extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_2);        new Thread(new MyRunnable()).start();    }    static class MyRunnable implements Runnable {        @Override        public void run() {            try {                Thread.sleep(100000);                // 耗时运算 例如网络请求                // 拿到资源后更新UI            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

很明显使用static内部类就不再持有activity实例

这里写图片描述

但是如果线程里要更新UI需要外部的activity实例,可以使用weakref,这样内部类实例不能持有activity2实例,如果activity2退出释放了,mAct为空线程就不会做UI更新了

public class Activity2 extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_2);        new Thread(new MyRunnable(this)).start();    }    private void refreshUI() {        // 更新UI    }    static class MyRunnable implements Runnable {        WeakReference<Activity2> mAct;        public MyRunnable(Activity2 act) {            mAct = new WeakReference<>(act);        }        @Override        public void run() {            try {                Thread.sleep(100000);                // 耗时运算 例如网络请求                // 拿到资源后更新UI                Activity2 activity2 = mAct.get();                if (null != activity2) {                    activity2.refreshUI();                }            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

  • AsyncTask用法同1
public class Activity3 extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_3);        new MyAsyncTask(this).execute();    }    static class MyAsyncTask extends AsyncTask<Void, Void, Void> {        private WeakReference<Activity3> mAct;        public MyAsyncTask(Activity3 act) {            super();            mAct = new WeakReference(act);        }        @Override        protected Void doInBackground(Void... params) {            // 耗时操作            return null;        }        @Override        protected void onPostExecute(Void aVoid) {            super.onPostExecute(aVoid);            Activity3 activity3 = mAct.get();            if (null != activity3) {                // 更新UI            }        }        @Override        protected void onProgressUpdate(Void... values) {            super.onProgressUpdate(values);            Activity3 activity3 = mAct.get();            if (null != activity3) {                // 更新UI            }        }    }}

  • Handler用法同1
public class Activity4 extends AppCompatActivity {    private Handler mHandler = new MyHandler(this);    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_4);        //  延迟发送        mHandler.sendEmptyMessageDelayed(0, 100000);    }    private void refreshUI() {    }    private static class MyHandler extends Handler {        private WeakReference<Activity4> mAct;        public MyHandler(Activity4 act) {            mAct = new WeakReference<>(act);        }        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case 0:                    // 收到延迟10秒发送的消息0 然后更新UI                    Activity4 activity4 = mAct.get();                    if (null != activity4) {                        // 更新UI                        activity4.refreshUI();                    }                    break;                default:                    break;            }        }    }}

  • IO相关的连接使用完后要释放
public class Activity5 extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_5);        new Thread(new MyRun(getApplicationContext())).start();    }    private static class MyRun implements Runnable {        private Context mCtx;        public MyRun(Context ctx) {            mCtx = ctx;        }        @Override        public void run() {            String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();            String filePath = sdPath + File.separator + "test.txt";            File file = new File(filePath);            Logger.i("file = " + filePath);            if (!file.exists()) {                return;            }            RefWatcher refWatcher = TestMemApplication.getRefWatcher(mCtx);            FileInputStream in = null;            BufferedInputStream bin = null;            try {                in = new FileInputStream(file);                bin = new BufferedInputStream(in);                refWatcher.watch(in);                refWatcher.watch(bin);                byte[] buffer = new byte[1024];                int len = -1;                while ((len = bin.read(buffer)) != -1) {                    Logger.i(new String(buffer, len));                }            } catch (FileNotFoundException e) {                e.printStackTrace();            } catch (IOException e) {                e.printStackTrace();            } finally {                if (null != bin) {                    try {                        bin.close();                    } catch (IOException e) {                        e.printStackTrace();                    } finally {                        if(null != in) {                            try {                                in.close();                            } catch (IOException e) {                                e.printStackTrace();                            }                        }                    }                }            }        }    }}

  • 集合里不用的item一定要删除,不然也会内存泄露


  • BroadcastReceiver和各种listener一定要在Activity里的onDestory里反注册


  • 自定义的一些类需要context的时候不要直接用activity而使用activity.getApplicationContext()


  • 各种adapter里要重用convertView,不然内存会被耗尽切无法回收


  • 使用各种推送SDK用来更新UI的时候,在各个activity里注册的时候使用weakRef,防止泄露