Android开发,内存优化是一个很重要的问题,今天就对这方面的问题,做一个较为详细的介绍
在讲内存优化之前,先要将内存环境问题说清楚
java中内存环境分为三种
栈:用于存放基本数据类型和对象的引用
堆:用于存放创建出来的对象或者数组,由GC管理,被所有线程共享
方法区:也叫静态区,包含了静态变量和class对象,被所有线程共享
内存泄漏的原因,具体可以分为下面两种情况
1,当一个对象已经不需要使用时,准备被回收时,被另外一个对象持有了引用导致无法回收
2,有些对象只有有限的生命周期,在生命周期本该结束时,仍被引用。
内存泄漏的累积,会导致内存溢出。即OOM
接下来讲几种常见的内存泄漏情况和处理方法:
1,单例导致的内存泄漏
先来看以下代码,是一个简单的单例模式
public class SingerDemo {
private Context context;
private static SingerDemo instance;
private SingerDemo(Context context) {
this.context = context;
}
public static SingerDemo getInstance(Context context) {
if (instance == null) {
instance = new SingerDemo(context);
}
return instance;
}
}
在单例的构造方法里面,会传入一个Context对象
如果我们传入的是Activity的Context,在Activity关闭时,这个单例的对象仍然持有Activity的引用
从而导致内存泄漏
这个解决的方法,就是在单例的构造方法里面做处理
使用getApplicationContext()获取到应用的Context
具体代码如下
private SingerDemo(Context context) {
// this.context = context;
this.context = context.getApplicationContext();
}
这个例子也告诉我们,在使用Context的时候,要注意区分情况。
2,非静态内部类创建静态实例导致的内存泄漏
再来看下面的代码
public class MainActivity extends AppCompatActivity {
private static MainClass mainClass = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (mainClass == null) {
mainClass = new MainClass();
}
}
private class MainClass {
}
}
这种情况下,非静态的内部类MainClass默认持有了MainActivity的引用
从而导致他的静态实例对象一直持有Activity的引用
在Activity关闭时无法释放
从而导致MainClass无法被内存回收,导致内存泄漏
解决方法
把内部类改成静态内部类即可
3,handler造成的内存泄漏
Handler的生命周期和Activity的生命周期是不一致的,
所以Activity关闭的时候
Handler可能还存在内存
看下面这个例子
public class MainActivity extends AppCompatActivity {
private final Handler myhandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myhandler.postDelayed(new Runnable() {
@Override
public void run() {
}
}, 1000 * 60 * 10);
}
}
当Handler延迟执行消息的时候,把Activity关闭
这个时候Handler持有了Activity的对象
导致了内存泄漏
解决的方法有两种,第一种
就是在Activity关闭时,remove掉Handler的消息
removeMessages:移除单个message
removeCallbacksAndMessages:移除全部message和回调
另外一种办法
1,将handler声明为静态的
2,通过弱引用的方式引入Activity,
对相关方法和View进行操作
代码如下:
private static class MyHandler extends Handler {
private WeakReference<MainActivity> mWeakReference;
public MyHandler(MainActivity activity) {
mWeakReference = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity mainActivity = mWeakReference.get();
switch (msg.what) {
case 0x001:
if (mainActivity != null) {
mainActivity.mTextView.setText(msg.obj + "");
}
break;
default:
break;
}
}
}
4,线程导致的内存泄漏
线程导致的内存泄漏,处理方式和Handler一样
将Thread和Runnable
都声明为静态的
用弱引用的方式获得Activity对象
或者在合适的生命周期,
去结束这个线程
5,Webview造成的内存泄漏
Webview造成内存泄漏的原因有很多种
有时候需要移动端和网页端对应处理
这里提供一种解决方法
1,将Webview所处的Activity放在一个单独的进程当中
在Activity的配置清单里添加android:process=""属性即可
process属性的值即是你的进程的名字
2,关闭Activity时,调用下面这个方法,去杀掉这个进程即可
android.os.Process.killProcess(android.os.Process.myPid());
更多解决方式,可以参考这篇文章
最后分享几个关于内存优化的点
1,频繁的字符串拼接使用StringBuilder代替String
2,ArrayMap,Sparse代替HashMap
3,不要频繁地创建和回收大量对象,避免内存抖动
因为在调用GC的过程中,会停住其他进程
一旦要处理的对象过多,就会造成UI卡顿等问题
4,复用系统自带的资源,ListView的item复用等
5,避免在onDraw方法里创建对象
6,及时关闭Cursor对象
7,注册和取消注册要成对出现。如广播的注册和取消注册