1. Introduction
Through the learning of the previous period (senior APP Performance Optimization series of First-line manufacturers – Stuck location (I)), we learned to locate and obtain the time consumption of the program and find the place where the stuck. In this session, we’ll talk about specific optimization schemes, starting with asynchronous optimization
2. Asynchronous optimization
The core idea of asynchronous optimization is that sub-threads share the main thread’s tasks and reduce runtime
Pick up where we left off, using Caton location to find our code at Caton
public class MyApplication extends Application {
@Override
public void onCreate(a) {
super.onCreate();
// Debug.startMethodTracing("MyApplication");
initBugly();
initBaiduMap();
initJPushInterface();
initShareSDK();
// Debug.stopMethodTracing();
}
private void initBugly(a) throws InterruptedException {
initBaiduMap();
Thread.sleep(1000);
}
private void initBaiduMap(a) throws InterruptedException {
Thread.sleep(2000);
}
private void initJPushInterface(a) throws InterruptedException {
Thread.sleep(3000);
}
private void initShareSDK(a) throws InterruptedException {
Thread.sleep(500); }}Copy the code
It takes about 5.5 seconds to open the page after execution, which is too slow and needs to be optimized!
Startup optimization is the first step in large software development, because no matter how much content your APP does, a slow startup will make a bad first impression.
The first thought of asynchronous is to open the thread, but need to pay attention to is not directly to open the thread, because threads lack unified management, may be unlimited new threads, competition between each other, and may occupy too much system resources to cause a crash or OOM, so we use the way of thread pool to implement asynchronous.
If you don’t know about thread pools, look at the principle of bragging thread pools
3. The first change (FixedThreadPool)
Here we use a FixedThreadPool to solve the problem. But the question is, how many threads does this thread pool need to specify?
Some people say, there are a few ways to specify a few. (Suddenly want to ridicule, Ali inside once had a project, is to do so, open the thread pool is according to the mood casually specified a number… In fact, it is not true, but we are too small to say ah)
In order to make more efficient use of CPU, try not to write out the number of thread pools, because different phone manufacturers have different CPU cores. If the number of threads is set too small, some CPUS will be wasted. If the number of threads is set too much, they will preempt CPU, which will also increase the burden. So the correct way to write it is:
// Get the number of cores in the current CPU
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// Set the number of core threads in the thread pool between 2 and 4, depending on the number of CPU cores
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1.4));
// Create a thread pool
ExecutorService executorService = Executors.newFixedThreadPool(CORE_POOL_SIZE);
Copy the code
Experiments show that the number of threads calculated according to this formula is the most reasonable, as to why it is so calculated, not all the CPU is the best number?
If you don’t believe the author, please read the following formula for yourself.
All right, let’s do this with thread pools
@Override
public void onCreate(a) {
super.onCreate();
// Debug.startMethodTracing("MyApplication");
ExecutorService executorService = Executors.newFixedThreadPool(CORE_POOL_SIZE);
executorService.submit(new Runnable() {
@Override
public void run(a) { initBugly(); }}); executorService.submit(new Runnable() {
@Override
public void run(a) { initBaiduMap(); }}); executorService.submit(new Runnable() {
@Override
public void run(a) { initJPushInterface(); }}); executorService.submit(new Runnable() {
@Override
public void run(a) { initShareSDK(); }});// Debug.stopMethodTracing();
}
Copy the code
After testing, now open the page is basically seconds open.
However, there are problems, such as some must be executed before entering the page. That is, you have to let it complete before you can go to the home page.
4. Second transformation (CountDownLatch)
CountDownLatch (CountDownLatch) : CountDownLatch (CountDownLatch) : CountDownLatch (CountDownLatch) : CountDownLatch (CountDownLatch) Each call reduces the number of latches by one, latch.await(); When the deadbolt number is 0, it is released to perform the following logic.
@Override
public void onCreate(a) {
super.onCreate();
// Debug.startMethodTracing("MyApplication");
final CountDownLatch latch = new CountDownLatch(1);
ExecutorService executorService = Executors.newFixedThreadPool(CORE_POOL_SIZE);
executorService.submit(new Runnable() {
@Override
public void run(a) { initBugly(); latch.countDown(); }}); executorService.submit(new Runnable() {
@Override
public void run(a) { initBaiduMap(); }}); executorService.submit(new Runnable() {
@Override
public void run(a) { initJPushInterface(); }}); executorService.submit(new Runnable() {
@Override
public void run(a) { initShareSDK(); }});try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Debug.stopMethodTracing();
}
Copy the code
InitBugly can only be initialized when initBugly is finished. It should be added that there may be other time-consuming tasks in the onCreate method that must be initialized in the main thread, but initBugly can be initialized without the main thread. But it must be initialized to jump to the page. So in order not to increase the time, start the thread pool + door latch to initialize
Okay, so it speeds things up, but it also keeps things that don’t need to be initialized before the main thread is optimized and starts,
But the nith, writing it this way, is A little Low, and if there are some time consuming methods that are associated, for example, you have to finish A, then execute B based on the return value of A, and then execute C based on the return value of B, then you have to do A series, how do we optimize the nith?
5. Third modification (starter)
Look at the picture first, the purpose is to categorize the tasks
Look at the picture, is it a little bit clear? Our task basic is like this, some must be initialized before so task, some must be initialized in the main thread, and some can free in the initialization, some must be initialized again in some task has been completed, such as laser need push device ID, then it must be after obtaining device ID this method performs to perform, So we need to categorize the time-consuming tasks first.
Hence the picture above
- Head Tasks: We will put some tasks here that must be started first
- Main thread: Put tasks that must be initialized in the main thread here
- Concurrency: Put tasks here that do not have to be initialized in the main thread
- Tall Task: some tasks are initialized after all tasks are completed, such as some log printing etc
- Ilde Task: Literally put some tasks in there that can be reinitialized at some time
After classifying, we found that it would be troublesome to do it in a conventional way, such as thread pool + door latch, and the efficiency is not high. For the ultimate performance experience, we will make a launcher by ourselves
Let’s take a look at our improved code
// How to use the initiator
TaskManager manager = TaskManager.getInstance(this);
manager.add(new InitBuglyTask()) // It is added by default and processed concurrently
.add(new InitBaiduMapTask()) // Another time-consuming task, initShareSDK, needs to be processed before it can be processed
.add(new InitJPushTask()) // Wait for the main thread to complete before executing
.add(new InitShareTask())
.start();
manager.startLock();
Copy the code
It’s incredibly simple. Whether it’s an inheritance that requires other tasks to be executed, a wait that requires the main thread to be executed, or a concurrent execution, you only need one method to define a task.
5.1 starter
What is an initiator? Why use it?
We usually have a lot of work to do when an application starts up, and we try to make it as concurrent as possible to speed it up. However, there may be a dependent relationship between these tasks, so we need to find ways to ensure the correctness of their execution order, which is very troublesome.
Although Alibaba has also launched a startup called Alibaba/Alpha
However, Ni, it feels strange to use Ali’s framework in dogdong, so I’m going to lead you to build your own launcher.
If you want to be an initiator, you first have to resolve some dependencies, so for example, we’re passing in tasks A, B, C but if A depends on B, then you need to initialize B, process C, and then process A.
How do I sort the Ni, which has a technical name, topological sort of directed acyclic graphs of tasks
If you don’t know, look at the topological ordering of directed acyclic graphs here
Well, I first posted the algorithm of the code, there is a simple understanding of the next period to lead you step by step to fight their own launcher.
TaskSortUtil.java
package com.bj.performance.alpha.sort;
import android.os.Build;
import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.bj.performance.alpha.task.Task;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class TaskSortUtil {
private static List<Task> taskArrayList = new ArrayList<>();
/** * Topological ordering of directed acyclic graphs for tasks **@return* /
@RequiresApi(api = Build.VERSION_CODES.M)
public static synchronized List<Task> getSortResult(List
originTasks, List
> clsLaunchTasks)
>
{
Set<Integer> dependSet = new ArraySet<>();
Graph graph = new Graph(originTasks.size());
for (int i = 0; i < originTasks.size(); i++) {
Task task = originTasks.get(i);
if (task.dependentArr() == null || task.dependentArr().size() == 0) {
continue;
}
for (Class cls : task.dependentArr()) {
int indexOfDepend = getIndexOfTask(originTasks, clsLaunchTasks, cls);
if (indexOfDepend < 0) {
throw new IllegalStateException(task.getClass().getSimpleName() +
" depends on " + cls.getSimpleName() + " can not be found in task list ");
}
dependSet.add(indexOfDepend);
graph.addEdge(indexOfDepend, i);
}
}
List<Integer> indexList = graph.topologicalSort();
List<Task> newTasksAll = getResultTasks(originTasks, dependSet, indexList);
return newTasksAll;
}
@NonNull
private static List<Task> getResultTasks(List
originTasks, Set
dependSet, List
indexList)
{
List<Task> newTasksAll = new ArrayList<>(originTasks.size());
List<Task> newTasksDepended = new ArrayList<>();// Be dependent on
List<Task> newTasksWithOutDepend = new ArrayList<>();// No dependencies
List<Task> newTasksRunAsSoon = new ArrayList<>();// If you need to increase your priority, do it first.
for (int index : indexList) {
if (dependSet.contains(index)) {
newTasksDepended.add(originTasks.get(index));
} else {
Task task = originTasks.get(index);
if (task.needRunAsSoon()) {
newTasksRunAsSoon.add(task);
} else{ newTasksWithOutDepend.add(task); }}}// Order: dependent ————, need to upgrade its priority ————, need to wait ————, no dependent
taskArrayList.addAll(newTasksDepended);
taskArrayList.addAll(newTasksRunAsSoon);
newTasksAll.addAll(taskArrayList);
newTasksAll.addAll(newTasksWithOutDepend);
return newTasksAll;
}
public static List<Task> getTasksHigh(a) {
return taskArrayList;
}
/** * Gets the task's index ** in the task list@param originTasks
* @return* /
private static int getIndexOfTask(List
originTasks, List
> clsLaunchTasks, Class cls)
>
{
int index = clsLaunchTasks.indexOf(cls);
if (index >= 0) {
return index;
}
// Just protective code
final int size = originTasks.size();
for (int i = 0; i < size; i++) {
if (cls.getSimpleName().equals(originTasks.get(i).getClass().getSimpleName())) {
returni; }}returnindex; }}Copy the code
Graph.java
package com.bj.performance.alpha.sort;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Vector;
/** * Topological sorting algorithm for directed acyclic graphs */
public class Graph {
/ / number of vertices
private int mVerticeCount;
/ / adjacency list
private List<Integer>[] mAdj;
public Graph(int verticeCount) {
this.mVerticeCount = verticeCount;
mAdj = new ArrayList[mVerticeCount];
for (int i = 0; i < mVerticeCount; i++) {
mAdj[i] = newArrayList<Integer>(); }}/** * add edge **@param u from
* @param v to
*/
public void addEdge(int u, int v) {
mAdj[u].add(v);
}
/** * Topology sort */
public Vector<Integer> topologicalSort(a) {
int indegree[] = new int[mVerticeCount];
for (int i = 0; i < mVerticeCount; i++) {// Initializes the number of entries for all points
ArrayList<Integer> temp = (ArrayList<Integer>) mAdj[i];
for (int node : temp) {
indegree[node]++;
}
}
Queue<Integer> queue = new LinkedList<Integer>();
for (int i = 0; i < mVerticeCount; i++) {// Find all the points with an entry degree of 0
if (indegree[i] == 0) { queue.add(i); }}int cnt = 0;
Vector<Integer> topOrder = new Vector<Integer>();
while(! queue.isEmpty()) {int u = queue.poll();
topOrder.add(u);
for (int node : mAdj[u]) {// Find all adjacent points to this point (degree 0)
if (--indegree[node] == 0) {// Subtract the input degree of this point by one. If the input degree becomes 0, add it to the queue with the input degree 0
queue.add(node);
}
}
cnt++;
}
if(cnt ! = mVerticeCount) {// Check if there is a ring. Theoretically, the number of points should be the same as the number of points. If not, there is a ring
throw new IllegalStateException("Exists a cycle in the graph");
}
returntopOrder; }}Copy the code
There’s a lot of this type of code on the web, but Baidu will do
6. Afterword.
Well, was going to want to write all starter in this section, however, this week is a day, rest time is too little, can tear open the content of the chapter for chapter 2, interested friends remember the thumb up and focus on, the next chapters about custom starter, about 3 days, if not busy, update.
This chapter is basically to realize that do not write dead thread pool, that is not optimal solution, and Application or Activity startup items, can be categorized according to the picture above. But ordinary methods are too cumbersome to implement. Keep an eye out for the next launcher chapter, End!