Android 内存优化知识点梳理总结
一、内存泄漏
内存泄漏指由于疏于释放内存而导致内存溢出的一种情况。在 Android 中,可能导致内存泄漏的场景包括:
- 非静态内部类引用外部类实例
- Handler 引起的内存泄漏
- 单例模式中的 Context 引起的内存泄漏
- ListView/RecyclerView 的 ViewHolder 引起的内存泄漏
- Bitmap 引起的内存泄漏
解决方案包括:
- 尽量避免使用非静态内部类引用外部类实例,如果必须使用可以使用弱引用或静态内部类
- 尽量使用 Application Context 而不是 Activity Context
- 在使用 Handler 时,尽量使用静态内部类并在内部弱引用一下外部类
- 在 Activity/Fragment 销毁时及时取消掉相关联的消息
- 在使用 ListView/RecyclerView 时,尽量减少 ViewHolder 中的变量数目以及对 Bitmap 等占用较大的资源的引用
以 Handler 引起的内存泄漏的示例说明:
public class MyActivity extends Activity {
private HandlerThread mWorkThread;
private Handler mWorkHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWorkThread = new HandlerThread("MyWorkThread");
mWorkThread.start();
mWorkHandler = new Handler(mWorkThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// do something
}
};
}
@Override
protected void onDestroy() {
super.onDestroy();
// 导致内存泄漏的写法,需要手动调用 quit 方法停止 HandlerThread
mWorkThread.quit();
}
}
在上述代码中,由于 HandlerThread 是用在 Handler 中的,而 Handler 又被赋值给了成员变量 mWorkHandler,因此在 Activity onDestroy 时必须调用 mWorkThread.quit() 来手动停止 HandlerThread,否则就会导致内存泄漏。
二、内存抖动
内存抖动指的是申请一次性大量内存,而同时又频繁地进行内存分配和回收导致 GC 频繁执行的一种情况。
Android 中可能导致内存抖动的场景包括:
- 频繁地创建临时对象
- 频繁地使用 String 进行字符串拼接
解决方案包括:
- 尽量避免频繁地创建临时对象,可以使用对象池或者使用静态方法/属性等方式来代替创建对象
- 尽量避免频繁地使用 String 进行字符串拼接,可以使用 StringBuilder 或者使用其他方式来代替字符串拼接
以频繁地使用 String 进行字符串拼接的示例说明:
public class MyActivity extends Activity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.textview);
}
public void onButtonClick(View view) {
String result = "";
for (int i = 0; i < 100000; i++) {
result += i;
}
mTextView.setText(result);
}
}
在上述代码中,当按钮点击时会频繁地进行字符串拼接,由于 String 拼接会产生大量的临时对象,导致了内存抖动。改进的方案应该是使用 StringBuilder 进行字符串拼接,例如:
public class MyActivity extends Activity {
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.textview);
}
public void onButtonClick(View view) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 100000; i++) {
builder.append(i);
}
mTextView.setText(builder.toString());
}
}
三、大图加载
在 Android 中,由于内存有限制,一些较大的图片如果直接加载会导致 OOM 错误的出现,因此需要进行图片压缩或者分块加载等处理。
解决方案包括:
- 使用图片压缩或者缩放等方式减少图片内存占用
- 对于较大的图片,可以使用分块加载的方式进行处理,例如使用 SubsamplingScaleImageView 等库
以图片压缩的示例说明:
public class MyActivity extends Activity {
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImageView = findViewById(R.id.imageview);
// 加载大图
Bitmap bitmap = BitmapFactory.decodeFile("/sdcard/test.jpg");
int width = bitmap.getWidth();
int height = bitmap.getHeight();
// 计算出压缩比例
int scaleFactor = Math.min(width/1000, height/1000);
if (scaleFactor <= 0) {
scaleFactor = 1;
}
// 根据压缩比例缩小图片
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = scaleFactor;
Bitmap compressedBitmap = BitmapFactory.decodeFile("/sdcard/test.jpg", options);
mImageView.setImageBitmap(compressedBitmap);
}
}
在上述代码中,首先加载原始的大图,然后根据图片实际大小计算出需要压缩的比例,最后通过 BitmapFactory.Options 进行图片压缩,从而减少了图片的内存占用。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android 内存优化知识点梳理总结 - Python技术站