APP性能优化系列-异步优化与拓扑排序(二),牛客java面试宝典pdf

异步优化的核心思想:子线程来分担主线程的任务,并减少运行时间

接着上期的内容,通过卡顿定位,找到我们卡顿处的代码

public class MyApplication extends Application {

@Override

public void onCreate() {

super.onCreate();

// Debug.startMethodTracing(“MyApplication”);

initBugly();

initBaiduMap();

initJPushInterface();

initShareSDK();

// Debug.stopMethodTracing();

}

private void initBugly() throws InterruptedException {

initBaiduMap();

Thread.sleep(1000);

}

private void initBaiduMap() throws InterruptedException {

Thread.sleep(2000);

}

private void initJPushInterface() throws InterruptedException {

Thread.sleep(3000);

}

private void initShareSDK() throws InterruptedException {

Thread.sleep(500);

}

}

执行完毕打开页面大约是 5.5秒,太慢了,需要优化!

启动优化是一个大型软件开发中要做的第一步优化,因为无论你的APP做的内容有多么丰富,如果启动比较慢的话,那么给用户的第一印象就会非常不好。

想到异步首先想到的就是开线程,但是需要注意的是不要直接就去开线程,因为线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom,所以我们使用线程池的方式去执行异步。

对线程池不了解的就去看 大话线程池原理

3.第一次改造(FixedThreadPool)


这里我们采用 FixedThreadPool 来解决卡顿。但是问题来了, 这个线程池需要指定多少个线程合适尼/p>

有人会说,有几个方法就指定几个呗。。(突然想吐槽下,阿里里面曾经有个项目,就是这么做的,开的线程池就是按心情随便指定了个数字。。。其实是不对的,但咱人小势微的也不敢说啊)

为了更高效的利用CPU,尽量线程池数量不要写死,因为不同的手机厂商的CPU核数也是不一样的,设置的线程数过小的话,有的CPU是浪费的,设置过多的话,均抢占CUP,也会增加负担。所以正确的写法是:

// 获得当前CPU的核心数

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

// 设置线程池的核心线程数2-4之间,但是取决于CPU核数

private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT – 1, 4));

// 创建线程池

ExecutorService executorService = Executors.newFixedThreadPool(CORE_POOL_SIZE);

实验证明,根据这个公式来算出的线程数是最合理的,至于为什么是这样算,不是全占用了CPU数是最好的吗/p>

表示不信作者的同学你就自己看下面的公式,欢迎来怼作者!

APP性能优化系列-异步优化与拓扑排序(二),牛客java面试宝典pdf

看了图,是不是略微明白了们的任务基本就是这样的,有的必须要在所以任务之前初始化,有的必须要在主线程初始化,有的可以有空在初始化,有的必须要在有的任务执行完毕再初始化,比如激光推送需要设备ID,那么就必须要在获取设备ID这个方法执行完才能执行,所以我们要对耗时任务先分类。

于是有了上图

  • head task : 我们会把一些必须先启动的task放到这里
  • 主线程:将必须要在主线程中初始化的task放入这里
  • 并发:将非必须在主线程中初始化的task放入这里
  • tall task: 一些在所有任务执行完毕之后才去初始化的放入这里,比如一些log打印等
  • ilde task: 通过字面就知道了将一些可以有空再初始化的task放入这里

好了分好类了之后,我们发现如果用常规的方式去做,比如线程池+门栓就很麻烦了,而且效率不高,为了极致的性能体验,我们会自己做一个启动器

先看下我们完善后的代码

// 使用启动器的方式

TaskDispatcher.init(this);

TaskDispatcher instance = TaskDispatcher.createInstance();

instance.addTask(new initBuglyTask()) // 默认添加,并发处理

instance.addTask(new initBaiduMapTask()) // 在这里需要先处理了另外一个耗时任务initShareSDK,才能再处理它

instance.addTask(new initJPushInterface()) // 等待主线程处理完毕,再进行执行

.start();

instance.await();

看无语伦比的简单,无论是需要别的任务执行完再执行的继承关系,还是必须主线程执行完的等待,还是可以并发的执行,在定义task里面,只需要一个方法即可。

5.1 启动器

什么是启动器啥用它/p>

在应用启动的时候,我们通常会有很多工作需要做,为了提高启动速度,我们会尽可能让这些工作并发进行。但这些工作之间可能存在前后依赖的关系,所以我们又需要想办法保证他们执行顺序的正确性,很麻烦。

虽然阿里也出了个启动器 alibaba / alpha

但是尼,在狗东用阿里的框架总感觉怪怪的,接下来的时间就带领大家去打造一款属于自己的启动器。

要想做启动器,首先是要解决一些依赖关系,比如,我们传入的任务是A,B,C但是尼,如果A依赖于B,那么就需要先初始化B,同时处理C,然后再处理A。

怎么排序尼,这个有个专业的名称,叫做了 任务的有向无环图的拓扑排序

不知道的去看这里 有向无环图的拓扑排序

好了,我先贴出算法的代码,有个简单的认识便可,下期带领大家一步一步的打架自己的启动器。

TaskSortUtil.java

public class TaskSortUtil {

private static List sNewTasksHigh = new ArrayList();// 高优先级的Task

/**

  • 任务的有向无环图的拓扑排序

  • @return

*/

public static synchronized List getSortResult(List originTasks,

List> clsLaunchTasks) {

long makeTime = System.currentTimeMillis();

Set dependSet = new ArraySet();

Graph graph = new Graph(originTasks.size());

for (int i = 0; i

Task task = originTasks.get(i);

if (task.isSend() || task.dependsOn() == null || task.dependsOn().size() == 0) {

continue;

}

for (Class cls : task.dependsOn()) {

int indexOfDepend = getIndexOfTask(originTasks, clsLaunchTasks, cls);

if (indexOfDepend

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 indexList = graph.topologicalSort();

List newTasksAll = getResultTasks(originTasks, dependSet, indexList);

DispatcherLog.i(“task analyse cost makeTime ” + (System.currentTimeMillis() – makeTime));

printAllTaskName(newTasksAll);

return newTasksAll;

}

@NonNull

private static List getResultTasks(List originTasks,

Set dependSet, List indexList) {

List newTasksAll = new ArrayList(originTasks.size());

List newTasksDepended = new ArrayList();// 被别人依赖的

文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树Java异步任务线程与进程92925 人正在系统学习中

来源:普通网友

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年11月19日
下一篇 2021年11月19日

相关推荐