An article on high-end Android performance optimization - summary

The following summarizes the performance optimization of Android from several aspects:

1: Interface freeze optimization

2: Memory optimization

3: App startup optimization

UI freeze optimization

The Android interface is 60 frames per second, that is, the drawing of one frame must be completed within 16ms. If a certain method takes time and the drawing cannot be completed within 16ms, it will cause frame loss, and more frames are lost. The intuitive feeling is that The interface freezes.

60 frames is a more suitable frequency for human eyes to watch animations. If the number of frames per second is too small, that is, frequent frame drops occur, the interface will feel stuck.

1: Find out where the main main thread of the card is through Traceview

Stuck in the main thread means that the function is called in the main thread for a long time, including:

1) The duration of a single function call is long

2) The number of times the function is called is relatively large

2: How to use:

1) Terminal opens DDMS, and enters the command: Monitor

2) Click start method profiling

3) Operate the APP interface that may have problems

4) Click stop method profiling again to generate the form:

3: Specific case analysis: onBindViewHolder of recyclerView, when the data outside the screen is reused, it is dirty data, it will be rebound, and onBindViewHolder will be called

@Override

publicvoid onBindViewHolder(RecyclerView.ViewHolder holder, int position)

{

Log.d(TAG, "onBindViewHolder--->" + position);

。。。。。。

       。。。。。。。

SystemClock.sleep(7);

}

SystemClock.sleep(10) simulates the time-consuming operation during the scrolling process of RecyclerView, operates Recyclerview, and obtains the following table:

Real Time/Call indicates the duration of a function being called, Calls+Recursion + call totals indicates the number of times a function is called and recursively called

Memory optimization:

1:Memory Monitor:

Memory Monitor can only look at a rough idea, you can check memory jitter, or the trend of memory growth, specific small leaks, you have to check through Heap Viewer

Memory jitter: There have been many ups and downs of memory in a short period of time, which means that memory jitter is likely to occur.

Problems caused by memory jitter: The memory soars in a short period of time, and the system needs to perform GC frequently, and GC requires the main thread to stop and consumes CPU resources, which will cause the interface to freeze.

Example 1:

publicvoid click(View view) {

for (int i = 0; i < 100; i++) {

byte[] b = newbyte[2000];

}

}

Example 2:

privateint nums[][] = newint[250][250];

//Memory jitter case:

//Create a large number of temporary variables in a short time privatevoid init() {

Random random = new Random();

for (int i = 0; i < nums.length; i++) {

for(int j=0; j<nums[i].length; j++){

nums[i][j] = random.nextInt(1000);

}

}

}

privatevoid printNums(){
        String totalNums = "";
        for (int i = 0; i < nums.length; i++) {
            for(int j=0; j<nums[i].length; j++){
                totalNums += nums[i][j];
            }
        }
    }

//优化:
    private void printNums2(){
        StringBuffer totalNums = new StringBuffer();
        for (int i = 0; i < nums.length; i++) {
            for(int j=0; j<nums[i].length; j++){
                totalNums.append(nums[i][j]);
            }
        }
    }

使用Monitor监控:

避免内存抖动的方法:

1)尽量避免在循环体内创建对象,应该把对象创建移到循环体外

2)自定义view的onDraw会被频繁调用,避免在这个函数里面new一个新的对象

2:Heap Viewer:

监控:能够实时观测内存的变动(短时间内通过Memory Monitor是看不出来的,曲线坡度太小,内存变动值很小,定位不到具体的代码)

Heap Viewer具体定位到哪个位置内存泄漏。

内存泄漏例子:

protectedvoid onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //test1();        init();
        handler.sendEmptyMessageDelayed(0, 100000);
    }
//模拟当MainActivity跳转到MainActivity2的时候,
    // 延迟发送消息导致的内存泄漏问题private Handler handler = new Handler(){
        @Override
        publicvoid handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    publicvoid click(){
        Intent intent = new Intent(this, MainActivity2.class);
        startActivity(intent);
        finish();
    }

首先运行应用,处在MainActivity,点击Dump Java Heap,获取当前内存的快照:

得到以下表格:

修改为根据包名查看更方便快速定位到我们自己的代码:

Leaks为0说明当前没有发生内存泄漏

MainActivity的depth为3,说明MainActivity有被引用

点击MainActivity的click,跳转到MainActivity2,MainActivity.finish(),模拟MainActivty发生内存泄漏的场景(Handler引用MainActivity导致内存泄漏),然后点击GC,再截取内存快照

Dump Java Heap:

黄色的Leaks为2说明发生了内存泄漏,点击Leaks,通过以下References可以找到MainActivity被引用的路径。

3:LeakCanary

在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存

在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存

在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存

在一个Activity执行完onDestroy后,将它放入到WeakReference中,然后将这个WeakReference类型的Activity的对象与ReferenceQueue关联,注意: 如果一个对象要被GC回收了,会把它引用的对象放入到ReferenceQueue中。这时候只需要在ReferenceQueue中去查找是否存在该对象,如果没有就执行一个GC,再次查找,如果还是没有,则说明该对象可能无法被回收,也就可能发生了内存泄漏,最后使用HAHA这个开源库取分析dump之后的heap内存

分为两个步骤:

1)通过虚引用的ReferenceQueue,判断一个对象是否被回收:

虚引用:对于对象来说,是无感的,如果只存在虚引用,GC的时候会直接被回收。虚引用的目的是为了追踪一个对象被回收的时机。如果一个定义了虚引用的对象GC之后被回收了,这个对象会被放入RefereceQueue中,LeakCanary就是在GC之后去检测该队列中是否有该对象判断该对象是否已经被回收。

2)初步判定有内存泄漏之后,通过开源库Haha分析dump之后的heap内存,从而定位到具体的内存泄露的对象的引用链条。

APP应用启动优化

1:冷启动和热启动

1)冷启动:app没有启动过或者进程被杀死,系统不存在该app进程,此时启动为冷启动。冷启动流程就是app启动流程全过程,包括创建app进程、加载资源、启动MainThread、初始化SplashActivity并加载布局等。

2)热启动:app暂时退到了后台,热启动将它从后台重新带到前台,展示给客户。

我们要做的优化就是针对热启动

3):冷启动的函数调用过程:

Zygote Fork Proccess 
-> Application:attachBaseContext()
-> Application:onCreate() 
-> MainActiviity:onCreate()

2:用TraceView获取App的启动耗时,查找具体的耗时的函数进行优化:

publicclass MyApplication extends Application {

@Override

publicvoid onCreate() {

Debug.startMethodTracing("startApp");

super.onCreate();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

Debug.stopMethodTracing();

}

}

在/sdcard/目录下得到一个startApp.trace文件

通过DDMS打开该文件:可以看到Application里面的onCreate里的哪些代码进行了耗时操作:

3:解决方案:

1)将启动页主题背景设置成闪屏页图片

这么做的目的主要是为了消除启动时的黑白屏,给用户一种秒响应的感觉,但是并不会真正减少用户启动时间,仅属于视觉优化。

2)第三方工具的初始化一般都在Application的onCreate里面执行,会造成大量的耗时,解决方案:

a:放在子线程中加载不影响业务的情况,则优先选择放在子线程中加载

b:第三方工具的懒加载初始化,即用到的时候再进行初始化

Guess you like

Origin blog.csdn.net/m0_70748845/article/details/129169099