Optimisation des performances Android ---- optimisation du temps d'exécution

Auteur : lu tout le monde le sait

Lorsque l'APP effectue une optimisation de démarrage, l'application effectue un travail d'initialisation, mais n'effectue pas d'opérations fastidieuses dans l'application. Cependant, certains travaux d'initialisation peuvent prendre du temps, alors que dois-je faire ? L'opération d'initialisation peut être terminée en démarrant un thread enfant.

Calculer le temps d'exécution

  • Solution conventionnelle (repère manuel enterré)
  • Manière AOP d'obtenir

1. Régime conventionnel

La solution classique consiste à marquer l'heure de début avant l'exécution, à marquer l'heure de fin après l'exécution, puis à calculer la différence entre l'heure de début et l'heure de fin. La différence de temps est le temps chronophage.

La mise en œuvre de calcul chronophage spécifique est illustrée dans le code suivant. De nombreuses méthodes d'initialisation sont appelées dans la méthode onCreate dans Application. Nous calculons le temps chronophage de chaque méthode en enfouissant manuellement la marque.

//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));    
}

La méthode de calcul ci-dessus est une méthode de mise en œuvre facile à penser, mais les inconvénients sont également évidents :

  • Chaque méthode prend du temps à marquer et à calculer, et le code n'est pas assez élégant.
  • Très envahissant pour le projet et beaucoup de travail.

2. Manière AOP d'obtenir

AOP c'est ce qu'on dit souvent 面向切面编程, ça peut 同一类问题viser 统一处理.

Ensuite, nous utiliserons AOP pour obtenir élégamment le temps d'exécution de chaque méthode de l'Application.

2.1 Présentation d'AspectJ

Utilisez AOP dans Android via la bibliothèque AspectJ, puis introduisez cette bibliothèque :

  • Introduisez le plugin AspectJ dans la racine du projet build.gradle
    • chemin de classe 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
  • Plug-in d'application Build.gradle dans le module
    • appliquer le plugin : 'android-aspectjx'
  • Introduire la bibliothèque aspectj dans build.gradle dans le module
    • implémentation 'org.aspectj:aspectjrt:1.8.9'

2.2 Utilisation spécifique de l'AOP

  • Définir une classe PerformanceAop en utilisant l'annotation @Aspect
  • @Around("execution(* com.lu.aop.MyApplication.**(...))") indique que chaque méthode de MyApplication doit être accrochée.
  • Enregistrez les horodatages avant et après l'exécution de la méthode et calculez la différence de temps correspondante.
@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 est implémenté de manière plus élégante, il est zéro invasif pour le code existant et facile à modifier.

Effectuez des tâches chronophages de manière asynchrone

L'initialisation de ces bibliothèques tierces dans l'application ralentira le processus de démarrage de l'ensemble de l'application. Par conséquent, le sous-thread et le thread principal sont utilisés en parallèle pour partager le travail du thread principal, réduisant ainsi l'exécution. l'heure du fil principal.

Exécuter des tâches dans des threads enfants

On peut facilement penser aux deux manières suivantes :

  • première méthode
  public void onCreate(){
      new Thread() {
          public run() {
            //执行任务1
            //执行任务2
            //执行任务3
          }
      }.start();
    }
  • chemin deux
public void onCreate(){
    new Thread() {
        public run() {
            //执行任务1
        }
    }.start();
    
    new Thread() {
        public run() {
            //执行任务2
        }
    }.start();
    
    new Thread() {
        public run() {
            //执行任务3
        }
    }.start();
}

La deuxième méthode utilise pleinement les ressources du processeur, mais elle n'est pas assez élégante pour créer directement des threads, il est donc préférable d'utiliser un pool de threads pour gérer ces threads.

Gestion du pool de threads

Le pool de threads correspondant est obtenu, mais le nombre de threads ne peut pas être renseigné à volonté, nous devons en faire pleinement usage CPU 资源, afin de pouvoir nous référer à AsyncTaskla façon dont il est défini 核心线程数.

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

Consultez le code source d'AsyncTask pour comprendre le paramètre de numéro de thread principal

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));

De cette façon, nous pouvons définir le nombre de threads principaux

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

Implémentez l'exécution de tâches asynchrones dans 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

À partir de la comparaison des données du journal Log, on peut voir que le temps d'exécution de la méthode onCreate exécutée par le thread principal a été réduit des 28 ms d'origine à 4 ms. Il est donc tout à fait judicieux d'utiliser des sous-threads pour effectuer des tâches chronophages. Mais il y a un autre problème ici, c'est-à-dire que certaines méthodes doivent être initialisées avant la fin de l'exécution de Application onCreate, car elles doivent être utilisées dans MainActivity, il y aura alors des problèmes avec l'asynchrone ci-dessus, alors comment résoudre ce problème Chiffon de laine?

Les tâches asynchrones doivent être exécutées à un certain stade

En prenant initImageLoader() comme exemple, je ne sais pas quand les sous-threads de l'application termineront la tâche d'initialisation, mais à ce moment, ils sont entrés dans MainActivity et ImageLoader est utilisé.ImageLoader ne peut pas être utilisé avant l'initialisation est terminé dans l'application, de sorte que ImageLoader doit être contrôlé. L'initialisation se termine avant la fin de l'exécution de l'application onCreate. Ensuite, vous devez utiliser CountDownLatch.

CountDownLatch est une classe d'outils de synchronisation sous le package java.util.concurrent qui permet à un ou plusieurs threads d'attendre qu'un ensemble d'opérations dans d'autres threads soit terminé.

  • Définir CountDownLatch dans l'application
//Application
private CountDownLatch countDownLatch = new CountDownLatch(1);
  • Lorsque la méthode initImageLoader est exécutée, exécutez countDownLatch.countDown()
private void initImageLoader() {
  Fresco.initialize(this);
   //try {
     //模拟耗时
     //Thread.sleep(3000);
   //} catch (Exception e) {
      // e.printStackTrace();
   //}
   //Log.e(TAG, "初始化initImageLoader完毕");
   //数量减一
   countDownLatch.countDown();
   
}
  • attendre countDownLatch. attendre ()

Attendez à la fin de la méthode onCreate, si countDownLatch.countDown() est appelé avant ici, alors sautez directement, sinon attendez ici.

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 执行完毕");
}

De cette façon, notre méthode Application onCreate attendra que la tâche asynchrone initImageLoader se termine avant de terminer le cycle de vie de la méthode onCreate.

Résumer

  • Comprendre le calcul du temps d'exécution des tâches
  • Comprendre les connaissances en programmation orientée aspect AOP
  • Comprendre le numéro de thread principal et l'application d'AsyncTask
  • Apprentissage du schéma d'optimisation asynchrone lors de l'initialisation des données

Afin d'aider chacun à mieux appréhender l'optimisation des performances de manière complète et claire, nous avons préparé des notes de base pertinentes (en revenant à la logique sous-jacente) :https://qr18.cn/FVlo89

Notes de base sur l'optimisation des performances :https://qr18.cn/FVlo89

Optimisation du démarrage

Optimisation de la mémoire Optimisation

de l'interface utilisateur

Optimisation du réseau

Optimisation des bitmaps et optimisation de la compression des images : optimisation de la concurrence multithread et optimisation de l'efficacité de la transmission des données Optimisation des packages de volumehttps://qr18.cn/FVlo89




"Cadre de surveillance des performances Android":https://qr18.cn/FVlo89

"Manuel d'apprentissage du cadre Android":https://qr18.cn/AQpN4J

  1. Processus d'initialisation de démarrage
  2. Démarrer le processus Zygote au démarrage
  3. Démarrer le processus SystemServer au démarrage
  4. Conducteur de classeur
  5. Processus de démarrage AMS
  6. Le processus de démarrage du PMS
  7. Processus de démarrage du lanceur
  8. Les quatre principaux composants d'Android
  9. Service système Android - processus de distribution de l'événement d'entrée
  10. Analyse du code source du mécanisme de rafraîchissement de l'écran de rendu sous-jacent d'Android
  11. L'analyse du code source Android en pratique

Je suppose que tu aimes

Origine blog.csdn.net/weixin_61845324/article/details/132340696
conseillé
Classement