您的位置:首页 > 移动开发 > Android开发

Android 程序页面之间多次跳转照成手机内存飙高从而引发OOM的解决方案 初稿

2017-09-01 11:49 537 查看
先简单说下这个问题是怎么来的吧,程序打开进入主页,这个时候就可以通过主页来跳转到其他页面,有的是Activity或者是Fragment构建的页面,这个时候我们就可以通过Android Studio提供的Android Monitor观察Monitors中的Memory实时展示当前程序运行占用的内存,根据页面的复杂程度占用的内存高低不一,简单的就几MB,复杂的都是20MB/50MB,你以为这就结束了,呵呵!还没完,现在我们需要回到主页或者上一个页面,很简单直接调用下finish这时我们需要盯着Memory看看内存的占用情况,理论上当我们结束一个页面,被结束的页面应该立刻被GC才对,但实际上关闭的页面占用的内存并没有被释放掉,久而久之当用户在无规则的进行页面之间的跳转时,系统分配的内存很容易被占满从而照成程序闪退,说了这么多废话就是在为下面的解决方案做铺垫,现在直接上方案。

第一步:

在每个页面都调用onDestory(),当页面结束时在onDestory中释放掉实例出来的参数。例如:

public class XxxActivity extends Activity {
priavte TextView textView = null; // 文本控件
// 这里顺便一起把adapter的释放也一起演示了
private RecyclerView xxx_rv;
private
4000
XxxAdapter xxxAdapter;
private XxxUtils xxxUtils = new XxxUtils(); // 实例化出来的外部类
private XxxHandle handle = new XxxHandle(this); // 为了避免Handle造成的内存泄露,我们需要优化写法,下面有例子
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xxxx);
textView = (TextView)findViewById(R.id.xxx);
xxx_rv = (RecyclerView) findViewById(R.id.xxx);
/**
* 这里有必要说下,我看网上很多同学都遇到的
* RecyclerView: No adapter attached; skipping layout 这个问题,网上也有客官老爷没给出了自己的解决方案,但大多都只能解决自己遇到的问题,我这次说不定也是一样,反正分享出来
* 出现这个异常,程序是不会蹦掉的,但是如果有处女情结的同学可能就会焦虑好久,我是这样解决这个问题的,在onCreate中初始好Adapter和RecyclerView,Adapter中数据传入单独写个方法(下面会给出例子),如果Adapter中onBindViewHolder里写好了数据展示,就先做个非空判断,这时只要我们的数据解析完成了,就将数据传入然后调用notifyDataSetChanged()刷新Adapter,这样logcat中就不会出现这个异常提示了
*/
xxxAdapter = new XxxAdapter();
}

/**
* 避免Handle泄漏优化写法
*/
private static class XxxHandle extends Handler {
private WeakReference<Context> reference;
XxxHandle(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
XxxActivity m = (XxxActivity) reference.get();
switch (msg.what) {
case xxx: {
m.xxx(msg.obj);
break;
}
}
}
}

@Override
public void onDestroy() {
// 释放Handle
handle.removeCallbacksAndMessages(null);
handle = null;
// 释放Adapter
xxx_rv.setAdapter(null);
xxx_rv.setAdapter(xxxAdapter);
xxx_rv = null;
// 释放文本控件
textView = null;
// 释放实例的外部类
xxxUtils = null;
// 这里是我看网上有位同学给出的方案,有效果但也不大,view_null.xml是一个没有控件的空layout
setContentView(R.layout.view_null);
super.onDestroy();
}
}


第二步:

加入Leakcanary来检查内存泄漏问题。用法:

1. 在build.gradle中加入引用

dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}


2.在 Application中初始化调用

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

private RefWatcher refWatcher;

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


3.最后在内存占用比较多的页面中的onDestroy()里调用我们在Application中实例化的LeakCanary

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


这样就完成了Leakcanary的嵌入,当程序出现内存泄漏问题时会主动发推送到通知栏,然后就可以根据提示去手动优化了,Leakcanary还会去帮我们释放程序占用的内存。

总结:

完成以上步骤后,我们再来运行程序观察Memory的变化就会发现之前finish掉的页面内存没有被释放的情况被有效的解决了(例如首页启动占用了20MB,开启一个页面加载完成后内存上升到50MB,finish掉这个页面,过几秒内存就会降到20MB,可能会比原有多出几MB)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息