安卓内存优化案例一

安卓内存优化是一个很重要的话题,有很多方面可以考虑,比如避免内存泄漏、减少内存抖动、优化图片加载、使用缓存和对象池等。下面我举一些代码案例,分别展示不合适的写法和高性能的写法。
欢迎评论区留言指正和补充。

1. 避免使用枚举类型。

枚举类型会占用更多的内存,因为它是一个类对象,而不是一个基本类型。如果需要定义一些常量,可以使用 static final int 或者 @IntDef 注解来代替。例如:

// 不合适的写法
public enum Color {
    RED, GREEN, BLUE
}

// 高性能的写法
public static final int RED = 0;
public static final int GREEN = 1;
public static final int BLUE = 2;

@IntDef({RED, GREEN, BLUE})
@Retention(RetentionPolicy.SOURCE)
public @interface Color {}

这样做可以节省内存空间,因为枚举类型会占用至少4个字节,而 int 类型只占用4个字节。另外,使用注解可以保证类型安全和编译时检查。

测试枚举、数组和整型数本身占用内存的大小

//测试代码
public int type_int_a = 16;

public int type_int_b = 20;

public int[] type_int_array_a = {1,2,2};

public enum TypeEnumD{
    first,
    second,
    third,
}

public TypeEnumD f = TypeEnumD.first;
public TypeEnumD s= TypeEnumD.second;
public TypeEnumD t = TypeEnumD.third;

测试结果


枚举、数组和整型数本身大小.png

2. 避免在循环中创建对象。

这会导致内存抖动和频繁的GC,影响性能和用户体验。如果需要在循环中使用对象,可以在循环外创建并复用,或者使用对象池来管理对象的生命周期。例如:

// 不合适的写法
for (int i = 0; i < 100; i++) {
    String s = new String("Hello"); // 每次循环都会创建一个新的字符串对象
    // do something with s
}

// 高性能的写法
String s = new String("Hello"); // 在循环外创建一个字符串对象
for (int i = 0; i < 100; i++) {
    // do something with s
}

这样做可以减少内存分配和回收的次数,提高性能。如果对象的创建和销毁成本较高,可以考虑使用对象池来缓存和复用对象,例如 BitmapPool

3. 避免使用 String 连接符 + 来拼接字符串。

这会产生很多临时的字符串对象,占用内存空间,并触发GC。如果需要拼接字符串,可以使用 StringBuilder 或者 StringBuffer 来代替。例如:

// 不合适的写法
String s = "Hello" + "World" + "!" // 这会创建三个字符串对象

// 高性能的写法
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append("World");
sb.append("!");
String s = sb.toString(); // 这只会创建一个字符串对象

这样做可以避免不必要的字符串对象的创建,节省内存空间,并提高字符串拼接的效率。

4. 避免使用 System.gc() 来主动触发GC。

这会影响系统的自动内存管理机制,并可能导致应用卡顿或者OOM。如果需要释放内存,可以通过合理地设计数据结构和算法来减少内存占用,并及时释放不再使用的对象的引用。例如:

// 不合适的写法
System.gc(); // 强制调用GC

// 高性能的写法
list.clear(); // 清空列表中的元素,并释放引用
list = null; // 将列表对象置为null,让GC自动回收

这样做可以让系统根据内存情况自动调整GC策略,并避免不必要的GC开销。

5. 避免在 onDraw() 方法中创建对象。

这会导致每次绘制都会分配内存,造成内存抖动和GC。如果需要在 onDraw() 方法中使用对象,可以在构造方法或者 onSizeChanged() 方法中创建并复用,或者使用静态常量来代替。例如:

// 不合适的写法
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    Paint paint = new Paint(); // 每次绘制都会创建一个画笔对象
    paint.setColor(Color.RED);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint);
}

// 高性能的写法
private Paint paint; // 在类中声明一个画笔对象

public MyView(Context context) {
    super(context);
    paint = new Paint(); // 在构造方法中创建画笔对象,并设置颜色
    paint.setColor(Color.RED);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawCircle(getWidth() / 2, getHeight() / 2, 100, paint); // 复用画笔对象
}

6. 避免使用 HashMap 来存储少量的键值对。

HashMap 的内部实现需要维护一个数组和一个链表,会占用较多的内存空间,并且可能导致内存碎片。如果只需要存储少量的键值对,可以使用 ArrayMap 或者 SparseArray 来代替。例如:

// 不合适的写法
HashMap<String, Integer> map = new HashMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);

// 高性能的写法
ArrayMap<String, Integer> map = new ArrayMap<>();
map.put("a", 1);
map.put("b", 2);
map.put("c", 3);

这样做可以节省内存空间,因为 ArrayMapSparseArray 的内部实现是使用两个数组来存储键和值,没有额外的开销。另外,它们还可以避免 HashMap 的扩容和哈希冲突的问题。

7. 避免使用 setXxx() 方法来设置视图的属性。

这会导致视图的重新布局和重绘,消耗CPU和内存资源,并可能导致卡顿。如果需要动态改变视图的属性,可以使用属性动画来实现。例如:

// 不合适的写法
view.setAlpha(0.5f); // 设置视图的透明度,会触发视图的重绘

// 高性能的写法
ObjectAnimator.ofFloat(view, "alpha", 0.5f).start(); // 使用属性动画来设置视图的透明度,不会触发视图的重绘

这样做可以避免不必要的视图更新,提高动画效果和流畅度。

8. 避免在 onCreate() 方法中初始化不必要的对象。

这会导致应用启动时间变长,影响用户体验,并可能导致ANR。如果有些对象不需要在启动时就初始化,可以延迟到使用时再初始化,或者放到子线程中初始化。例如:

// 不合适的写法
@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);

   OkHttpClient client = new OkHttpClient(); // 在启动时就创建一个网络客户端对象,占用内存空间,并可能影响启动速度
}

// 高性能的写法
private OkHttpClient client; // 在类中声明一个网络客户端对象

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
}

private OkHttpClient getClient() {
   if (client == null) {
       client = new OkHttpClient(); // 在需要使用时才创建网络客户端对象,节省内存空间,并提高启动速度

9. 避免使用 findViewById() 方法来查找视图。

这会导致每次查找都会遍历视图树,消耗CPU和内存资源,并可能导致卡顿。如果需要使用视图,可以在 onCreate() 方法中使用 findViewById() 方法来获取并保存到变量中,或者使用 ViewBinding 或者 ButterKnife 等库来自动绑定视图。例如:

// 不合适的写法
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

@Override
protected void onResume() {
    super.onResume();
    TextView textView = findViewById(R.id.text_view); // 每次调用都会查找视图树,影响性能
    textView.setText("Hello World");
}

// 高性能的写法
private TextView textView; // 在类中声明一个视图变量

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    textView = findViewById(R.id.text_view); // 在启动时就获取并保存视图对象,避免重复查找
}

@Override
protected void onResume() {
    super.onResume();
    textView.setText("Hello World"); // 复用视图对象
}

这样做可以避免不必要的视图查找,提高性能和流畅度。

10. 避免使用 VectorDrawable 来显示矢量图形。

VectorDrawable 的内部实现是使用 Path 来绘制矢量图形,这会消耗较多的CPU和内存资源,并可能导致卡顿。如果需要显示矢量图形,可以使用 SVG 或者 WebP 等格式来代替。例如:

// 不合适的写法
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM12,4c4.41,0 8,3.59 8,8s-3.59,8 -8,8 -8,-3.59 -8,-8 3.59,-8 8,-8zM6.5,9L10,12.5l-3.5,3.5L8,16l5,-5 -5,-5L6.5,9zM14,13h4v-2h-4v2z" />
</vector>

// 高性能的写法
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_arrow_forward_24px.webp" /> // 使用WebP格式的图片来显示矢量图形,节省CPU和内存资源,并提高绘制效率

这样做可以避免不必要的矢量图形绘制,提高性能和流畅度。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容

  • 通常情况下我们说的内存是指手机的RAM,它主要包括一下几个部分1.寄存器 :速度最快的存储场所,因为寄存器位于...
    gogoingmonkey阅读 1,335评论 1 10
  • 本文转载来源 http://www.csdn.net/article/2015-09-18/2825737/1 (...
    yoosir阅读 1,091评论 0 5
  • 如何避免OOM 一、减小对象的内存占用 1、使用更加轻量的数据结构 例如,我们可以考虑使用ArrayMap/Spa...
    吕侯爷阅读 734评论 0 5
  • 如需转载请评论或简信,并注明出处,未经允许不得转载 目录 前言 在Android中,内存是十分宝贵的资源,内存优化...
    Geekholt阅读 9,367评论 8 30
  • 1.内存与垃圾回收器 1.1.内存管理 不是所有指令都执行得又快又好,下面介绍内存及它如何影响系统运行。普遍认为,...
    贾里阅读 808评论 0 0