Android performance optimization----execution time optimization

Author: lu everyone knows

When the APP is doing startup optimization, the Application will do some initialization work, but do not do time-consuming operations in the Application. However, some initialization work may be time-consuming, so what should I do? The initialization operation can be completed by starting a child thread.

Calculate execution time

  • Conventional solution (manual buried point mark)
  • AOP way to get

1. Conventional plan

The conventional solution is to mark the start time before execution, mark the end time after execution, and then calculate the difference between the start time and the end time. The time difference is the time-consuming time.

The specific time-consuming calculation implementation is shown in the following code. Many initialization methods are called in the onCreate method in Application. We calculate the time-consuming time of each method by manually burying the mark.

//Application.java
@Override
public void onCreate() {
    initSharedPreference();
    initImageLoader();
    initSQLite();
    //.....
}

private void initSharedPreference() {
    long startTime = System.currentTimeMillis();
    //init SharedPreference
    Log.d(TAG, "initSharedPreference cost :" + (System.currentTimeMillis() - startTime));    
}

private void initImageLoader() {
    long startTime = System.currentTimeMillis();
    Fresco.initialize(this);
    Log.d(TAG, "initImageLoader cost :" + (System.currentTimeMillis() - startTime));
}
private void initSQLite() {
    long startTime = System.currentTimeMillis();
    //init bugly
    Log.d(TAG, "initSQLite cost :" + (System.currentTimeMillis() - startTime));    
}

The above calculation method is an easy-to-think implementation method, but the disadvantages are also obvious:

  • Each method is time-consuming to mark and calculate, and the code is not elegant enough.
  • Very invasive to the project and a lot of work.

2. AOP way to obtain

AOP is what we often say 面向切面编程, it can be aimed 同一类问题at 统一处理.

Next, we will use AOP to elegantly obtain the execution time of each method of the Application.

2.1 Introducing AspectJ

Use AOP in Android through the AspectJ library, and then introduce this library:

  • Introduce the AspectJ plugin in the project root build.gradle
    • classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4’
  • Build.gradle application plugin in Module
    • apply plugin: ‘android-aspectjx’
  • Introduce aspectj library in build.gradle in Module
    • implementation ‘org.aspectj:aspectjrt:1.8.9’

2.2 Specific use of AOP

  • Define a class PerformanceAop using the @Aspect annotation
  • @Around("execution(* com.lu.aop.MyApplication.**(...))") indicates that each method in MyApplication needs to be hooked.
  • Record the time stamps before and after method execution, and calculate the corresponding time difference.
@Aspect
public class PerformanceAop {

    private static final String TAG = PerformanceAop.class.getSimpleName();

    @Around("execution(* com.lu.aop.MyApplication.**(..))")
    public void getTime(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        //得到方法的名字,例如:MyApplication.attachBaseContext(..)
        String name = signature.toShortString();
        //记录方法执行前的时间戳
        long startTime = System.currentTimeMillis();

        try {
            //执行该方法
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        //记录执行该方法的时间
        long costTime = System.currentTimeMillis() - startTime;

        Log.e(TAG, "method " + name + " cost:" + costTime);
    }
}

运行结果
2019-08-18 17:09:12.946 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-08-18 17:09:12.979 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.initSQLite() cost:11
2019-08-18 17:09:13.002 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.initImageLoader() cost:17
2019-08-18 17:09:13.002 10094-10094/com.lu.aop E/PerformanceAop: method MyApplication.onCreate() cost:28

AOP is implemented in a more elegant way. It is zero-invasive to existing code and easy to modify.

Perform time-consuming tasks asynchronously

Performing the initialization of these third-party libraries in the Application will slow down the startup process of the entire application. Therefore, the sub-thread and the main thread are used in parallel to share the work of the main thread, thereby reducing the execution time of the main thread.

Execute tasks in child threads

We may easily think of the following two ways:

  • method one
  public void onCreate(){
      new Thread() {
          public run() {
            //执行任务1
            //执行任务2
            //执行任务3
          }
      }.start();
    }
  • way two
public void onCreate(){
    new Thread() {
        public run() {
            //执行任务1
        }
    }.start();
    
    new Thread() {
        public run() {
            //执行任务2
        }
    }.start();
    
    new Thread() {
        public run() {
            //执行任务3
        }
    }.start();
}

The second method makes full use of CPU resources, but it is not elegant enough to directly create threads, so it is better to use a thread pool to manage these threads.

Thread pool management

The corresponding thread pool is obtained, but the number of threads cannot be filled in at will. We must make full use of it CPU 资源, so we can refer to AsyncTaskhow it is set 核心线程数.

Executors service = Executors.newFixedThreadPool(核心线程个数);

See the AsyncTask source code to understand the core thread number setting

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//CORE_POOL_SIZE 就是核心线程数
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

In this way we can set the number of core threads

//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);

Implement asynchronous task execution in MyApplication:

@Override
public void onCreate() {
    super.onCreate();
    //参考AsyncTask来设置线程的个数。
    ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
    service.submit(new Runnable() {
        @Override
        public void run() {
            initSQLite();
        }
    });
    service.submit(new Runnable() {
        @Override
        public void run() {
            initImageLoader();
        }
    });
}

异步加载的代码执行结果
2019-08-18 19:09:38.022 13948-13948/com.lu.aop E/PerformanceAop: method MyApplication.attachBaseContext(..) cost:1
2019-08-18 19:09:38.062 13948-13948/com.lu.aop E/PerformanceAop: method MyApplication.onCreate() cost:4
2019-08-18 19:09:38.078 13948-13967/com.lu.aop E/PerformanceAop: method MyApplication.initSQLite() cost:9
2019-08-18 19:09:38.094 13948-13968/com.lu.aop E/PerformanceAop: method MyApplication.initImageLoader() cost:15

From the comparison of Log log data, it can be seen that the execution time of the onCreate method executed by the main thread has been reduced from the original 28ms to 4ms. So it is quite good to use sub-threads to perform time-consuming tasks. But there is another problem here, that is, there are some methods that must be initialized before the execution of Application onCreate is completed, because they need to be used in MainActivity, then there will be problems with the asynchronous above, so how to solve this problem Woolen cloth?

Asynchronous tasks must be executed at a certain stage

Taking initImageLoader() as an example, I don’t know when the sub-threads in the Application will complete the initialization task, but at this time, they have entered the MainActivity, and ImageLoader is used. ImageLoader cannot be used before the initialization is completed in the Application, so the ImageLoader must be controlled Initialization completes before Application onCreate execution completes. Then you need to use CountDownLatch.

CountDownLatch is a synchronization tool class under the java.util.concurrent package that allows one or more threads to wait until a set of operations in other threads has completed.

  • Define CountDownLatch in Application
//Application
private CountDownLatch countDownLatch = new CountDownLatch(1);
  • When the initImageLoader method is executed, execute countDownLatch.countDown()
private void initImageLoader() {
  Fresco.initialize(this);
   //try {
     //模拟耗时
     //Thread.sleep(3000);
   //} catch (Exception e) {
      // e.printStackTrace();
   //}
   //Log.e(TAG, "初始化initImageLoader完毕");
   //数量减一
   countDownLatch.countDown();
   
}
  • await countDownLatch.await()

Wait at the end of the onCreate method, if countDownLatch.countDown() is called before here, then skip directly, otherwise wait here.

public void onCreate() {
   super.onCreate();
   //参考AsyncTask来设置线程的个数。
   ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
   service.submit(new Runnable() {
       @Override
       public void run() {
           initSQLite();
       }
   });
   service.submit(new Runnable() {
       @Override
       public void run() {
           initImageLoader();
       }
   });
   
   
   //在 onCreate 方法中等待,如果在此处之前之前调用了countDownLatch.countDown(),那么就直接跳过,否则就在此等待。
   try {
       countDownLatch.await();
   } catch (InterruptedException e) {
       e.printStackTrace();
   }
   Log.e(TAG, "Application onCreate 执行完毕");
}

In this way, our Application onCreate method will wait for the asynchronous task initImageLoader to complete before ending the life cycle of the onCreate method.

Summarize

  • Understanding Calculating Task Execution Time
  • Understand AOP aspect-oriented programming knowledge
  • Understand the core thread number and application of AsyncTask
  • Learned the asynchronous optimization scheme when initializing data

In order to help everyone better grasp the performance optimization in a comprehensive and clear manner, we have prepared relevant core notes (returning to the underlying logic):https://qr18.cn/FVlo89

Performance optimization core notes:https://qr18.cn/FVlo89

Startup optimization

Memory optimization

UI

optimization Network optimization

Bitmap optimization and image compression optimization : Multi-thread concurrency optimization and data transmission efficiency optimization Volume package optimizationhttps://qr18.cn/FVlo89




"Android Performance Monitoring Framework":https://qr18.cn/FVlo89

"Android Framework Study Manual":https://qr18.cn/AQpN4J

  1. Boot Init process
  2. Start the Zygote process at boot
  3. Start the SystemServer process at boot
  4. Binder driver
  5. AMS startup process
  6. The startup process of the PMS
  7. Launcher's startup process
  8. The four major components of Android
  9. Android system service - distribution process of Input event
  10. Android underlying rendering-screen refresh mechanism source code analysis
  11. Android source code analysis in practice

Guess you like

Origin blog.csdn.net/weixin_61845324/article/details/132340696