写点什么

APP 性能优化系列 - 自定义启动器(三),阿里巴巴 java 面试几轮

作者:Java高工P7
  • 2021 年 11 月 09 日
  • 本文字数:3604 字

    阅读完需:约 12 分钟

/**


  • 当前 Task 依赖的 Task 集合(需要等待被依赖的 Task 执行完毕才能执行自己),默认没有依赖

  • @return


*/


@Override


public List<Class<? extends Task>> dependsOn() {


return null;


}


}


很简单,主要做的是:


1.根据 dependsOn() 定义一个栅栏


很好理解,传入的 task(我们的耗时任务),因为需要依赖,比如 TaskA,必须得等 TaskB,TaskC 加载完毕才能加载 TaskA,那么 dependsOn()返回的就是 TaskB,TaskC,也就是在 TaskA 中加了几个同步锁(锁的数量就是 TaskA 所需要依赖的 Task 数量),每次执行 satisfy()就减少一把锁。


3.实现启动器




外部调用


TaskDispatcher instance = TaskDispatcher.createInstance();


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


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


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


.start();


instance.await();


构建启动器


public class TaskDispatcher {


private static Context mContext;


private static boolean sHasInit;


private static boolean sIsMainProcess;


// 存放依赖


private HashMap<Class<? extends Task>, ArrayList<Task>> mDependedHashMap = new HashMap<>();


// 存放所有的 task


private List<Task> mAllTasks = new ArrayList<>();


private List<Class<? extends Task>> mClsAllTasks = new ArrayList<>();


// 调用了 await 的时候还没结束的且需要等待的 Task 队列


private List<Task> mNeedWaitTasks = new ArrayList<>();


// 已经结束了的 Task 队列


private volatile List<Class<? extends Task>> mFinishedTasks = new ArrayList<>(100);


// 需要在主线程中执行的 Task 队列


private volatile List<Task> mMainThreadTasks = new ArrayList<>();


// 保存需要 Wait 的 Task 的数量


private AtomicInteger mNeedWaitCount = new AtomicInteger();


private CountDownLatch mCountDownLatch;


/**


  • 注意:每次获取的都是新对象


*/


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


public static TaskDispatcher getInstance(Context context) {


if (context != null) {


mContext = context;


sHasInit = true;


sIsMainProcess = Utils.isMainProcess(mContext);


}


return new TaskDispatcher();


}


/**


  • 添加任务


*/


public TaskDispatcher addTask(Task task) {


if (task != null) {


// ->> 1


collectDepends(task);


// ->> 2


mAllTasks.add(task);


mClsAllTasks.add(task.getClass());


// ->> 3


if (ifNeedWait(task)) {


mNeedWaitTasks.add(task);


mNeedWaitCount.getAndIncrement();


}


}


return this;


}


/**


  • 存放相关依赖信息

  • */


private void collectDepends(Task task) {


// 如果存在依赖


if (task.dependsOn() != null && task.dependsOn().size() > 0) {


// 获取依赖


for (Class<? extends Task> cls : task.dependsOn()) {


if (mDependedHashMap.get(cls) == null) {


mDependedHashMap.put(cls, new ArrayList<Task>());


}


mDependedHashMap.get(cls).add(task);


if (mFinishedTasks.contains(cls)) {


task.satisfy();


}


}


}


}


/**


  • task 是否需要主线程等其完成再执行

  • */


private boolean ifNeedWait(Task task) {


return !task.runOnMainThread() && task.needWait();


}


@UiThread


public void start() {


if (Looper.getMainLooper() != Looper.myLooper()) {


throw new RuntimeException("小子,启动器必须要在主线程启动");


}


if (mAllTasks.size() > 0) {


// 4.->> 查看被依赖的信息


printDependedMsg();


// 5.->> 拓扑排序并返回


mAllTasks = TaskSortUtil.getSortResult(mAllTasks, mClsAllTasks);


// 6.->> 构建同步锁


mCountDownLatch = new CountDownLatch(mNeedWaitCount.get());


// 7.->> 分发 task


dispatchTasks();


executeTaskMain();


}


}


/**


  • 查看被依赖的信息


*/


private void printDependedMsg() {


DispatcherLog.i("needWait size : " + (mNeedWaitCount.get()));


if (false) {


for (Class<? extends Task> cls : mDependedHashMap.keySet()) {


DispatcherLog.i("cls " + cls.getSimpleName() + " " + mDependedHashMap.get(cls).size());


for (Task task : mDependedHashMap.get(cls)) {


DispatcherLog.i("cls " + task.getClass().getSimpleName());


}


}


}


}


/**


  • task 分发,根据设定的不同规则,分发到不同的线程


*/


private void dispatchTasks() {


for (Task task : mAllTasks) {


if (task.runOnMainThread()) {


mMainThreadTasks.add(task);


if (task.needCall()) {


task.setTaskCallBack(new TaskCallBack() {


@Override


public void call() {


TaskStat.markTaskDone();


task.setFinished(true);


satisfyChildren(task);


markTaskDone(task);


}


});


}


} else {


// 异步线程中执行,是否执行取决于具体线程池


Future future = task.runOn().submit(new DispatchRunnable(task,this));


mFutures.add(future);


}


}


/**


  • 从等待队列中移除,添加进结束队列


*/


public void markTaskDone(Task task) {


// 8 ->>


if (ifNeedWait(task)) {


mFinishedTasks.add(task.getClass());


mNeedWaitTasks.remove(task);


mCountDownLatch.countDown();


mNeedWaitCount.getAndDecrement();


}


}


private void executeTaskMain() {


mStartTime = System.currentTimeMillis();


for (Task task : mMainThreadTasks) {


long time = System.currentTimeMillis();


new DispatchRunnable(task,this).run();


}


}


首先是通过 getInstance()构造了一个实例对象,然后通过 addTask() 添加我们的 Task, 如果它不为空的话


根据上面的角标,逐一介绍


  1. 调用 collectDepends(),遍历该 task 所依赖的全部 task,并且以它所依赖的 task 为 Key, 本身为 Value 中集合元素的一员添加进去,然后判断,该 task 中的依赖是否已经加载过了,如果加载过了,调用该 task 的 satisfy()方法减该 task 的一把锁。

  2. 然后将这个 task 和它的 class 文件添加到 2 个集合中,方便后面使用。

  3. 如果该 Task 需要主线程等其完成再执行的话,则添加到等待队列中,等待队列计数器+1

  4. 打印该 task 所依赖的信息

  5. 拓扑排序,经典的算法,用于描述依赖关系的排序,在上一章节有过介绍也给出过源码,这里就不再赘述

  6. 这里实际上就是构建一把锁,这个锁注意并不在 Task 里面,Task 里面的锁,注意是为了先执行依赖的 Task,执行完毕,再执行自己,而这里的锁是在启动器上,其作用是让主线程等待,优先执行那些必须要先执行完毕才能让主线程继续执行完毕,再跳转页面的 task

  7. 根据需要分发不同的线程去执行,如果是需要在主线程中执行,那就先存储起来,如果是需要在一部现场中执行,那就直接调用 task.runOn()方法来异步执行耗时 task,runOn()可复写,不写为默认线程池

  8. 如果该线程需要在主线程中执行,将它从等待队列中移除,添加进结束队列,如果该 task 需要主线程等待的话,主线程的同步锁-1,等待队列数-1


4.DispatchRunnable 的实现




好了如何执行任务尼?


public class DispatchRunnable implements Runnable {


private Task mTask;


private TaskDispatcher mTaskDispatcher;


public DispatchRunnable(Task task) {


this.mTask = task;


}


public DispatchRunnable(Task task,TaskDispatcher dispatcher) {


this.mTask = task;


this.mTaskDispatcher = dispatcher;


}


@Override


public void run() {


Process.setThreadPriority(mTask.priority());


long startTime = System.currentTimeMillis();


mTask.setWaiting(true);


mTask.waitToSatisfy();


long waitTime = System.currentTimeMillis() - startTime;


startTime = System.currentTimeMillis();


// 执行 Task


mTask.setRunning(true);


mTask.run();


// 执行 Task 的尾部任务


Runnable tailRunnable = mTask.getTailRunnable();


if (tailRunnable != null) {


tailRunnable.run();


}


if (!mTask.needCall() || !mTask.runOnMainThread()) {


printTaskLog(startTime, waitTime);


TaskStat.markTaskDone();


mTask.setFinished(true);


if(mTaskDispatcher != null){


mTaskDispatcher.satisfyChildren(mTask);


// --> 8


mTaskDispatcher.markTaskDone(mTask);


}


}


TraceCompat.endSection();


}


}


复制代码


好了,是不是很简单? 优先执行需要依赖的 Task, 然后再执行自己,等都执行完毕后,调用 mTaskDispatcher.markTaskDone(mTask); 将该 task 从等待队列中移除,添加进结束队列,如果该 task 需要主线程等待的话,主线程的同步锁-1,等待队列数-1


再看下我们自己的 task


public class InitJPushTask extends Task {


@Override


public boolean needWait() {


return true;


}


@Override


public List<Class<? extends Task>> dependsOn() {


List<Class<? extends Task>> tasks = new ArrayList<>();


tasks.add(GetDeviceIdTask.class);


return tasks;


}


@Override


public void run() {


// 模拟 InitJPush 初始化


try {


Thread.sleep(1500);


} catch (InterruptedException ex) {


ex.printStackTrace();


}


}


}

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
APP性能优化系列-自定义启动器(三),阿里巴巴java面试几轮