为何要作启动器?直接写它不香吗?来先回顾下恶心的代码结构java
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); // 一堆耗时方法,严重影响启动 initBugly(); initBaiduMap(); initJPushInterface(); initShareSDK(); } } 复制代码
面对这些比较恶心的启动方法,为了加快启动,咱们通常会采用线程池的方式启动,一线大厂资深APP性能优化系列-异步优化与拓扑排序(二),算法
可是若是有的方法本身须要依赖的方法执行完毕才能执行,好比 initJPushInterface() 可能须要先执行完毕 GetDeviceID() 执行完毕才能进行再执行,那么把它们都放入线程池里面并行执行就会产生问题,另外有的方法好比initBugly(); 必须先执行完它以后,主线程才能执行完毕,再跳转页面。那么由于这些问题,若是只是用线程池来并行,就会致使代码写起来过于复杂。性能优化
这也就是为何要推出启动器的缘由,固然阿里作的仍是不错的,可是狗东用阿里作的启动器感受怪怪的,因此跟着做者一块儿从零搭建一个启动器吧。markdown
首先,咱们要定义本身的一些task, 就是用来执行耗时方法的。先定义个接口吧。并发
/** * @author: lybj * @date: 2020/5/14 * @Description: */ public interface ITask { void run(); /** * Task执行所在的线程池,可指定,通常默认 */ Executor runOnExecutor(); /** * 存放须要先执行的task任务集合(也就是添加须要先执行的依赖) */ List<Class<? extends ITask>> dependentArr(); /** * 开始锁 * */ void startLock(); /** * 解锁 * */ void unlock(); /** * 异步线程执行的Task是否须要在被调用await的时候等待,默认不须要 * * @return */ boolean needWait(); /** * 是否在主线程执行 */ boolean runOnMainThread(); /** * Task主任务执行完成以后须要执行的任务 */ Runnable getTailRunnable(); } 复制代码
好了,这些基本够用了。异步
public abstract class Task implements ITask { // 当前Task依赖的Task数量(须要等待被依赖的Task执行完毕才能执行本身),默认没有依赖 private CountDownLatch taskCountDownLatch = new CountDownLatch(dependentArr() == null ? 0 : dependentArr().size()); /** * 当前Task等待,让依赖的Task先执行 */ @Override public void startLock() { try { taskCountDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } } /** * 依赖的Task执行完一个 */ @Override public void unlock() { taskCountDownLatch.countDown(); } /** * 是否须要尽快执行,解决特殊场景的问题:一个Task耗时很是多可是优先级却通常,颇有可能开始的时间较晚, * 致使最后只是在等它,这种能够早开始。 */ public boolean needRunAsSoon() { return false; } /** * Task的优先级,运行在主线程则不要去改优先级 */ @Override public int priority() { return Process.THREAD_PRIORITY_BACKGROUND; } /** * Task执行在哪一个线程池,默认在IO的线程池; */ @Override public ExecutorService runOnExecutor() { return DispatcherExecutor.getIOExecutor(); } /** * 异步线程执行的Task是否须要在被调用await的时候等待,默认不须要 */ @Override public boolean needWait() { return false; } /** * 当前Task依赖的Task集合(须要等待被依赖的Task执行完毕才能执行本身),默认没有依赖 */ @Override public List<Class<? extends ITask>> dependentArr() { return null; } @Override public boolean runOnMainThread() { return false; } @Override public Runnable getTailRunnable() { return null; } } 复制代码
很简单,主要作的是:
1.dependentArr() 定义一个栅栏ide
很好理解,传入的task(咱们的耗时任务),由于须要依赖,好比TaskA,必须得等TaskB,TaskC加载完毕才能加载TaskA,dependentArr()返回的就是TaskB,TaskC,也就是在TaskA中加了几个同步锁(锁的数量就是TaskA所须要依赖的Task数量),unlock()就减小一把锁。oop
外部调用post
TaskManager manager = TaskManager.getInstance(this); manager.add(new InitBuglyTask()) // 默认添加,并发处理 .add(new InitBaiduMapTask()) // 在这里须要先处理了另一个耗时任务initShareSDK,才能再处理它 .add(new InitJPushTask()) // 等待主线程处理完毕,再进行执行 .add(new InitShareTask()) .start(); manager.startLock(); 复制代码
构建启动器性能
public class TaskManager { private static TaskManager sInstance; private Context mContext; // 维持task和它的依赖Task的依赖关系,这里是仿照EventBus的存放事件的机制设计 private HashMap<Class<? extends ITask>, ArrayList<ITask>> dependOfTaskArray = new HashMap<>(); // 存放已经执行完毕的Task队列 private volatile List<Class<? extends ITask>> taskFinishedArray = new ArrayList<>(); // 存放全部的task private List<Task> taskAll = new ArrayList<>(); private List<Class<? extends Task>> taskAllClazz = new ArrayList<>(); // 须要在主线程中执行的Task队列 private volatile List<Task> mainThreadTaskArray = new ArrayList<>(); // 主线程须要等待先执行的task数量 private AtomicInteger mainNeedWaitCount = new AtomicInteger(); private CountDownLatch mCountDownLatch; private static final int WAITTIME_TIME = 996 * 31; private List<Future> futureArray = new ArrayList<>(); private TaskManager(Context context) { if (context == null) { throw new IllegalArgumentException("Context is null."); } mContext = context; } /** * 使用单例模式建立对象 */ public static TaskManager getInstance(Context context) { if (sInstance == null) { sInstance = new TaskManager(context); } return sInstance; } /** * 添加任务 */ public TaskManager add(Task task) { if (task == null) { throw new IllegalArgumentException("task is null !"); } // ->> 1 setDependentOfTask(task); // ->> 2 taskAll.add(task); taskAllClazz.add(task.getClass()); // ->> 3 // 非主线程且须要wait的 if (ifNeedWait(task)) { // 主线程的锁加一把 mainNeedWaitCount.getAndIncrement(); } return this; } /** * 获取依赖的集合,主要作的为两件事 * * 1.是以依赖类为Key,对应的依赖者的集合为value添加进map里面 * 2.在从完成的任务集合里面查询,该task所依赖的类是否已经完成,完成的话进行解锁 * */ private void setDependentOfTask(Task task) { if (task.dependentArr() != null && task.dependentArr().size() > 0) { for (Class<? extends ITask> dependTaskClazz : task.dependentArr()) { if (dependOfTaskArray.get(dependTaskClazz) == null) { dependOfTaskArray.put(dependTaskClazz, new ArrayList<ITask>()); } // 若是该task所依赖的依赖任务已经加载过了,就解锁其中已经完成的 dependOfTaskArray.get(dependTaskClazz).add(task); if (taskFinishedArray.contains(dependTaskClazz)) { task.unlock(); } } } } private boolean ifNeedWait(Task task) { return !task.runOnMainThread() && task.needWait(); } @UiThread public void start() { if (Looper.getMainLooper() != Looper.myLooper()) { throw new RuntimeException("小子,启动器必需要在主线程启动"); } if (taskAll.size() > 0) { // 4.->> 效率排序 taskAll = TaskSortUtil.getSortResult(taskAll, taskAllClazz); // 5.->> 构建同步锁 mCountDownLatch = new CountDownLatch(mainNeedWaitCount.get()); // 6.->> 分发任务 dispatchTasks(); runOnMainThread(); } } /** * task分发,根据设定的不一样规则,分发到不一样的线程 */ private void dispatchTasks() { for (final Task task : taskAll) { // 若是是须要在主线程中运行的,加入到主线程队列中 if (task.runOnMainThread()) { mainThreadTaskArray.add(task); } else { // 异步线程中执行,是否执行取决于具体线程池 Future future = task.runOnExecutor().submit(new TaskRunnable(task, this)); futureArray.add(future); } } } private void runOnMainThread() { for (Task task : mainThreadTaskArray) { new TaskRunnable(task,this).run(); } } @UiThread public void startLock() { try { if (mainNeedWaitCount.get() > 0) { mCountDownLatch.await(WAITTIME_TIME, TimeUnit.MILLISECONDS); } } catch (InterruptedException e) { } } /** * 取消 * */ public void cancel() { for (Future future : futureArray) { future.cancel(true); } } /** * 当完成一个任务以后,通知全部依赖它的任务,并解锁他们 */ public void unLockForChildren(Task task) { ArrayList<ITask> arrayList = dependOfTaskArray.get(task.getClass()); if (arrayList != null && arrayList.size() > 0) { for (ITask subTask : arrayList) { subTask.unlock(); } } } // 7 ->> public void finish(Task task) { if (ifNeedWait(task)) { taskFinishedArray.add(task.getClass()); mCountDownLatch.countDown(); mainNeedWaitCount.getAndDecrement(); } } } 复制代码
首先是经过getInstance()构造了一个实例对象,而后经过addTask() 添加咱们的Task, 若是它不为空的话
根据上面的角标,逐一介绍
好了如何执行任务尼?
/** * 任务真正执行的地方 */ public class TaskRunnable implements Runnable { private Task task; private TaskManager taskManager; public TaskRunnable(Task task) { this.task = task; } public TaskRunnable(Task task, TaskManager taskManager) { this.task = task; this.taskManager = taskManager; } @Override public void run() { TraceCompat.beginSection(task.getClass().getSimpleName()); Process.setThreadPriority(task.priority()); task.startLock(); task.run(); // 执行Task的尾部任务 Runnable tailRunnable = task.getTailRunnable(); if (tailRunnable != null) { tailRunnable.run(); } if (!task.runOnMainThread()) { if(taskManager != null){ taskManager.unLockForChildren(task); taskManager.finish(task); } } TraceCompat.endSection(); } } 复制代码
好了,是否是很简单? 优先执行须要依赖的Task, 而后再执行本身,等都执行完毕后,调用 taskManager.unLockForChildren(mTask); 将该task从等待队列中移除,添加进结束队列,若是该task须要主线程等待的话,主线程的同步锁-1,等待队列数-1
再看下咱们本身的task
public class InitJPushTask extends Task { @Override public boolean needWait() { return true; } @Override public List<Class<? extends ITask>> dependentArr() { List<Class<? extends ITask>> tasks = new ArrayList<>(); tasks.add(InitShareTask.class); return tasks; } @Override public void run() { try { Thread.sleep(1500); System.out.println("InitJPushTask运行完毕,它所在的线程是:"+Thread.currentThread().getName()); } catch (InterruptedException ex) { ex.printStackTrace(); } } } 复制代码
咱们本身的这个Task就写完看,这是一个须要先执行完毕GetDeviceIdTask, 而后须要执行完毕本身,才能容许Application去加载页面的任务,看是否是很是简单,看下Application的改造
TaskManager manager = TaskManager.getInstance(this); manager.add(new InitBuglyTask()) // 默认添加,并发处理 .add(new InitBaiduMapTask()) // 在这里须要先处理了另一个耗时任务initShareSDK,才能再处理它 .add(new InitJPushTask()) // 等待主线程处理完毕,再进行执行 .add(new InitShareTask()) .start(); manager.startLock(); 复制代码
这个启动器目前已经在某厂的一个比较成熟的项目中使用了,目测仍是蛮好用的,确实比以前启动速度提高了不少,大约能提高到3秒,还有其他的大概18-19章节,包含不少核心的优化及大型APP的容灾方案,前几天有人联系我,想让我把接下来的内容写书,犹豫了好久,不知道大家是爱看书,仍是喜欢看博客,欢迎在底下留言