一线大厂大型APP性能优化系列(三)自定义启动器

为什么要做启动器?直接写它不香吗?来先回顾下恶心的代码结构

public class MyApplication extends Application {

@Override
public void onCreate() {
super.onCreate();

// 一堆耗时方法,严重影响启动
initBugly();
initBaiduMap();
initJPushInterface();
initShareSDK();
}
}

面对这些比较恶心的启动方法,为了加快启动,我们一般会采用线程池的方式启动,一线大厂资深APP性能优化系列-异步优化与拓扑排序(二)

但是如果有的方法自己需要依赖的方法执行完毕才能执行,比如 initJPushInterface() 可能需要先执行完毕 GetDeviceID() 执行完毕才能进行再执行,那么把它们都放入线程池里面并行执行就会产生问题,另外有的方法比如initBugly(); 必须先执行完它之后,主线程才能执行完毕,再跳转页面。那么因为这些问题,如果只是用线程池来并行,就会导致代码写起来过于复杂。

这也就是为什么要推出启动器的原因,当然阿里做的还是不错的,但是狗东用阿里做的启动器感觉怪怪的,所以跟着作者一起从零搭建一个启动器吧。

1.定义task接口

首先,我们要定义自己的一些task, 就是用来执行耗时方法的。先定义个接口吧。

/**

  • @author: lybj
  • @date: 2020/5/14
  • @Description:
    */
    public interface ITask {

void run();

/**

  • Task执行时所在的线程池,可以指定,一般使用默认
    */
    Executor runOn();

/**

  • 存放需要先执行的task任务集合
    */
    List<Class<? extends Task>> dependsOn();

/**

  • 该task如果是异步任务,是否需要先等待自己完成后,
  • 主线程在继续,也就是是否需要在主线程调用await的时候等待
    */
    boolean needWait();

/**

  • 是否在主线程执行
    */
    boolean runOnMainThread();

/**

  • 只能在主进程执行
    */
    boolean onlyOnMainProcess();

/**

  • Task主任务执行完成之后需要执行的任务
    */
    Runnable getTailRunnable();

/**

  • Task执行过程中的回调
    */
    void setTaskCallBack(TaskCallBack callBack);
    }

好了,这些基本够用了。

2.实现task接口

public abstract class Task implements ITask {

private volatile boolean mIsWaiting; // 是否正在等待
private volatile boolean mIsRunning; // 是否正在执行
private volatile boolean mIsFinished; // Task是否执行完成
private volatile boolean mIsSend; // Task是否已经被分发

// 当前Task依赖的Task数量(需要等待被依赖的Task执行完毕才能执行自己),默认没有依赖
private CountDownLatch mDepends = new CountDownLatch(dependsOn() == null ? 0 : dependsOn().size());

/**

  • 依赖的Task执行完一个
    */
    public void satisfy() {
    mDepends.countDown();
    }

/**

  • 当前Task等待,让依赖的Task先执行
    */
    public void waitToSatisfy() {
    try {
    mDepends.await();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

/**

  • 异步线程执行的Task是否需要在被调用await的时候等待(也就是是否需要主线程等你执行完再执行),默认不需要
  • @return
    */
    @Override
    public boolean needWait() {
    return false;
    }

/**

  • 当前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> mDependedHashMap = new HashMap<>();

// 存放所有的task
private List mAllTasks = new ArrayList<>();
private List<Class<? extends Task>> mClsAllTasks = new ArrayList<>();

// 调用了await的时候还没结束的且需要等待的Task队列
private List mNeedWaitTasks = new ArrayList<>();

// 已经结束了的Task队列
private volatile List<Class<? extends Task>> mFinishedTasks = new ArrayList<>(100);

// 需要在主线程中执行的Task队列
private volatile List mMainThreadTasks = new ArrayList<>();

// 保存需要Wait的Task的数量
private AtomicInteger mNeedWaitCount = new AtomicInteger();
private CountDownLatch mCountDownLatch;

/**

  • 注意:每次获取的都是新对象
    */
    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());
}
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) {

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

要如何成为Android架构师?

搭建自己的知识框架,全面提升自己的技术体系,并且往底层源码方向深入钻研。
大多数技术人喜欢用思维脑图来构建自己的知识体系,一目了然。这里给大家分享一份大厂主流的Android架构师技术体系,可以用来搭建自己的知识框架,或者查漏补缺;

对应这份技术大纲,我也整理了一套Android高级架构师完整系列的视频教程,主要针对3-5年Android开发经验以上,需要往高级架构师层次学习提升的同学,希望能帮你突破瓶颈,跳槽进大厂;

最后我必须强调几点:

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。

你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

1.搭建知识框架可不是说你整理好要学习的知识顺序,然后看一遍理解了能复制粘贴就够了,大多都是需要你自己读懂源码和原理,能自己手写出来的。
2.学习的时候你一定要多看多练几遍,把知识才吃透,还要记笔记,这些很重要! 最后你达到什么水平取决你消化了多少知识
3.最终你的知识框架应该是一个完善的,兼顾广度和深度的技术体系。然后经过多次项目实战积累经验,你才能达到高级架构师的层次。

你只需要按照在这个大的框架去填充自己,年薪40W一定不是终点,技术无止境

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 9
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值