• 安卓内存优化案例穷举


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

    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 {}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样做可以减少内存分配和回收的次数,提高性能。如果对象的创建和销毁成本较高,可以考虑使用对象池来缓存和复用对象,例如 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(); // 这只会创建一个字符串对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

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

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

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

    // 不合适的写法
    System.gc(); // 强制调用GC
    
    // 高性能的写法
    list.clear(); // 清空列表中的元素,并释放引用
    list = null; // 将列表对象置为null,让GC自动回收
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样做可以让系统根据内存情况自动调整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); // 复用画笔对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这样做可以避免在绘制过程中频繁创建和回收对象,提高绘制效率和流畅度。

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

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

    // 不合适的写法
    HashMap map = new HashMap<>();
    map.put("a", 1);
    map.put("b", 2);
    map.put("c", 3);
    
    // 高性能的写法
    ArrayMap map = new ArrayMap<>();
    map.put("a", 1);
    map.put("b", 2);
    map.put("c", 3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

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

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

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

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

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

    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(); // 在需要使用时才创建网络客户端对象,节省内存空间,并提高启动速度
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    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"); // 复用视图对象
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    这样做可以避免不必要的视图查找,提高性能和流畅度。
    ###10. 避免使用 VectorDrawable 来显示矢量图形。
    VectorDrawable 的内部实现是使用 Path 来绘制矢量图形,这会消耗较多的CPU和内存资源,并可能导致卡顿。如果需要显示矢量图形,可以使用 SVG 或者 WebP 等格式来代替。例如:

    // 不合适的写法
    
        
    
    
    // 高性能的写法
     // 使用WebP格式的图片来显示矢量图形,节省CPU和内存资源,并提高绘制效率
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    11. 避免使用 AsyncTask 来执行异步任务。

    AsyncTask 的内部实现是使用一个线程池和一个消息队列来管理任务,这会占用内存空间,并可能导致内存泄漏和并发问题。如果需要执行异步任务,可以使用 RxJava 或者 Coroutine 等库来代替。例如:

    // 不合适的写法
    private class MyTask extends AsyncTask {
    
        private WeakReference contextRef;
    
        public MyTask(Context context) {
            contextRef = new WeakReference<>(context);
        }
    
        @Override
        protected String doInBackground(Void... params) {
            // do some background work
            return "result";
        }
    
        @Override
        protected void onPostExecute(String result) {
            Context context = contextRef.get();
            if (context != null) {
                // do something with result and context
            }
        }
    }
    
    // 高性能的写法
    private fun doAsyncTask(context: Context) {
        CoroutineScope(Dispatchers.IO).launch {
            // do some background work
            val result = "result"
            withContext(Dispatchers.Main) {
                // do something with result and context
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    这样做可以避免不必要的内存分配和回收,避免内存泄漏和并发问题,并提高异步任务的管理和调度。

    ###12. 避免使用 BitmapFactory 来加载图片。
    BitmapFactory 的内部实现是使用 nativeDecodeStream 方法来解码图片,这会消耗较多的内存空间,并可能导致OOM。如果需要加载图片,可以使用 Glide 或者 Picasso 等库来代替。例如:

    // 不合适的写法
    ImageView imageView = findViewById(R.id.image_view);
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image); // 这会创建一个原始大小的位图对象,占用内存空间,并可能导致OOM
    imageView.setImageBitmap(bitmap);
    
    // 高性能的写法
    ImageView imageView = findViewById(R.id.image_view);
    Glide.with(this).load(R.drawable.image).into(imageView); // 这会根据视图的大小和屏幕密度来加载合适大小的位图对象,节省内存空间,并避免OOM
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    这样做可以避免不必要的位图对象的创建,节省内存空间,并提高图片加载的效率和质量。

    13. 避免使用 Serializable 接口来实现序列化。

    Serializable 接口的内部实现是使用反射机制来序列化和反序列化对象,这会消耗较多的CPU和内存资源,并可能导致性能下降。如果需要实现序列化,可以使用 Parcelable 接口或者 ProtoBuf 等库来代替。例如:

    // 不合适的写法
    public class User implements Serializable {
        private String name;
        private int age;
    
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // getter and setter methods
    }
    
    // 高性能的写法
    public class User implements Parcelable {
        private String name;
        private int age;
    
        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // getter and setter methods
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(age);
        }
    
        public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
    
            @Override
            public User createFromParcel(Parcel source) {
                return new User(source.readString(), source.readInt());
            }
    
            @Override
            public User[] newArray(int size) {
                return new User[size];
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    这样做可以避免不必要的反射操作,节省CPU和内存资源,并提高序列化和反序列化的效率。

    14. 避免使用 LinkedList 来存储数据。

    LinkedList 的内部实现是使用一个双向链表来存储数据,这会占用较多的内存空间,并且可能导致内存碎片。如果需要存储数据,可以使用 ArrayList 或者 ArrayDeque 来代替。例如:

    // 不合适的写法
    LinkedList list = new LinkedList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    
    // 高性能的写法
    ArrayList list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样做可以节省内存空间,因为 ArrayList 和 ArrayDeque 的内部实现是使用一个数组来存储数据,没有额外的开销。另外,它们还可以提供更快的随机访问和迭代的性能。

    15. 避免使用 StringTokenizer 来分割字符串。

    StringTokenizer 的内部实现是使用一个字符数组来存储字符串,并且每次调用 nextToken() 方法都会创建一个新的字符串对象,这会消耗较多的CPU和内存资源,并可能导致GC。如果需要分割字符串,可以使用 split() 方法或者 Scanner 类来代替。例如:

    // 不合适的写法
    StringTokenizer st = new StringTokenizer("Hello World!");
    while (st.hasMoreTokens()) {
        String token = st.nextToken(); // 每次调用都会创建一个新的字符串对象
        // do something with token
    }
    
    // 高性能的写法
    String[] tokens = "Hello World!".split(" "); // 这只会创建一个字符串数组对象
    for (String token : tokens) {
        // do something with token
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样做可以避免不必要的字符串对象的创建,节省CPU和内存资源,并提高字符串分割的效率。

    16. 避免使用 SimpleDateFormat 来格式化日期和时间。

    SimpleDateFormat 的内部实现是使用一个 Calendar 对象来存储日期和时间,并且每次调用 format() 方法都会创建一个新的 Date 对象,这会消耗较多的CPU和内存资源,并可能导致GC。如果需要格式化日期和时间,可以使用 DateTimeFormatter 或者 FastDateFormat 等库来代替。例如:

    // 不合适的写法
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date = new Date();
    String formattedDate = sdf.format(date); // 每次调用都会创建一个新的日期对象
    
    // 高性能的写法
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime date = LocalDateTime.now();
    String formattedDate = dtf.format(date); // 这不会创建任何新的对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样做可以避免不必要的日期对象的创建,节省CPU和内存资源,并提高日期和时间格式化的效率。

    17. 避免使用 SparseIntArray 来存储稀疏矩阵。

    SparseIntArray 的内部实现是使用两个数组来存储键和值,这会占用较多的内存空间,并且可能导致数组扩容和复制。如果需要存储稀疏矩阵,可以使用 SparseMatrix 或者 EJML 等库来代替。例如:

    // 不合适的写法
    SparseIntArray matrix = new SparseIntArray();
    matrix.put(0, 1);
    matrix.put(1, 2);
    matrix.put(2, 3);
    
    // 高性能的写法
    SparseMatrix matrix = new SparseMatrix(3, 3);
    matrix.set(0, 0, 1);
    matrix.set(1, 1, 2);
    matrix.set(2, 2, 3);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样做可以节省内存空间,因为 SparseMatrix 和 EJML 的内部实现是使用一个链表或者一个哈希表来存储非零元素,没有额外的开销。另外,它们还可以提供更快的矩阵运算和转置的性能。

    18. 避免使用 JSONObject 来解析JSON字符串。

    JSONObject 的内部实现是使用一个 HashMap 来存储键值对,这会占用较多的内存空间,并且可能导致哈希冲突和扩容。如果需要解析JSON字符串,可以使用 Gson 或者 Moshi 等库来代替。例如:

    // 不合适的写法
    String json = "{\"name\":\"Alice\",\"age\":18}";
    JSONObject jsonObject = new JSONObject(json); // 这会创建一个哈希表对象,占用内存空间,并可能导致哈希冲突和扩容
    String name = jsonObject.getString("name");
    int age = jsonObject.getInt("age");
    
    // 高性能的写法
    String json = "{\"name\":\"Alice\",\"age\":18}";
    Gson gson = new Gson();
    User user = gson.fromJson(json, User.class); // 这会直接创建一个用户对象,节省内存空间,并提高JSON解析的效率
    String name = user.getName();
    int age = user.getAge();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    这样做可以避免不必要的哈希表对象的创建,节省内存空间,并提高JSON解析的效率和质量。

    19. 避免使用 Random 类来生成随机数。

    Random 类的内部实现是使用一个线性同余发生器来生成随机数,这会导致随机数的质量不高,并且可能导致并发问题。如果需要生成随机数,可以使用 ThreadLocalRandom 或者 SecureRandom 等类来代替。例如:

    // 不合适的写法
    Random random = new Random();
    int n = random.nextInt(10); // 这会生成一个不太随机的整数,并且可能导致并发问题
    
    // 高性能的写法
    int n = ThreadLocalRandom.current().nextInt(10); // 这会生成一个更随机的整数,并且避免并发问题
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样做可以提高随机数的质量和安全性,并避免并发问题。

    20. 避免使用 Log 类来打印日志。

    Log 类的内部实现是使用一个 PrintStream 对象来输出日志到控制台或者文件,这会消耗较多的CPU和内存资源,并且可能导致IO阻塞和性能下降。如果需要打印日志,可以使用 Timber 或者 Logger 等库来代替。例如:

    // 不合适的写法
    Log.d("TAG", "Hello World!"); // 这会输出一条日志到控制台或者文件,消耗CPU和内存资源,并且可能导致IO阻塞和性能下降
    
    // 高性能的写法
    Timber.d("Hello World!"); // 这会输出一条日志到控制台或者文件,节省CPU和内存资源,并提高日志输出的效率和质量
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这样做可以避免不必要的IO操作,节省CPU和内存资源,并提高日志输出的效率和质量。

    使用静态内部类或者弱引用来避免非静态内部类持有外部类的引用,造成内存泄漏。

    // 不合适的写法
    public class MainActivity extends AppCompatActivity {
    
        private MyTask task;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            task = new MyTask();
            task.execute();
        }
    
        private class MyTask extends AsyncTask {
            // 这是一个非静态内部类,它会隐式地持有外部类的引用
            @Override
            protected Void doInBackground(Void... params) {
                // do some background work
                return null;
            }
        }
    }
    
    // 高性能的写法
    public class MainActivity extends AppCompatActivity {
    
        private MyTask task;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            task = new MyTask(this);
            task.execute();
        }
    
        private static class MyTask extends AsyncTask {
            // 这是一个静态内部类,它不会持有外部类的引用
            private WeakReference activityRef;
    
            public MyTask(MainActivity activity) {
                activityRef = new WeakReference<>(activity);
            }
    
            @Override
            protected Void doInBackground(Void... params) {
                // do some background work
                return null;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    这样做可以避免内存泄漏,因为如果MainActivity被销毁,而MyTask还在后台运行,那么非静态内部类会导致MainActivity无法被回收,而静态内部类或者弱引用则不会。这样可以节省内存空间,并提高性能。

    使用单例模式时,注意使用Application的Context,而不是Activity的Context,避免Activity无法被回收。

    // 不合适的写法
    public class MySingleton {
    
        private static MySingleton instance;
        private Context context;
    
        private MySingleton(Context context) {
            this.context = context;
        }
    
        public static MySingleton getInstance(Context context) {
            if (instance == null) {
                instance = new MySingleton(context); // 这里使用了Activity的Context,会导致Activity无法被回收
            }
            return instance;
        }
    }
    
    // 高性能的写法
    public class MySingleton {
    
        private static MySingleton instance;
        private Context context;
    
        private MySingleton(Context context) {
            this.context = context.getApplicationContext(); // 这里使用了Application的Context,不会导致Activity无法被回收
        }
    
        public static MySingleton getInstance(Context context) {
            if (instance == null) {
                instance = new MySingleton(context);
            }
            return instance;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    这样做可以避免内存泄漏,因为如果Activity被销毁,而MySingleton还在使用它的Context,那么Activity无法被回收,而Application的Context则不会。这样可以节省内存空间,并提高性能。

    使用Proguard或者R8等工具来混淆和压缩代码,减少方法数和字节码大小。例如:

    android {
      buildTypes {
        release {
          minifyEnabled true // 这里开启了代码混淆和压缩
          proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
          shrinkResources true // 这里开启了资源文件压缩
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    这样做可以减少APK的体积,提高应用的安全性和运行效率。

    使用Lint工具来检测和移除无用的资源文件,减少APK的体积。例如:

    android {
      lintOptions {
        checkReleaseBuilds true // 这里开启了Lint检查
        abortOnError true // 这里设置了如果发现错误就终止编译
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    这样做可以减少APK的体积,提高应用的运行效率和质量。

    使用inBitmap选项来复用Bitmap的内存空间,减少内存分配。例如:

    // 不合适的写法
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options); // 这里没有使用inBitmap选项,会导致每次都分配新的内存空间
    
    // 高性能的写法
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = false;
    options.inBitmap = reusableBitmap; // 这里使用了inBitmap选项,会复用已有的内存空间,reusableBitmap是一个合适大小的位图对象
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这样做可以减少内存分配和回收的次数,提高性能和流畅度。

    使用inSampleSize选项来按比例缩放图片,避免加载过大的图片。例如:

    // 不合适的写法
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = false;
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options); // 这里没有使用inSampleSize选项,会加载原始大小的图片,占用内存空间,并可能导致OOM
    
    // 高性能的写法
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(getResources(), R.drawable.image, options); // 这里先获取图片的原始宽高,不加载图片到内存中
    int width = options.outWidth;
    int height = options.outHeight;
    int inSampleSize = 1; // 这里根据需要计算一个合适的缩放比例,例如根据视图的大小和屏幕密度等因素
    options.inJustDecodeBounds = false;
    options.inSampleSize = inSampleSize; // 这里使用inSampleSize选项,会按比例缩放图片,节省内存空间,并避免OOM
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image, options);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这样做可以避免加载过大的图片,节省内存空间,并提高图片加载的效率和质量。

    优化布局文件,减少布局层级和冗余控件,使用include、merge、ViewStub等标签来复用和延迟加载布局。例如:

    
    
    
        
    
        
    
            
    
            
    
        
    
    
    
    
    
    
        
    
        
    
        
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    这样做可以减少布局层级和冗余控件,提高布局加载和渲染的效率和流畅度。如果想要复用和延迟加载布局,可以使用include、merge、ViewStub等标签来实现。

    Android 学习笔录

    Android 性能优化篇:https://qr18.cn/FVlo89
    Android Framework底层原理篇:https://qr18.cn/AQpN4J
    Android 车载篇:https://qr18.cn/F05ZCM
    Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
    Android 音视频篇:https://qr18.cn/Ei3VPD
    Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
    OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
    Kotlin 篇:https://qr18.cn/CdjtAF
    Gradle 篇:https://qr18.cn/DzrmMB
    Flutter 篇:https://qr18.cn/DIvKma
    Android 八大知识体:https://qr18.cn/CyxarU
    Android 核心笔记:https://qr21.cn/CaZQLo
    Android 往年面试题锦:https://qr18.cn/CKV8OZ
    2023年最新Android 面试题集:https://qr18.cn/CgxrRy
    Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
    音视频面试题锦:https://qr18.cn/AcV6Ap

  • 相关阅读:
    【数据库原理及应用】为什么要学习数据库?数据库的由来和发展以及数据库和数据管理系统是什么?
    GBase 8s gcadmin之distribution命令解析
    Java栈和队列的实现
    【HTML学生作业网页】基于HTML+CSS+JavaScript仿南京师范大学泰州学院(11页)
    赋能建筑建材企业物流网络内外联通,B2B交易管理系统打造行业智慧供应链
    [springMVC学习]10、文件上传和下载
    vscode运行Java utf-8文件中文乱码报错
    Blue Prism 异常处理
    【无标题】
    threadSafeMap代码解析
  • 原文地址:https://blog.csdn.net/weixin_61845324/article/details/132887667