初始化框架Alpha源码阅读

前言

在应用性能优化中,启动优化一直是一个热门的话题,我们都知道应用的启动速度直接影响我们的用户体验。Android应用开发者一直处于既要丰富强大的功能,又要应用秒开的效果之间矛盾中,中间一定需要跟启动耗时任务作斗争。启动中的耗时主要是耗时任务,如何高效的让必须在应用启动前就就绪的任务最快速的得到执行呢。有些聪明的开发者就想到了做一个初始化框架。本文的Alpha就是这样一个开源库,出自Alibaba。下面我们就来分析一下它的原理。

在这里插入图片描述

使用

直接参考Alpha库中Sample部分代码

//这里ConfigTest就是一个Demo类无需太关心
ConfigTest test = new ConfigTest(getApplicationContext());
test.start();

ConfigTest代码

//ConfigTest.java
public void start() {
    //配置任务
    config();
    MyLog.e("==ALPHA==", "start -->" + System.currentTimeMillis());
    //启动任务
    AlphaManager.getInstance(mContext).start();
}


private void config() {
    Project.Builder builder = new Project.Builder().withTaskCreator(new MyTaskCreator());
    
    builder.add(TASK_A);
    builder.add(TASK_B).after(TASK_A);
    builder.add(TASK_C).after(TASK_A);
    builder.add(TASK_D).after(TASK_B, TASK_C);
    builder.setProjectName("innerGroup");
    ...
    AlphaManager.getInstance(mContext).addProject(builder.create());
}

原理分析

这个库的代码其实还是相对简单的,而且每个类的代码大都是400行以内,读起来也比较方便。我们结合前面的例子,大致猜测下这个开源库的原理。注意我这个猜测其实就是结论,基于已经阅读的基础上。

config()方法中的代码作用就是编排任务。既然是初始化框架核心任务就是编排任务。既然是任务,最终还是要执行,后面调用lphaManager.getInstance(mContext).start();就是启动任务。

Alpha库的核心是编排任务,让库的使用者可以自由的编排自己的启动子任务。编排好任务之后下一个要做的就是执行。既然是编排好的任务,先执行谁,后执行谁,执行完成后要做什么等等。设想下如果你是这个库的作者,让你实现。你会如何实现?用到哪些集合?使用什么设计模式?用到哪些Java的功能?多个任务如A,B,C都执行完成后才能执行D该如何实现?

也许你能比作者实现的更好,也许你不知道该出哪里下手。让我们带着问题继续看代码。

从一个Task说起

作者首先抽象出了一个Task的类,

  • 既然要编排任务首先要有任务类呀,Task就充当了这样的角色。
  • 一个任务前面可能有多个任务,后面可能也有多个任务。该用什么数据结构或者集合来实现呢?
  • 既然是任务就要有状态,该如何表示呢?
  • 既然是任务就要可以执行下,大概率会有run,Start之类的方法区启动

好,上面我说的作者都有考虑,下面看代码,注释非常全面,大部分不需要我补充

//Task.jva
public abstract class Task {

    /**
     * {@code Task}执行状态,{@code Task}尚未执行
     */
    public static final int STATE_IDLE = 0;

    /**
     * {@code Task}执行状态,{@code Task}正在执行中
     */
    public static final int STATE_RUNNING = 1;

    /**
     * {@code Task}执行状态,{@code Task}已经执行完毕
     */
    public static final int STATE_FINISHED = 2;

    /**
     * {@code Task}执行状态,{@code Task}等待执行
     */
    public static final int STATE_WAIT = 3;

    /**
     * 默认的执行优先级
     */
    public static final int DEFAULT_EXECUTE_PRIORITY = 0;

    /**
     * 执行优先级,由于线程池是有限的,对于同一时机执行的task,其执行也可能存在先后顺序。值越小,越先执行。
     */
    private int mExecutePriority = DEFAULT_EXECUTE_PRIORITY;

    /**
     * 线程优先级,优先级高,则能分配到更多的cpu时间
     */
    private int mThreadPriority;
    
    //这里赋值一个线程执行器,通过AlphaConfig.getExecutor()获取,用来执行子线程任务
    private static ExecutorService sExecutor = AlphaConfig.getExecutor();
    
    //主线程的Handler,用来执行主线程的任务
    private static Handler sHandler = new Handler(Looper.getMainLooper());
    
    //当前状态
    private volatile int mCurrentState = STATE_IDLE;
    //这里果然有后面任务的集合,用List来存放
    private List<Task> mSuccessorList = new ArrayList<Task>();
    //这里还有前面任务的集合,用Set来存放,为什么后面任务用List,前面任务用Set呢?
    protected Set<Task> mPredecessorSet = new HashSet<Task>();
    
}

下面看任务的启动

public synchronized void start() {
    //一个任务不能被启动多次
    if (mCurrentState != STATE_IDLE) {
        throw new RuntimeException("You try to run task " + mName + " twice, is there a circular dependency?");
    }
    //切换到等待状态
    switchState(STATE_WAIT);

    if (mInternalRunnable == null) {
        //新建一个Runnable
        mInternalRunnable = new Runnable() {
            @Override
            public void run() {
                //设置线程优先级
                android.os.Process.setThreadPriority(mThreadPriority);
                //记录启动时间
                long startTime = System.currentTimeMillis();
                //切换到运行态
                switchState(STATE_RUNNING);
                //执行run方法,在Task类中是一个抽象方法,由子类实现,就是子类任务要做的事情放里面
                Task.this.run();
                //run方法执行玩切换到完成状态
                switchState(STATE_FINISHED);

                long finishTime = System.currentTimeMillis();
                //记录task耗时
                recordTime((finishTime - startTime));
                //通知完成
                notifyFinished();
                //回收资源
                recycle();
            }
        };
    }

    if (mIsInUiThread) {
        //主线程通过上面的主线程Handler实现
        sHandler.post(mInternalRunnable);
    } else {
        //子线程通过线程执行器执行
        sExecutor.execute(mInternalRunnable);
    }
}

任务编排

想在再让我们代入一下作者的角度。你现在抽象出了任务,如何把多个任务组成PERT网路图呢?并且如何让任务按照PERT网路图来执行呢?

  • 你需要准备一个起始节点,和一个终止节点
  • 你需要构造出节点的先后关系,对于每一个节点的先后节点,都是一对多的关系
  • 一个任务执行结束后,需要执行它后面的任务(后面的任务如何知道自己该执行了?观察者模式?还是已经编排好了,直接放在一个线程执行?)

作者抽象出了一个Project的概念,用于承载一个PERT网路图,而一个PERT网路图里面的子节点又可以是一个PERT网路图也就是一个Project,这个我们先不分析,先主要关注Project。Project的构造是建造者模式。

Project.Builder builder = new Project.Builder().withTaskCreator(new MyTaskCreator());
    
builder.add(TASK_A);
builder.add(TASK_B).after(TASK_A);
builder.add(TASK_C).after(TASK_A);
builder.add(TASK_D).after(TASK_B, TASK_C);
builder.setProjectName("innerGroup");

先看Project.Builder的构造方法,里面有一些初始化工作

public Builder() {
    init();
}

private void init() {
    //设置mCacheTask为null
    mCacheTask = null;
    //设置mIsSetPosition标志位为true
    mIsSetPosition = true;
    //构造Project,Project继承自Task,构造方法仅仅做了命名和设置优先级
    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);
    //构造一个执行监听器
    mMonitor = new ExecuteMonitor();
    //给Project设置项目执行监听器
    mProject.setProjectExecuteMonitor(mMonitor);
}

看代码中builder.add(TASK_A);TASK_A是一个字符串,如何转化成Task的呢?

public Builder add(String taskName) {
    ...
    
    Task task = mTaskFactory.getTask(taskName);
    
    ...
}

public synchronized Task getTask(String taskName) {
    //mTasks是缓存好的,如果没有找到,继续往下走
    Task task = mTasks.get(taskName);

    if (task != null) {
        return task;
    }
    //通过mTaskCreator创建Task,mTaskCreator就是最开始withTaskCreator方法传入的
    task = mTaskCreator.createTask(taskName);
    //为空抛异常
    if (task == null) {
        throw new IllegalArgumentException("Create task fail, there is no task corresponding to the task name. Make sure you have create a task instance in TaskCreator.");
    }
    //放入缓存
    mTasks.put(taskName, task);
    return task;
}

MyTaskCreator实现ITaskCreator接口,实现createTask方法,看里面其实就是构造了这些Task,通过把构造的过程放在这里,在Project传递任务的时候更加的方便,只需一个TASK_A字符串即可。

public static class MyTaskCreator implements ITaskCreator {
    @Override
    public Task createTask(String taskName) {
        Log.d("==ALPHA==", taskName);
        switch (taskName) {
            case TASK_A:
                return new TaskA();
            case TASK_B:
                return new TaskB();
            case TASK_C:
                return new TaskC();
            case TASK_D:
                return new TaskD();
            case TASK_E:
                return new TaskE();
            case TASK_F:
                return new TaskF();
            case TASK_G:
                return new TaskG();
        }

        return null;
    }
}

继续看Project.Builder的add方法

public Builder add(String taskName) {
    if (mTaskFactory == null) {
        throw new IllegalAccessError(
            "You should set a ITaskCreator with withTaskCreator(), and then you can call add() and after() with task name.");
    }

    Task task = mTaskFactory.getTask(taskName);
    add(task);

    return Builder.this;
}

public Builder add(Task task) {
    addToRootIfNeed();
    //给mCacheTask赋值为传入的task
    mCacheTask = task;
    //设置执行监听器,init方法中有构造
    mCacheTask.setExecuteMonitor(mMonitor);
    //设置mIsSetPosition为false
    mIsSetPosition = false;
    //添加任务执行完成监听器
    mCacheTask.addOnTaskFinishListener(new InnerOnTaskFinishListener(mProject));
    //设置成功执行后的任务是mFinishTask
    mCacheTask.addSuccessor(mFinishTask);

    return Builder.this;
}

 /**
 * 这个方法的作用是在需要的时候将当前添加的任务放到mStartTask的后面
 */
private void addToRootIfNeed() {
    //mIsSetPosition前面init方法已经设置为true,这里直接返回
    if (!mIsSetPosition && mCacheTask != null) {
        mStartTask.addSuccessor(mCacheTask);
    }
}

假如我们就编排一个任务A,效果是怎么样的呢?
mStartTask->任务A->mFinishTask

总结一下:

  • 一个Project默认会有一个mStartTask和一个mFinishTask
  • 编排的时候会将添加的库在合适的时机添加到mStartTask的后面

继续看after方法

public Builder after(String taskName) {
    if (mTaskFactory == null) {
        throw new IllegalAccessError(
            "You should set a ITaskCreator with withTaskCreator(), and then you can call add() and after() with task name.");
    }
    //获取到Task
    Task task = mTaskFactory.getTask(taskName);
    after(task);

    return Builder.this;
}

public Builder after(Task task) {
    //将mCacheTask放到传入的task的后面
    task.addSuccessor(mCacheTask);
    //mFinishTask的前置任务中移除task
    mFinishTask.removePredecessor(task);
    //设置mIsSetPosition为true,已经设置好位置
    mIsSetPosition = true;
    return Builder.this;
}

看完after我们看到一些问题,
问题一:我能不能不add任何任务,直接after这个任务A呢?
问题二:我先add某个任务B,然后能不能after一个没有add的任务A呢?
看代码时候不行的,只能先add任务,然后这个任务也必须after一个已经add的任务。

至此,就完成了任务的编排。

任务执行

首先去看Project的mStartTask

public void start() {
    Project project = null;

    //暂时忽略掉多进程相关的逻辑,后面感兴趣可以自行分析
    ...

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


public void start() {
    //启动mStartTask
    mStartTask.start();
}

这里会去调用mStartTask的run方法,并且会调用Project的onProjectStart回调,记录开始时间。mStartTask也是Task,run方法执行完也会调用notifyFinished()

/**
 * 通知所有紧后{@code Task}以及{@code OnTaskFinishListener}自己执行完成。
 */
/*package*/ void notifyFinished() {
    if (!mSuccessorList.isEmpty()) {
        AlphaUtils.sort(mSuccessorList);

        for (Task task : mSuccessorList) {
            //调用所有mSuccessorList中Task的onPredecessorFinished
            task.onPredecessorFinished(this);
        }
    }

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

        mTaskFinishListeners.clear();
    }
}

synchronized void onPredecessorFinished(Task beforeTask) {
    //如果前置任务集合为空,直接返回
    if (mPredecessorSet.isEmpty()) {
        return;
    }
    //清除当前这个依赖任务
    mPredecessorSet.remove(beforeTask);
    //如果前置依赖任务不为空什么也不做,继续等待,为空,表示该自己执行了
    if (mPredecessorSet.isEmpty()) {
        start();
    }

}

最后看mFinishTask执行完,做一些打印时间等操作

private static class AnchorTask extends Task {
    private boolean mIsStartTask = true;
    ...

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

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

}

总结

看Alpha的源码让我感受到一种思想,图从某种意义上说也是链表的拓展。
单链表一个节点前面和后面只会有一个节点,图就是每一个节点的前面和后面都有可能出现多个节点,所以对于图,就不能再用preNode,nextNode.而应该用preList,nextList这样的数据结构。
看完是不是感觉自己也能写一个初始化框架了,无非就行借助已有的数据结构,设计模式编排自己的任务。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值