Autor: lu todo mundo sabe
Quando o aplicativo está fazendo otimização de inicialização, o aplicativo fará algum trabalho de inicialização, mas não fará operações demoradas no aplicativo. No entanto, algum trabalho de inicialização pode ser demorado, então o que devo fazer? A operação de inicialização pode ser concluída iniciando um thread filho.
Calcular o tempo de execução
- Solução convencional (marca manual de ponto enterrado)
- AOP maneira de obter
1. Plano convencional
A solução convencional é marcar a hora de início antes da execução, marcar a hora de término após a execução e então calcular a diferença entre a hora de início e a hora de término.A diferença de tempo é o tempo que consome tempo.
A implementação específica do cálculo demorado é mostrada no código a seguir. Muitos métodos de inicialização são chamados no método onCreate em Application. Calculamos o tempo demorado de cada método enterrando manualmente a marca.
//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));
}
O método de cálculo acima é um método de implementação fácil de pensar, mas as desvantagens também são óbvias:
- Cada método é demorado para marcar e calcular, e o código não é elegante o suficiente.
- Muito invasivo para o projeto e muito trabalho.
2. AOP maneira de obter
AOP é o que costumamos dizer
面向切面编程
, pode ser同一类问题
direcionado统一处理
.
A seguir, usaremos o AOP para obter elegantemente o tempo de execução de cada método da Aplicação.
2.1 Apresentando AspectJ
Use o AOP no Android por meio da biblioteca AspectJ e, em seguida, apresente esta biblioteca:
- Introduza o plug-in AspectJ na raiz do projeto build.gradle
- classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
- Plug-in do aplicativo Build.gradle no módulo
- aplicar plug-in: 'android-aspectjx'
- Introduza a biblioteca aspectj em build.gradle no Módulo
- implementação 'org.aspectj:aspectjrt:1.8.9'
2.2 Uso específico de AOP
- Defina uma classe PerformanceAop usando a anotação @Aspect
- @Around("execution(* com.lu.aop.MyApplication.**(...))") indica que cada método em MyApplication precisa ser conectado.
- Registre os carimbos de data/hora antes e depois da execução do método e calcule a diferença de tempo correspondente.
@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
O AOP é implementado de maneira mais elegante, não é invasivo para o código existente e é fácil de modificar.
Execute tarefas demoradas de forma assíncrona
Executar a inicialização dessas bibliotecas de terceiros no aplicativo retardará o processo de inicialização de todo o aplicativo. Portanto, o subthread e o thread principal são usados em paralelo para compartilhar o trabalho do thread principal, reduzindo assim a execução hora da thread principal.
Executar tarefas em threads filho
Podemos facilmente pensar nas duas maneiras a seguir:
- método um
public void onCreate(){
new Thread() {
public run() {
//执行任务1
//执行任务2
//执行任务3
}
}.start();
}
- caminho dois
public void onCreate(){
new Thread() {
public run() {
//执行任务1
}
}.start();
new Thread() {
public run() {
//执行任务2
}
}.start();
new Thread() {
public run() {
//执行任务3
}
}.start();
}
O segundo método faz uso total dos recursos da CPU, mas não é elegante o suficiente para criar threads diretamente, portanto, é melhor usar um pool de threads para gerenciar esses threads.
Gerenciamento de pool de threads
O pool de threads correspondente é obtido, mas o número de threads não pode ser preenchido à vontade. Devemos aproveitá-lo ao máximo CPU 资源
, para que possamos nos referir a AsyncTask
como ele é definido 核心线程数
.
Executors service = Executors.newFixedThreadPool(核心线程个数);
Consulte o código-fonte AsyncTask para entender a configuração do número do 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));
Desta forma, podemos definir o número de threads principais
//参考AsyncTask来设置线程的个数。
ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
Implemente a execução de tarefa assíncrona em 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
A partir da comparação dos dados do Log log, pode-se observar que o tempo de execução do método onCreate executado pela thread principal foi reduzido dos 28ms originais para 4ms. Portanto, é muito bom usar sub-threads para executar tarefas demoradas. Mas há outro problema aqui, ou seja, existem alguns métodos que devem ser inicializados antes que a execução do Application onCreate seja concluída, pois precisam ser utilizados na MainActivity, então haverá problemas com o assíncrono acima, então como resolver este problema Pano de lã?
Tarefas assíncronas devem ser executadas em um determinado estágio
Tomando initImageLoader() como exemplo, não sei quando as sub-threads na Aplicação irão completar a tarefa de inicialização, mas neste momento, elas já entraram na MainActivity, e ImageLoader é usado. ImageLoader não pode ser usado antes da inicialização é concluído no aplicativo, portanto, o ImageLoader deve ser controlado. A inicialização é concluída antes que a execução onCreate do aplicativo seja concluída. Então você precisa usar o CountDownLatch.
CountDownLatch é uma classe de ferramenta de sincronização no pacote java.util.concurrent que permite que um ou mais encadeamentos esperem até que um conjunto de operações em outros encadeamentos seja concluído.
- Definir CountDownLatch no aplicativo
//Application
private CountDownLatch countDownLatch = new CountDownLatch(1);
- Quando o método initImageLoader for executado, 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()
Aguarde no final do método onCreate, se countDownLatch.countDown() for chamado antes aqui, pule diretamente, caso contrário, aguarde aqui.
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 执行完毕");
}
Dessa forma, nosso método onCreate do aplicativo aguardará a conclusão da tarefa assíncrona initImageLoader antes de encerrar o ciclo de vida do método onCreate.
Resumir
- Compreendendo o cálculo do tempo de execução da tarefa
- Compreender o conhecimento de programação orientada a aspectos AOP
- Entenda o número do thread principal e a aplicação do AsyncTask
- Aprendeu o esquema de otimização assíncrona ao inicializar dados
Para ajudar todos a entender melhor a otimização de desempenho de maneira abrangente e clara, preparamos notas básicas relevantes (retornando à lógica subjacente):https://qr18.cn/FVlo89
Notas básicas de otimização de desempenho:https://qr18.cn/FVlo89
Otimização de inicialização
Otimização de memória Otimização
de interface do usuário
Otimização de rede
Otimização de bitmap e otimização de compressão de imagem : Otimização de simultaneidade multi-thread e otimização de eficiência de transmissão de dados Otimização de pacote de volumehttps://qr18.cn/FVlo89
"Estrutura de monitoramento de desempenho do Android":https://qr18.cn/FVlo89
"Manual de aprendizado do Android Framework":https://qr18.cn/AQpN4J
- Processo de inicialização de inicialização
- Inicie o processo Zygote na inicialização
- Inicie o processo do SystemServer na inicialização
- driver de fichário
- Processo de inicialização do AMS
- O processo de inicialização do PMS
- Processo de inicialização do iniciador
- Os quatro principais componentes do Android
- Serviço do sistema Android - processo de distribuição do evento de entrada
- Análise de código-fonte do mecanismo de atualização de tela de renderização subjacente do Android
- Análise de código-fonte do Android na prática