前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >启动器—Alpha框架解析

启动器—Alpha框架解析

作者头像
码上积木
发布于 2021-01-11 02:28:51
发布于 2021-01-11 02:28:51
1.8K00
代码可运行
举报
文章被收录于专栏:码上积木码上积木
运行总次数:0
代码可运行

前言

启动优化,其实就是优化从点击icon到主页面展示这个过程的速度,让主界面尽量快的展现在用户面前。所以我们要做的就是找到那些耗时操作,并将其优化。

怎么找到?一般分成两个场景:

1、线下(debug)场景 在应用的开发阶段,我们一般通过AOP进行函数的耗时统计,通过aspectj库可以很方便的将代码插入到函数内部,从而统计到每个方法的耗时时间。或者直接通过Android Studio 自带的Profiler CPU工具,查看每个方法的时间,CPU信息。

2、线上场景 当应用已经发布到线上,统计就变得不是那么容易了。所以我们一般就通过函数插桩的方式,自己写一个统计耗时的工具类,部署到需要统计的地方,比如Application和Activity的生命周期,数据库的初始化,第三方库的初始化,然后最后上传数据到服务器即可。

找到耗时的地方后该怎么优化解决呢?

一般就是通过分析这些要执行的任务,进行异步,懒加载,预加载等操作。

这里就涉及到我们今天要讲的内容了,异步启动方案。顾名思义就是帮我们优化启动的一个工具,可以高效合理的帮我们安排启动过程中的一些任务处理。

接下来就带大家从源码开始分析,一起看看阿里的异步启动框架——Alpha

作为一个异步启动框架,该有什么功能

有人可能要问了,不就是异步任务吗,我整几个线程,把任务往里面一丢不就行了。

事情可没那么简单,比如现在有6个任务需要在Application里面执行,其中Task1,Task4,Tas6需要在主线程执行,Task2,Task3需要在Task1执行完才能执行,Task4,Task5需要Task2和Task3执行完才能执行,Task6需要Task4和Task5执行完才能执行,Task4的耗时要大于Task5

这是个啥啊?我晕了。这么多关系,我该怎么处理?既然文字看着太麻烦,就画个图吧,这里涉及到一个用于时间管理的图形——Pert图。Pert 图是一个有向图,能清晰地描述子任务之间的依赖关系。比如我们这个项目的情况,画成Pert 图如下:

Pert图

通过Pert图还是可以很直观的看到每个Task的关系,其中当执行完Task2和Task3之后,我们有两个选择,先执行Task4或者先执行Task5,由于Task4的耗时要大于Task5,所以我们就选择先执行Task4了。

其实制定任务执行的计划在我们生活中也随处可见,比如我们早起后也有很多事情要处理,比如烧水(5分钟),刷牙(3分钟),洗脸(2分钟),上厕所(8分钟)。怎么选一条最优路线能让我们最快完成这些事情呢?肯定是能一起并行的事情就安排到一起,然后并行的同时让耗时久的事情先发生。比如先烧水,然后上厕所的同时刷牙洗脸?扯远了扯远了,哈哈哈,收!

好了,看看我们如果用Alpha框架实现该怎么写呢?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        //构造方法,true为主线程执行
        Task1=new Task("task1",true);
        Task2=new Task("task2",false);
        Task3=new Task("task3",false);
        Task4=new Task("task4",true);
        Task5=new Task("task5",false);
        Task6=new Task("task6",true);
        
        //设置优先级,耗时操作优先级较高
        Task4.setExecutePriority(1);
        Task5.setExecutePriority(2);
        
        Project.Builder builder = new Project.Builder().withTaskCreator(new MyTaskCreator());
        builder.add(Task1);
        builder.add(Task2).after(Task1);
        builder.add(Task3).after(Task1);
        builder.add(Task4).after(Task2,Task3);
        builder.add(Task5).after(Task2,Task3);
        builder.add(Task6).after(Task4,Task5);
        builder.setProjectName("innerGroup");
        
        AlphaManager.getInstance(mContext).addProject(builder.create());
        
        AlphaManager.getInstance(mContext).start();

搞定!还不错吧。那就来一起分析下它吧!

首先,我们自己如果好好想想,如果让我们来做一个异步启动框架,需要考虑哪些问题?

  • 多线程管理
  • 任务的优先级
  • 任务之间的先后关系
  • 任务是否需要在主线程执行
  • 多进程处理

就让我们带着这些问题去看看Alpha的内部源码。

Alpha源码解析

从上面的代码可以看到,任务的开启是由AlphaManager.getInstance(mContext).start()方法开始调用,所以我们就从这个start方法开始研究:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public void start() {
        Project project = null;

        do {
            //1.是否有为当前进程单独配置的Project,此为最高优先级
            if (mProjectForCurrentProcess != null) {
                project = (Project) mProjectForCurrentProcess;
                break;
            }

            //2.如果当前是主进程,是否有配置主进程Project
            if (AlphaUtils.isInMainProcess(mContext)
                    && mProjectArray.indexOfKey(MAIN_PROCESS_MODE) >= 0) {
                project = (Project) mProjectArray.get(MAIN_PROCESS_MODE);
                break;
            }

            //3.如果是非主进程,是否有配置非主进程的Project
            if (!AlphaUtils.isInMainProcess(mContext)
                    && mProjectArray.indexOfKey(SECONDARY_PROCESS_MODE) >= 0) {
                project = (Project) mProjectArray.get(SECONDARY_PROCESS_MODE);
                break;
            }

            //4.是否有配置适用所有进程的Project
            if (mProjectArray.indexOfKey(ALL_PROCESS_MODE) >= 0) {
                project = (Project) mProjectArray.get(ALL_PROCESS_MODE);
                break;
            }
        } while (false);

        if (project != null) {
            addListeners(project);
            project.start();
        } else {
            AlphaLog.e(AlphaLog.GLOBAL_TAG, "No startup project for current process.");
        }
    }

哇,一开始就把我们多进程的疑惑给解决了。start方法首先就判断了当前的进程以及是否能匹配到相关进程的任务。可以看到一共有三种进程配置变量:

  • MAIN_PROCESS_MODE :主进程任务
  • SECONDARY_PROCESS_MODE :非主进程任务
  • ALL_PROCESS_MODE:适用于所有进程的任务

那么在哪里配置这些进程选项呢?addProject方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public void addProject(Task project, int mode) {
        if (project == null) {
            throw new IllegalArgumentException("project is null");
        }

        if (mode < MAIN_PROCESS_MODE || mode > ALL_PROCESS_MODE) {
            throw new IllegalArgumentException("No such mode: " + mode);
        }

        if (AlphaUtils.isMatchMode(mContext, mode)) {
            mProjectArray.put(mode, project);
        }
    }

ok,够简单吧。继续往下看start方法。跳转到project的start方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void start() {
        mStartTask.start();
    }

这么简单吗,就开启了一个mStartTask?这个mStartTask是之前设置的那些任务中第一个任务吗?接着看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        //Project.java
        private void init() {
        ...
            mProject = new Project();
            mFinishTask = new AnchorTask(false, "==AlphaDefaultFinishTask==");
            mFinishTask.setProjectLifecycleCallbacks(mProject);
            mStartTask = new AnchorTask(true, "==AlphaDefaultStartTask==");
            mStartTask.setProjectLifecycleCallbacks(mProject);
            mProject.setStartTask(mStartTask);
            mProject.setFinishTask(mFinishTask);
       ...
        }
        
        
    private static class AnchorTask extends Task {
        private boolean mIsStartTask = true;
        private OnProjectExecuteListener mExecuteListener;

        public AnchorTask(boolean isStartTask, String name) {
            super(name);
            mIsStartTask = isStartTask;
        }

        public void setProjectLifecycleCallbacks(OnProjectExecuteListener callbacks) {
            mExecuteListener = callbacks;
        }

        @Override
        public void run() {
            if (mExecuteListener != null) {

                if (mIsStartTask) {
                    mExecuteListener.onProjectStart();
                } else {
                    mExecuteListener.onProjectFinish();
                }
            }
        }

    }        

可以看到,在Project类的初始化方法中,定义了一个开始任务和一个结束任务。这是因为从执行角度看,一个任务序列必须有一个开始节点和一个结束节点。但是实际情况中,可能会有多个任务可以同时开始,而且有多个任务可以同时作为结束点。所以就设置了这两个节点方便控制整个流程,标记流程的开始和结束,也方便了任务的监听

说回上面,开始任务的start方法走到哪里去了呢?自然是AnchorTask的父类Task,看看源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public synchronized void start() {
        ...
        switchState(STATE_WAIT);

        if (mInternalRunnable == null) {
            mInternalRunnable = new Runnable() {
                @Override
                public void run() {
                    android.os.Process.setThreadPriority(mThreadPriority);
                    long startTime = System.currentTimeMillis();

                    switchState(STATE_RUNNING);
                    Task.this.run();
                    switchState(STATE_FINISHED);

                    long finishTime = System.currentTimeMillis();
                    recordTime((finishTime - startTime));

                    notifyFinished();
                    recycle();
                }
            };
        }

        if (mIsInUiThread) {
            sHandler.post(mInternalRunnable);
        } else {
            sExecutor.execute(mInternalRunnable);
        }
    }

源码还是挺简单的哈,定义了一个Runnable,然后判断是否主线程,并执行这个Runnable。其中还穿插了一些状态的改变,在Runnable内部主要是执行了Task.this.run(),也就是执行了任务本身。其中setThreadPriority方法主要是设置了线程的优先级,比如THREAD_PRIORITY_DEFAULT等,这里的优先级是较线程而言的,主要是针对CPU资源的竞争,跟我们需要的Task之间的优先级关系不大。如果是需要在主线程执行的任务,就会通过Handler(sHandler)将事件传递给主线程执行。如果是需要在非主线程执行的任务,就会通过线程池(sExecutor)去执行线程任务。

诶,好像没了?开始任务执行了就没了吗?再回头看看,还有一个notifyFinished方法。按这个名字应该就是通知任务结束的一个方法,看看源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    void notifyFinished() {
        if (!mSuccessorList.isEmpty()) {
            AlphaUtils.sort(mSuccessorList);

            for (Task task : mSuccessorList) {
                task.onPredecessorFinished(this);
            }
        }

        if (!mTaskFinishListeners.isEmpty()) {
            for (OnTaskFinishListener listener : mTaskFinishListeners) {
                listener.onTaskFinish(mName);
            }

            mTaskFinishListeners.clear();
        }
    }

这个方法主要做了三件事:

  • mSuccessorList 排序
  • 遍历mSuccessorList列表,执行onPredecessorFinished方法
  • 监听回调onTaskFinish方法

mSuccessorList是什么呢?我们叫它紧后任务列表,也就是接下来要执行的任务列表。所以流程就是先把当前任务之后的任务列表进行一个排序,根据优先级排序。然后按顺序执行onPredecessorFinished方法。

如果紧后任务列表为空,也就代表没有后续任务了,那么就会走onTaskFinish回调方法,告知当前Project已经执行完毕。

接下来就看看紧后任务是怎么加进来的呢?又该怎么排序?onPredecessorFinished方法又执行了些什么东西?

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    //1、紧后任务添加
    public Builder after(Task task) {
        task.addSuccessor(mCacheTask);
        mFinishTask.removePredecessor(task);
        mIsSetPosition = true;
        return Builder.this;
    }
        
    void addSuccessor(Task task) {
        task.addPredecessor(this);
        mSuccessorList.add(task);
    }
      
    //2、紧后任务列表排序 
    public static void sort(List<Task> tasks) {
        if (tasks.size() <= 1) {
            return;
        }
        Collections.sort(tasks, sTaskComparator);
    }    
    
    private static Comparator<Task> sTaskComparator = new Comparator<Task>() {
        @Override
        public int compare(Task lhs, Task rhs) {
            return lhs.getExecutePriority() - rhs.getExecutePriority();
        }
    };    
    
    //3、紧后任务执行
    synchronized void onPredecessorFinished(Task beforeTask) {

        if (mPredecessorSet.isEmpty()) {
            return;
        }

        mPredecessorSet.remove(beforeTask);
        if (mPredecessorSet.isEmpty()) {
            start();
        }

    }    
      

ok,源码写的很清楚了,这里逐步分析下:

由源码得知,紧后任务列表主要是通过after方法,还记得之前配置任务的时候吗?builder.add(Task2).after(Task1),所以这个after就代表Task2要在Task1后面执行,也就是Task2成了Task1的紧后任务。同理,Task1也就成了Task2的紧前任务。也就是代码中的addPredecessor方法,在添加紧后任务的同时也添加了紧前任务。

可能有人会问了,紧前任务添加了有什么用呢?难不成还倒退回去执行?试想一下,如果有多个任务的紧后任务都是一个呢?比如这种情况:builder.add(Task4).after(Task2,Task3)。Task4是Task2和Task3的紧后任务,所以在Task2执行完之后,还要判断Task3是否执行成功,然后才能执行Task4,这就是紧前任务列表的作用。这也就对应到上述代码中onPredecessorFinished方法的逻辑了。

然后这个紧后任务列表的排序是怎么排的呢?其实就是通过getExecutePriority方法获取task的执行优先级数字,按照正序排列,越小的任务执行时机越早。还记得之前配置的时候我设置了setExecutePriority方法吗,就是这里设置了优先级的。

至此主要逻辑就差不多了。好像还挺简单的是不是。还有一些细节我也简单的提下:

  • 各种回调:包括一些task的回调,project的回调。
  • 日志记录:比如耗时时间的记录,刚才执行任务时候的recordTime方法,就是记录了每个task的耗时。
  • 多种Task配置方法:除了上面用Java代码配置,还可以通过xml文件来配置Project和里面的Task,这个就主要是XmlPullParser类来解析xml数据,然后生成Prject。
  • 各种设计模式:比如构建Project的建造者模式,还有通过传入task名称就可以创建Task的工厂模式。

诸如此类的一些细节感兴趣朋友的可以自己下源码看看。

最后用一张流程图总结下吧:

总结

分析下来,这个异步启动框架应该算比较简单的,但是能解决问题啊!其实我们平时工作中也可以做一些积累,然后写成工具或者框架,如果能开源出来大家一起使用还真是一件不错的事情呢!


本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码上积木 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Python中文分词库——jieba的用法
jieba是优秀的中文分词第三方库。由于中文文本之间每个汉字都是连续书写的,我们需要通过特定的手段来获得其中的每个单词,这种手段就叫分词。而jieba是Python计算生态中非常优秀的中文分词第三方库,需要通过安装来使用它。
Python学习者
2023/05/07
5390
Python帮你分析孙猴子在大闹天宫时出现了几回?
由于该库是第三方库,并不是Python自带的模块,因此需要通过pip命令进行安装,pip安装命令如下:
灰小猿
2022/05/05
3550
Python帮你分析孙猴子在大闹天宫时出现了几回?
中文分词库 jieba
使用 python 的 jieba库可以将中文句子分割成一个一个词语, 在机器学习中,可用于生成中文的词向量。我们可以使用 pip 免费安装 jieba 库。
用户6021899
2019/08/14
1.9K0
【Elasticsearch系列十八】Ik 分词器
官网:https://github.com/medcl/elasticsearch-analysis-ik
kwan的解忧杂货铺
2024/09/22
2670
jieba库详解「建议收藏」
点击windows+r,进入命令提示符输入cmd,进入界面后,输入pip install jieba。即可安装,示例如下:
全栈程序员站长
2022/11/08
1.1K0
jieba库详解「建议收藏」
jieba库分词代码_怎么下载jieba库
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
全栈程序员站长
2022/11/08
5860
jieba库分词代码_怎么下载jieba库
7个优秀的开源中文分词库推荐,实用性强!
纵观整个开源领域,陆陆续续做中文分词的也有不少,不过目前仍在维护的且质量较高的并不多。下面整理了一些个人认为比较优秀的中文分词库,以供大家参考使用。
一墨编程学习
2018/12/17
13.3K0
中文分词器 jcseg 和 IK Analyzer
在 lucene 的开发过程中,常常会遇到分词时中文识别的问题,lucene提供了 lucene-analyzers-common-5.0.0.jar包来支持分词,但多的是对英语,法语,意大利语等语言的支持,对中文的支持不太友好,因此需要引入中文分词器。
BUG弄潮儿
2022/03/08
1.2K0
python之第三方库安装及使用(jieba库)
1.jieba库的安装及使用 1.1安装 pip install jieba -i https://pypi.douban.com/simple/ 1.2功能 主要实现中文分词功能。 1.3分词原理 ​ 1.4三种模式及主要函数 ​ jieba.lcut(s)函数---对s使用精确模式进行分词 jieba.lcut(s,cut_all=True)函数---对s使用全模式进行分词 jieba.lcut_for_search(s)函数---对s使用搜索引擎模式进行分词 jieba.add.word(w)
用户4908836
2020/04/14
1.3K0
python jieba分词库使用
“Jieba” (Chinese for “to stutter”) Chinese text segmentation: built to be the best Python Chinese word segmentation module.
李小白是一只喵
2020/04/23
1.1K0
python jieba分词库使用
Python系列~字段类型以及jieba库的使用
真诚是为人处世的基础。无论表达关切的一方,还是被关注的一方,只有你情我愿,才能互惠互利。
小Bob来啦
2021/01/11
9420
Python系列~字段类型以及jieba库的使用
【问底】严澜:数据挖掘入门——分词
谷歌4亿英镑收购人工智能公司DeepMind,百度目前正推进“百度大脑”项目,腾讯、阿里等各大巨头也在积极布局深度学习。随着社会化数据大量产生,硬件速度上升、成本降低,大数据技术的落地实现,让冷冰冰的数据具有智慧逐渐成为新的热点。要从数据中发现有用的信息就要用到数据挖掘技术,不过买来的数据挖掘书籍一打开全是大量的数学公式,而课本知识早已还给老师了,着实难以下手、非常头大! 我们不妨先跳过数学公式,看看我们了解数据挖掘的目的——发现数据中价值。这个才是关键,如何发现数据中的价值。那什么是数据呢?比如大家要上网
CSDN技术头条
2018/02/08
8540
【问底】严澜:数据挖掘入门——分词
如何开发自己的搜索帝国之安装ik分词器
   Elasticsearch默认提供的分词器,会把每个汉字分开,而不是我们想要的根据关键词来分词,我是中国人 不能简单的分成一个个字,我们更希望 “中国人”,“中国”,“我”这样的分词,这样我们就需要安装中文分词插件,ik就是实现这个功能的。   elasticsearch-analysis-ik 是一款中文的分词插件,支持自定义词库。   现在开始安装ik分词器,安装之前,先说明一些变化: 之前可以在node节点上配置index默认的分词器,如果是多节点,那么在每个节点上都配置就行了。这个有点不灵活,
欢醉
2018/01/22
1.4K0
如何开发自己的搜索帝国之安装ik分词器
ES[7.6.x]学习笔记(七)IK中文分词器
在上一节中,我们给大家介绍了ES的分析器,我相信大家对ES的全文搜索已经有了深刻的印象。分析器包含3个部分:字符过滤器、分词器、分词过滤器。在上一节的例子,大家发现了,都是英文的例子,是吧?因为ES是外国人写的嘛,中国如果要在这方面赶上来,还是需要屏幕前的小伙伴们的~
小忽悠
2020/05/07
1.3K0
ES[7.6.x]学习笔记(七)IK中文分词器
数据挖掘基础:分词入门
点击标题下「大数据文摘」可快捷关注 摘自:lanceyan.com 谷歌4亿英镑收购人工智能公司DeepMind,百度目前正推进“百度大脑”项目,腾讯、阿里等各大巨头布局深度学习。随着社会化数据大量产生,硬件速度上升、成本降低,大数据技术的落地实现,让冷冰冰的数据具有智慧逐渐成为新的热点。要从数据中发现有用的信息就要用到数据挖掘技术,不过买来的数据挖掘书籍一打开全是大量的数学公式,而课本知识早已还给老师了,难以下手、非常头大! 我们可以跳过数学公式,先看看我们了解数据挖掘的目的:发现数据中价值。这个才是关键
大数据文摘
2018/05/22
6270
python jieba库_Python jieba库的使用说明「建议收藏」
txt= open(“D:\\三国演义.txt”, “r”, encoding=’utf-8′).read()
全栈程序员站长
2022/08/31
3.1K0
python jieba库_Python jieba库的使用说明「建议收藏」
使用 trie 树实现简单的中文分词
导语:工作中偶尔遇到需要对中文进行分词的情况,不要求非常高的精确度和语境符合度,仅是为了统计某些词出现的热度。本文提供了一种简单易行的中文分词方法。 工作中,偶尔会遇到需要进行中文分词统计的情况,但是并不需要做到高精度时,我们可以使用 trie 树,也就是 前缀树 来实现这个功能。 trie 树,可以叫前缀树,有时也称字典树,是字符串算法中比较常用的一种结构。关于 trie 树的概念及其扩展的其他更高效的数据结构,自行百度,这里不再占篇幅。 如果使用 trie 树来实现英文单词的查找,那么最终形成的结构,如
胖兔子兔胖
2018/01/15
3.2K0
使用 trie 树实现简单的中文分词
【迅搜16】SCWS分词(一)概念、词性、复合分词等级
正式进入到分词部分的学习了,这也是我们搜索引擎学习的最后一个部分了。在这里,我们还是以 XS 默认的 SCWS 分词器为基础进行学习,但是,就像之前的其它内容一样,原理和概念部分的内容很多都是相通的。即使你将来要用 Jieba 分词或者 IK 分词,它们所有的原理和 SCWS 都是大差不差的。
硬核项目经理
2024/01/09
5620
【迅搜16】SCWS分词(一)概念、词性、复合分词等级
Python中的jieba库
人们把词语组合成句子来表达意义,对于一句中文,人可以借助知识明白哪些是词,进而理解语句的含义,而计算机很难做到。确定句子中的词,是计算机理解中文的基础。jieba库是一款优秀的Python第三方中文分词库。
楚客追梦
2022/11/11
1.1K0
Python中的jieba库
Python3的简单语法与常用库(慢慢更新中)
之前学习Python的时候,主要是在网上简单看了些文档,并没有系统的去学习过,前些天抽空在中国大学MOOC上学习了由北京理工大学嵩天老师讲授的免费公开课--Python语言程序设计。这个课程讲的比较基础,但讲的确实不错。
用户7886150
2020/12/31
7090
相关推荐
Python中文分词库——jieba的用法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档