Android内存的优化几个方面
- 静态变量引起内存泄露
在代码优化的过程中,我们需要对代码中的静态变量特别留意。静态变量是类相关的变量, 它的生命周期是从这个类被声明,到这个类彻底被垃圾回收器回收才会被销毁。所以,一般情况下,静态变量从所在的类被使用开始就要一直占用着内存空间,直到 程序退出。如果不注意,静态变量引用了占用大量内存的资源,造成垃圾回收器无法对内存进行回收,就可能造成内存的浪费 - 使用Application的Context
在Android中,Application Context的生命周期和应用的生命周期一样长,而不是取决于某个Activity的生命周期。如果想保持一个长期生命的对象,并且这个对象需要一个 Context,就可以使用Application对象。可以通过调用Context.getApplicationContext()方法或者 Activity.getApplication()方法来获得Application对象。 - 及时关闭资源
Cursor是Android查询数据后得到的一个管理数据集合的类。正常情况下,如 果我们没有关闭它,系统会在回收它时进行关闭,但是这样的效率特别低。如果查询得到的数据量较小时还好,如果Cursor的数据量非常大,特别是如果里面 有Blob信息时,就可能出现内存问题。所以一定要及时关闭Cursor。 - 使用Bitmap及时调用recycle() // 把 重复循环的方法设置 为 null,释放内存
前面的章节讲过,在不使用Bitmap对象时,需要调用recycle()释放内存,然后将它设置为null。虽然调用recycle()并不能保证立即释放占用的内存,但是可以加速Bitmap的内存的释放。
在代码优化的过程中,如果发现某个Activity用到了Bitmap对象,却没有显式的调用recycle()释放内存,则需要分析代码逻辑,增加相关代码,在不再使用Bitmap以后调用recycle()释放内存。 - 对Adapter进行优化
下面以构造ListView的BaseAdapter为例说明如何对Adapter进行优化。
@软引用和弱引用。
如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存 空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队 列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引 用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱 引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联 的引用队列中。
弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。
UI优化
在Android应用开发过程中,屏幕上控件的布局代码和程序的逻辑代码通常是分开 的。界面的布局代码是放在一个独立的xml文件中的,这个文件里面是树型组织的,控制着页面的布局。通常,在这个页面中会用到很多控件,控件会用到很多的 资源。Android系统本身有很多的资源,包括各种各样的字符串、图片、动画、样式和布局等等,这些都可以在应用程序中直接使用。这样做的好处很多,既 可以减少内存的使用,又可以减少部分工作量,也可以缩减程序安装包的大小。
Android内存泄漏如何检测
- Eclipse工具检测
- Studio检测
- LeakCanary检测
LeakCanary检测原理
参考文章 https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
工作机制
RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。
然后在后台线程检查引用是否被清除,如果没有,调用GC。
如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。
本文主要介绍LeakCanary检测方式
- 编译配置
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
- 代码入口配置
public class ExampleApplication extends Application {
private RefWatcher mRefWatcher;
@Override
public void onCreate() {
super.onCreate();
mRefWatcher = LeakCanary.install(this);
}
}
- 来一个内存泄漏测一下,在单例类中持有上下文的引用
public class SingleInstance {
private static SingleInstance sInstance;
private final Context context;
private SingleInstance(Context context){
this.context=context;
}
public static SingleInstance getInstance(Context context) {
if (sInstance == null) {
sInstance = new SingleInstance(context);
}
return sInstance;
}
}
- 在Activity中调用一下
SingleInstance.getInstance(this);
退出Activity,数上20秒,看一下logcat 会有对应的泄漏分析,同时手机会弹出通知,通知如下图,点开+号就能够分析出哪里内存泄漏了。
内存泄漏解决,在单例类中,如果获取上下文的话,要获取和生命周期长的app的Context,单个Activity的上下文随着Activity销毁就销毁了…
public class SingleInstance {
private static SingleInstance sInstance;
private final Context context;
private SingleInstance(Context context){
this.context=context;
}
public static SingleInstance getInstance(Context context) {
if (sInstance == null) {
sInstance = new SingleInstance(context.getApplicationContext());
}
return sInstance;
}
}
- 搞定了对Activity的内存泄漏的检测
检测Fragment内存泄漏
public abstract class BaseFragment extends Fragment {
@Override public void onDestroy() {
super.onDestroy();
RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity());
refWatcher.watch(this);
}
}
- 这篇文章中https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
中还介绍了将异常传到服务器的操作流程