美文网首页
安卓内存优化案例一

安卓内存优化案例一

作者: 长点点 | 来源:发表于2023-08-30 10:35 被阅读0次

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

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和内存资源,并提高绘制效率

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

相关文章

  • 安卓内存优化

    Android内存 1.Android内存分配与回收机制 从Application Framework、Dalvi...

  • 关于android性能,内存优化

    关于android性能,内存优化 看了些资料整理了下,安卓的性能和内存优化的一些方法和注意事项。分享出来。 随着技...

  • 安卓性能优化3——内存优化

    通常情况下我们说的内存是指手机的RAM,它主要包括一下几个部分1.寄存器 :速度最快的存储场所,因为寄存器位于...

  • Android进阶之内存优化篇

    今天来分析一下安卓的内存优化,安卓应用层的开发大多数开发者会使用Java语言进行开发,其特点之一就是支持GC(Ga...

  • 内存方面优化分析(二)

    安卓内存管理机制 Android是一个基于Linux实现的操作系统,并对Linux的内存管理机制进行了优化。从操作...

  • Android周报第十二期

    1)优化安卓应用内存的神秘方法以及背后的原理,一般人我不告诉他 WindowManagerGlobal.getIn...

  • FMOD热更新在安卓下的堆内存占用

    1)FMOD热更新在安卓下的堆内存占用2)优化MeshSkinning.Render的Draw Call3)通过U...

  • 内存优化(app专项测试)

    1.优化内存的常见操作 (一)内存管理机制 (二)常用内存监控工具 (三)内存优化案例分析 2.内存管理机制 AR...

  • HashMap.ArrayMap的区别

    我们都知道在安卓开发过程中,如果hashmap和Arraymap在内存优化方面,肯定会选择ArrayMap,因为A...

  • 为什么苹果1G内存完爆安卓3G?

    为什么苹果1G内存完爆安卓3G? 为什么苹果1G内存完爆安卓3G? 为什么苹果1G内存完爆安卓3G? 完爆这个词真...

网友评论

      本文标题:安卓内存优化案例一

      本文链接:https://www.haomeiwen.com/subject/vjaomdtx.html