Android 性能优化之启动优化解析

应用启动分类:

  1. 冷启动:耗时最多、衡量标准。

    ClickEvent->IPC->Process.start->ActivityThread->bindApplication->LifeCycle->ViewRootImpl

  2. 热启动:最快,后台->前台

  3. 温启动:较快

冷启动的相关任务

  • 冷启动之前:

    启动App -> 加载空白Window-> 创建进程

  • 随后任务:

    创建Application -> 启动主线程 -> 创建入口Acitivity -> 加载布局 -> 布置屏幕 -> 首帧绘制

优化方向:

Application和Activity生命周期

1.1 启动时间测量方式

1.1.1 adb命令

adb shell am start -W packagename/首屏Activity

Android 性能优化之启动优化解析_第1张图片

Status:ok(启动无异常) LaunchState:COLD(冷启动) Activity:.SplashActivity(目标Activity)

TotalTime:所有Activity启动耗时 ThisTime:最后一个Activity启动耗时 WaitTime:AMS启动Activity的总耗时

当前为application->SplashActivity,中间无中转Activity,因此TotalTime与ThisTime一致。

线下使用,不能带到线上 非严谨、精确时间

1.1.2 手动打点

启动时埋点,启动后结束埋点,二者差值即为启动时间。

从Application的attachBaseContext()为起点。

误区:以onWindowsFocusChanged为首帧时间,其实这个回调只是Activity的首帧时间,实际上并不代表已绘制至屏幕中。

正解:真实数据展示,Feed的第一条展示的时候作为结束时间。

public class StarTimeUtil {
    private static final String TAG = "StartUtil";
    static long startTime;
    static long time;

    public static void startRecord() {
        startTime = System.currentTimeMillis();
    }

    public static void endRecord() {
        time = System.currentTimeMillis() - startTime;
        Log.d(TAG, "耗时:" + time + "ms");
    }
}

@Override
protected void attachBaseContext(Context base) {
    StarTimeUtil.startRecord();
    MultiDex.install(this);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    setContentView(R.layout.activity_splash);
    findViewById(R.id.fl).getViewTreeObserver().addOnDrawListener(onDrawListener);
}

ViewTreeObserver.OnDrawListener onDrawListener =  new ViewTreeObserver.OnDrawListener() {
    @Override
    public void onDraw() {
        StarTimeUtil.endRecord();
        findViewById(R.id.fl).post(new Runnable() {
            @Override
            public void run() {
                findViewById(R.id.fl).getViewTreeObserver().removeOnDrawListener(onDrawListener);
            }
        });
    }
};

Android 性能优化之启动优化解析_第2张图片

精确,可带到线上,推荐使用。 注意避开误区,采用Feed第一条展示。 addOnDrawListener 要求 API16,版本低于16可选择使用addOnPreDrawListener。

1.2 启动优化工具选择

1.2.1 traceView

图形化展示执行时间、调用栈等,信息全面,包含所有线程。

缺点:运行时开销严重,程序整体变慢,可能会带偏优化方向。

使用方式:

Debug.startMethodTracing("");

Debug.stoptMethodTracing();

生成文件在sd卡:Android/data/packagename/files

Android 性能优化之启动优化解析_第3张图片

Android 性能优化之启动优化解析_第4张图片

WallClockTime:代码执行的所需时间,包括阻塞耗时如等待锁时

ThreadTime:CPU执行时间,CPU确实在该线程工作的时间

CallChart: 由上至下即是调用者到被调用者,橙色为系统api的调用,绿色为应用自身api的调用,蓝色为第三方api的调用

FlameChart: 收集重复调用的函数之类的

TopDown: 很清晰的函数调用列表,可右键跳转到详细代码中

Total:函数执行总时间

self:函数自身代码执行时间

Children:函数内部所调用函数的执行时间

Total=self+Children

BottomUp: 也是函数调用列表,只不过与TopDown的展示相反

TraceView是Android平台一个很好的性能分析的工具,能够以图形的形式显示跟踪日志,但是已弃用。另外TraceView的性能消耗太大,得到的结果不真实。

1.2.2 systrace

结合Android内核的数据,生成Html报告。

需要API18以上,推荐使用TraceCompat向下兼容。

轻量级,开销小,直观反映CPU利用率。

使用方式:

python环境

python systrace.py -t 10 [other-options] [categories]

python …/Library/Android/sdk/platform-tools/systrace/systrace.py -t 5 -a com.xxx.xxx.xxx -o performance.html sched gfx view wm am app

Android 性能优化之启动优化解析_第5张图片

Android 性能优化之启动优化解析_第6张图片

1.2.3 Systrace + 函数插桩

Systrace 允许你收集和检查设备上运行的所有进程的计时信息。它包括AndroidKernel的一些数据(例如CPU调度程序,IO和App Thread),并且会生成HTML报告,方便用户查看分析trace内容。但是不支持应用程序代码的耗时分析,如果需要分析程序代码的执行时间,那就要结合函数插桩的方式,对细节进行分析。

1.2.4 CPU Profiler

代替Traceview的,便是CPU Profiler。

它可以检查通过使用Debug类对应用进行插桩检测而捕获的.trace 文件、记录新方法跟踪信息、保存.trace 文件以及检查应用进程的实时CPU使用情况。

具体使用方式,与Traceview大同小异。

1.3 优雅获取方法耗时

1.3.1 常规方式(手动埋点)

long time = System.currentTimeMills();

long cost =System.currentTimeMills()-time;

或者

// cpu真正所耗时间
SystemClock.currentThreadTimeMills();

long time = System.currentTimeMillis();
initThirdPart();
long cost = System.currentTimeMillis() - time;

time = System.currentTimeMillis();
initThirdPart();
cost = System.currentTimeMillis() - time;

time = System.currentTimeMillis();
initThirdPart();
cost = System.currentTimeMillis() - time;


这种常规获取方法耗时的方式就很不合理,侵入性强、工作量大。

1.3.2 AOP方式

Aspect Oriented Programing,面向切面编程。

应用场景:针对同一类问题的统一处理时。

优点:无侵入添加代码,修改简单。

使用AspectJ辅助实现

1.3.2.1 JoinPoints

程序运行时的执行点,可以作为切面的地方:

  • 函数调用、执行,获取
  • 设置变量
  • 类初始化
1.3.2.2 PointCut

带条件的JoinPoints

1.3.2.3 Advice

一种Hook,要插入代码的位置。

  • Before:PointCut之前执行

  • After:PointCut之后执行

  • Around:PointCut之前、之后分别执行。

  • execution:处理JoinPoint的类型:call、execution

onActivityCalled:要插入的代码。

@Aspect
public class PerformanceAop {
    @Around("call(* debug.DemoApplication.**(..))")
    public void getTime(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        String name = signature.toShortString();
        long time = System.currentTimeMillis();
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

//        LogUtils.i(name + " cost " + (System.currentTimeMillis() - time));
    }

    @Around("execution(* android.app.Activity.setContentView(..))")
    public void getSetContentViewTime(ProceedingJoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        String name = signature.toShortString();
        long time = System.currentTimeMillis();
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        LogUtils.i(name + " cost " + (System.currentTimeMillis() - time));
    }
}

1.3 异步优化

核心思想:子线程分担主线程任务,并行减少时间.

需要注意:不符合异步优化的,需要在某阶段完成的,要区分CPU密集型和IO密集型任务。

常规初始化流程

public class DemoApplication extends BaseApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        initThirdPart();
        initBugly();
    }

    private void initBugly() {

    }

    /**
     * 模拟为在Application中的初始化操作的耗时行为
     */
    private void initThirdPart() {

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            ToastUtils.showShortToast("第三方框架初始化耗时异常");
        }
    }
}

常规异步优化

public class DemoApplication extends BaseApplication {

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private CountDownLatch mCountDownLatch = new CountDownLatch(1);

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

//      关于异步优化,一个初始化对应一条线程效果更好,而有的初始化并不适合在子线程中进行,
//      比如Handler,当然也可以改造成可以在子线程进行初始化,但是有的只能在主线程中初始化
//      异步优化无法保证初始化的完成时机,若依然还是需要在子线程进行初始化,可以借助CountDownLatch完成

        ExecutorService executorService = Executors.newFixedThreadPool(CORE_POOL_SIZE);

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                initThirdPart();
            }

        });

        executorService.submit(new Runnable() {

            @Override
            public void run() {
                initBugly();
                mCountDownLatch.countDown();//初始化完成

            }
        });
        mCountDownLatch.await();//等待条件满足

    }

    private void initBugly() {

    }

    /**
     * 模拟为在Application中的初始化操作的耗时行为
     */
    private void initThirdPart() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
            ToastUtils.showShortToast("第三方框架初始化耗时异常");
        }
    }
}

1.4 异步优化最优解

常规异步的缺点:代码不优雅,场景不好处理(有的初始化任务会存在依赖关系),维护成本高

启动器优化

核心思想:充分利用CPU多核,自动梳理任务顺序

启动器流程:

  • 代码Task化,启动逻辑抽象为Task;
  • 根据所有任务依赖关系排序生成一个有向无环图;
  • 多线程按照排序后的优先级依次执行

Android 性能优化之启动优化解析_第7张图片

主线程:相当于常规初始化

并发:相当于异步初始化

head task:主体task执行前的操作

tail task:主体task执行后的操作

ilde task:空闲时执行的操作

这三个task起到锦上添花的作用,可按需使用

        // 使用启动器的方式进行初始化优化
        TaskDispatcher.init(this);
        TaskDispatcher taskDispatcher = TaskDispatcher.createInstance();
        taskDispatcher.addTask(new InitBugly()).addTask(new InitThirdPartTask()).start();
        // 有的task需要等待完成
        taskDispatcher.await();

1.5 更优秀的延迟初始化

public interface OnFeedShowCallBack {
    void onFeedShow();
}

public class PerformanceOptimizationActivity extends BaseActivity implements OnFeedShowCallBack {
    ...
    ...
    ...

    @Override
    public void onFeedShow() {
        ...
        ...
        ...
        
        // 一系列操作 耗时十秒
        new DispatchRunnable(new DelayInitTaskA()).run();
        new DispatchRunnable(new DelayInitTaskB()).run();
    }

}

public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
    ...
    ...
    ...

    if (position == 0 && !mHasRecorded) {
        mHasRecorded = true;
        holder.layout.getViewTreeObserver()
                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        holder.layout.getViewTreeObserver().removeOnPreDrawListener(this);
                        LogUtils.i("FeedShow");
                        LaunchTimer.endRecord("FeedShow");
                        if (mCallBack != null) {
                            mCallBack.onFeedShow();
                        }
                        return true;
                    }
                });
    }
}

针对以上场景时:

  1. 常规方案:

    new Handler().postDelayed,Feed展示后调用

    时机不便控制导致Feed卡顿

  2. 更优方案

    核心思想:对延迟任务进行分批初始化

    利用IdleHandler特性,空闲执行

    执行时机明确,缓解Feed卡顿

    public class PerformanceOptimizationActivity extends BaseActivity implements OnFeedShowCallBack {
        
        ...
        ...
        ...
    
        @Override
        public void onFeedShow() {
            DelayInitDispatcher delayInitDispatcher = new DelayInitDispatcher();
            delayInitDispatcher.addTask(new DelayInitTaskA()).addTask(new DelayInitTaskB()).start();
        }
    
    }
    
    

1.6 全民视觉优化

在用户点击手机桌面APP的时候,看到的黑屏或者白屏其实是界面渲染前的第一帧,解决这个问题非常轻松,无非就是将Theme里的windowBackground设置成我们想要让用户看到的画面就可以了,这里有2种做法:

1.将背景图设置成APP的Logo图,作为APP启动的引导,现在市面上大部分的APP也是这么做的。

<style name="AppWelcome" parent="AppTheme">
<item name="android:windowBackground">@mipmap/bg_welcome_start</item>
</style>

2.将背景颜色设置为透明色,这样当用户点击桌面APP图片的时候,并不会"立即"进入APP,而且在桌面上停留一会,其实这时候APP已经是启动的了,只是我们心机的把Theme里的windowBackground的颜色设置成透明的,强行把锅甩给了手机应用厂商。

<style name="Appwelcome" parent="android:Theme.Translucent.NoTitleBar.Fullscreen"/>

透明化这种做法需要注意的一点,如果直接把Theme引入Activity,在运行的时候可能会出现如下异常:

java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

这个是因为使用了不兼容的Theme,例如我这里的Activity继承了AppCompatActivity,解决方案很简单: 1、让其Activity集成Activity而不要集成兼容性的AppCompatActivity 2、在onCreate()方法里的super.onCreate(savedInstanceState)之前设置我们原来APP的Theme。

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
}

1.7 启动优化的其它方案

优化总方针:异步、延迟、懒加载,技术、业务相结合。

收敛启动代码的的修改权限:结合CI,修改启动代码需要Review或通知,防止维护好的启动启动代码被破坏。

  1. 提前加载SharedPreferences

    SharedPreferences是IO操作,可在Multidex之前加载,利用此阶段的CPU,此时的CPU是肯定尚未跑满的。SharedPreferences是系统类,在这之前提前加载初始化SharedPreferences不会有异常,是少有的可以在Multidex之前加载优化的类。

    覆写getApplicationContext(),返回this。

  2. 启动阶段不启动子进程

    子进程会共享CPU资源。

    注意启动顺序:App onCreate之前是ContentProvider。

  3. 类加载优化:提前异步类加载

    Class.forName()只加载类本身及其静态变量引用类。

    new 类实例,可以额外加载类成员变量的引用类。

  4. 启动阶段抑制GC

  5. CPU锁频

2. 造火箭攻略

2.1 自问自答

你的启动优化是怎么做的?

是启动优化的负责人

分析现状、确认问题

比如启动速度的监听、竞品分析

针对性优化:思路从小到大

长期保持优化效果:启动器代码的封装;收敛启动代码的修改权限

是怎么异步的,异步遇到问题没有?

体现演进过程:线程池->启动器

详细介绍启动器

你做了启动优化,觉得有哪些容易忽略的注意点?

cpu time与wall time的区别,cpu time是优化方向

注意延迟初始化的优化:不用常规方式,用idleHandler

介绍下黑科技:类加载,抑制GC,CPU锁频

版本迭代导致的启动变慢,有好的解决方式吗?

启动器

结合CI

新加代码后,上线前就先及时测量启动速度,监控完善

2.2 Baseline Profiles

3. 拧螺丝攻略

3.1 启动优化预备知识

3.1.1 Wall Duration 与 CPU Duration

Android 性能优化之启动优化解析_第8张图片

Wall Duration:代码执行时间 CPU Duration:代码消耗CUP的时间(重点指标,优化方向)

比如锁的冲突:线程执行的方法本身也许并不耗时,但是由于要等待锁的释放,而这便会加长代码的执行时间,等待锁处于阻塞的过程,CPU并不会在在该线程的方法上消耗时间。

3.1.2 adb命令

获取当前APP的包名类名: adb shell "dumpsys window | grep mCurrentFocus

adb启动指定activity: adb shell am start -W [包名]/[类名]

adb shell am start -S -R 5-W [包名]/[类名]

-S:表示每次启动前先强行停止

-R:表示重复测试次数

adb查看相关进程信息(模糊匹配): adb shell "ps|grep [模糊匹配进程名]"

adb杀死进程: adb shell kill [PID]/adb shell am force-stop [包名]

3.2 启动器源码

3.2.1 排序算法

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc 有向无环图的拓扑排序算法
 */
public class Graph {
    //顶点数
    private int mVerticeCount;
    //邻接表
    private List<Integer>[] mAdj;

    public Graph(int verticeCount) {
        this.mVerticeCount = verticeCount;
        mAdj = new ArrayList[mVerticeCount];
        for (int i = 0; i < mVerticeCount; i++) {
            mAdj[i] = new ArrayList<Integer>();
        }
    }

    /**
     * 添加边
     *
     * @param u from
     * @param v to
     */
    public void addEdge(int u, int v) {
        mAdj[u].add(v);
    }

    /**
     * 拓扑排序
     */
    public Vector<Integer> topologicalSort() {
        int indegree[] = new int[mVerticeCount];
        for (int i = 0; i < mVerticeCount; i++) {//初始化所有点的入度数量
            ArrayList<Integer> temp = (ArrayList<Integer>) mAdj[i];
            for (int node : temp) {
                indegree[node]++;
            }
        }
        Queue<Integer> queue = new LinkedList<Integer>();
        for (int i = 0; i < mVerticeCount; i++) {//找出所有入度为0的点
            if (indegree[i] == 0) {
                queue.add(i);
            }
        }
        int cnt = 0;
        Vector<Integer> topOrder = new Vector<Integer>();
        while (!queue.isEmpty()) {
            int u = queue.poll();
            topOrder.add(u);
            for (int node : mAdj[u]) {//找到该点(入度为0)的所有邻接点
                if (--indegree[node] == 0) {//把这个点的入度减一,如果入度变成了0,那么添加到入度0的队列里
                    queue.add(node);
                }
            }
            cnt++;
        }
        if (cnt != mVerticeCount) {//检查是否有环,理论上拿出来的点的次数和点的数量应该一致,如果不一致,说明有环
            throw new IllegalStateException("Exists a cycle in the graph");
        }
        return topOrder;
    }
}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc 有向无环图的拓扑排序算法
 */
public class TaskSortUtil {

    private static List<Task> sNewTasksHigh = new ArrayList<>();// 高优先级的Task

    /**
     * 任务的有向无环图的拓扑排序
     *
     * @return
     */
    public static synchronized List<Task> getSortResult(List<Task> originTasks,
                                                        List<Class<? extends Task>> clsLaunchTasks) {
        long makeTime = System.currentTimeMillis();

        Set<Integer> dependSet = new ArraySet<>();
        Graph graph = new Graph(originTasks.size());
        for (int i = 0; i < originTasks.size(); i++) {
            Task task = originTasks.get(i);
            if (task.isSend() || task.dependsOn() == null || task.dependsOn().size() == 0) {
                continue;
            }
            for (Class cls : task.dependsOn()) {
                int indexOfDepend = getIndexOfTask(originTasks, clsLaunchTasks, cls);
                if (indexOfDepend < 0) {
                    throw new IllegalStateException(task.getClass().getSimpleName() +
                            " depends on " + cls.getSimpleName() + " can not be found in task list ");
                }
                dependSet.add(indexOfDepend);
                graph.addEdge(indexOfDepend, i);
            }
        }
        List<Integer> indexList = graph.topologicalSort();
        List<Task> newTasksAll = getResultTasks(originTasks, dependSet, indexList);

        DispatcherLog.i("task analyse cost makeTime " + (System.currentTimeMillis() - makeTime));
        printAllTaskName(newTasksAll);
        return newTasksAll;
    }

    @NonNull
    private static List<Task> getResultTasks(List<Task> originTasks,
                                             Set<Integer> dependSet, List<Integer> indexList) {
        List<Task> newTasksAll = new ArrayList<>(originTasks.size());
        List<Task> newTasksDepended = new ArrayList<>();// 被别人依赖的
        List<Task> newTasksWithOutDepend = new ArrayList<>();// 没有依赖的
        List<Task> newTasksRunAsSoon = new ArrayList<>();// 需要提升自己优先级的,先执行(这个先是相对于没有依赖的先)
        for (int index : indexList) {
            if (dependSet.contains(index)) {
                newTasksDepended.add(originTasks.get(index));
            } else {
                Task task = originTasks.get(index);
                if (task.needRunAsSoon()) {
                    newTasksRunAsSoon.add(task);
                } else {
                    newTasksWithOutDepend.add(task);
                }
            }
        }
        // 顺序:被别人依赖的————》需要提升自己优先级的————》需要被等待的————》没有依赖的
        sNewTasksHigh.addAll(newTasksDepended);
        sNewTasksHigh.addAll(newTasksRunAsSoon);
        newTasksAll.addAll(sNewTasksHigh);
        newTasksAll.addAll(newTasksWithOutDepend);
        return newTasksAll;
    }

    private static void printAllTaskName(List<Task> newTasksAll) {
        if (true) {
            return;
        }
        for (Task task : newTasksAll) {
            DispatcherLog.i(task.getClass().getSimpleName());
        }
    }

    public static List<Task> getTasksHigh() {
        return sNewTasksHigh;
    }

    /**
     * 获取任务在任务列表中的index
     *
     * @param originTasks
     * @param
     * @return
     */
    private static int getIndexOfTask(List<Task> originTasks,
                                      List<Class<? extends Task>> clsLaunchTasks, Class cls) {
        int index = clsLaunchTasks.indexOf(cls);
        if (index >= 0) {
            return index;
        }

        // 仅仅是保护性代码
        final int size = originTasks.size();
        for (int i = 0; i < size; i++) {
            if (cls.getSimpleName().equals(originTasks.get(i).getClass().getSimpleName())) {
                return i;
            }
        }
        return index;
    }
}

3.2.2 启动Task抽象化

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public interface ITask {

    /**
     * 优先级的范围,可根据Task重要程度及工作量指定;之后根据实际情况决定是否有必要放更大
     *
     * @return
     */
    @IntRange(from = Process.THREAD_PRIORITY_FOREGROUND, to = Process.THREAD_PRIORITY_LOWEST)
    int priority();

    void run();

    /**
     * Task执行所在的线程池,可指定,一般默认
     *
     * @return
     */
    Executor runOn();

    /**
     * 依赖关系
     *
     * @return
     */
    List<Class<? extends Task>> dependsOn();

    /**
     * 异步线程执行的Task是否需要在被调用await的时候等待,默认不需要
     *
     * @return
     */
    boolean needWait();

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

    /**
     * 只是在主进程执行
     *
     * @return
     */
    boolean onlyInMainProcess();

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

    void setTaskCallBack(TaskCallBack callBack);

    boolean needCall();
}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public abstract class Task implements ITask {
    protected String mTag = getClass().getSimpleName().toString();
    protected Context mContext = TaskDispatcher.getContext();
    protected boolean mIsMainProcess = TaskDispatcher.isMainProcess();// 当前进程是否是主进程
    private volatile boolean mIsWaiting;// 是否正在等待
    private volatile boolean mIsRunning;// 是否正在执行
    private volatile boolean mIsFinished;// Task是否执行完成
    private volatile boolean mIsSend;// Task是否已经被分发
    private CountDownLatch mDepends = new CountDownLatch(dependsOn() == null ? 0 : dependsOn().size());// 当前Task依赖的Task数量(需要等待被依赖的Task执行完毕才能执行自己),默认没有依赖

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

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

    /**
     * 是否需要尽快执行,解决特殊场景的问题:一个Task耗时非常多但是优先级却一般,很有可能开始的时间较晚,
     * 导致最后只是在等它,这种可以早开始。
     *
     * @return
     */
    public boolean needRunAsSoon() {
        return false;
    }

    /**
     * Task的优先级,运行在主线程则不要去改优先级
     *
     * @return
     */
    @Override
    public int priority() {
        return Process.THREAD_PRIORITY_BACKGROUND;
    }

    /**
     * Task执行在哪个线程池,默认在IO的线程池;
     * CPU 密集型的一定要切换到DispatcherExecutor.getCPUExecutor();
     *
     * @return
     */
    @Override
    public ExecutorService runOn() {
        return DispatcherExecutor.getIOExecutor();
    }

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

    /**
     * 当前Task依赖的Task集合(需要等待被依赖的Task执行完毕才能执行自己),默认没有依赖
     *
     * @return
     */
    @Override
    public List<Class<? extends Task>> dependsOn() {
        return null;
    }

    @Override
    public boolean runOnMainThread() {
        return false;
    }

    @Override
    public Runnable getTailRunnable() {
        return null;
    }

    @Override
    public void setTaskCallBack(TaskCallBack callBack) {}

    @Override
    public boolean needCall() {
        return false;
    }

    /**
     * 是否只在主进程,默认是
     *
     * @return
     */
    @Override
    public boolean onlyInMainProcess() {
        return true;
    }

    public boolean isRunning() {
        return mIsRunning;
    }

    public void setRunning(boolean mIsRunning) {
        this.mIsRunning = mIsRunning;
    }

    public boolean isFinished() {
        return mIsFinished;
    }

    public void setFinished(boolean finished) {
        mIsFinished = finished;
    }

    public boolean isSend() {
        return mIsSend;
    }

    public void setSend(boolean send) {
        mIsSend = send;
    }

    public boolean isWaiting() {
        return mIsWaiting;
    }

    public void setWaiting(boolean mIsWaiting) {
        this.mIsWaiting = mIsWaiting;
    }

}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public abstract class MainTask extends Task {

    @Override
    public boolean runOnMainThread() {
        return true;
    }
}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc 任务真正执行的地方
 */
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() {
//        TraceCompat.beginSection(mTask.getClass().getSimpleName());
        DispatcherLog.i(mTask.getClass().getSimpleName()
                + " begin run" + "  Situation  " + TaskStat.getCurrentSituation());

        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);
                mTaskDispatcher.markTaskDone(mTask);
            }
            DispatcherLog.i(mTask.getClass().getSimpleName() + " finish");
        }
//        TraceCompat.endSection();
    }

    /**
     * 打印出来Task执行的日志
     *
     * @param startTime
     * @param waitTime
     */
    private void printTaskLog(long startTime, long waitTime) {
        long runTime = System.currentTimeMillis() - startTime;
        if (DispatcherLog.isDebug()) {
            DispatcherLog.i(mTask.getClass().getSimpleName() + "  wait " + waitTime + "    run "
                    + runTime + "   isMain " + (Looper.getMainLooper() == Looper.myLooper())
                    + "  needWait " + (mTask.needWait() || (Looper.getMainLooper() == Looper.myLooper()))
                    + "  ThreadId " + Thread.currentThread().getId()
                    + "  ThreadName " + Thread.currentThread().getName()
                    + "  Situation  " + TaskStat.getCurrentSituation()
            );
        }
    }

}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public interface TaskCallBack {

    void call();
}

3.3.3 启动器线程池

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public class DispatcherExecutor {
    private static ThreadPoolExecutor sCPUThreadPoolExecutor;
    private static ExecutorService sIOThreadPoolExecutor;

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work

    public static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 5));
    private static final int MAXIMUM_POOL_SIZE = CORE_POOL_SIZE;
    private static final int KEEP_ALIVE_SECONDS = 5;
    private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>();
    private static final DefaultThreadFactory sThreadFactory = new DefaultThreadFactory();
    private static final RejectedExecutionHandler sHandler = new RejectedExecutionHandler() {// 一般不会到这里
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            Executors.newCachedThreadPool().execute(r);
        }
    };

    /**
     * 获取CPU线程池
     * @return
     */
    public static ThreadPoolExecutor getCPUExecutor() {
        return sCPUThreadPoolExecutor;
    }

    /**
     * 获取IO线程池
     * @return
     */
    public static ExecutorService getIOExecutor() {
        return sIOThreadPoolExecutor;
    }

    /**
     * The default thread factory.
     */
    private static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            namePrefix = "TaskDispatcherPool-" +
                    poolNumber.getAndIncrement() +
                    "-Thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

    static {
        sCPUThreadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory, sHandler);
        sCPUThreadPoolExecutor.allowCoreThreadTimeOut(true);
        sIOThreadPoolExecutor = Executors.newCachedThreadPool(sThreadFactory);
    }

}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public class DispatcherLog {
    private static final String TAG = DispatcherLog.class.getSimpleName();

    private static boolean sDebug = false;

    public static void i(String msg) {
        if (!sDebug) {
            return;
        }
        Log.i(TAG,msg);
    }

    public static boolean isDebug() {
        return sDebug;
    }

    public static void setDebug(boolean debug) {
        sDebug = debug;
    }

}

3.3.4 启动统计

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
class TaskStatBean {

    private String situation;
    private int count;

    public String getSituation() {
        return situation;
    }

    public void setSituation(String situation) {
        this.situation = situation;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public class TaskStat {

    private static volatile String sCurrentSituation = "";
    private static List<TaskStatBean> sBeans = new ArrayList<>();
    private static AtomicInteger sTaskDoneCount = new AtomicInteger();
    private static boolean sOpenLaunchStat = false;// 是否开启统计

    public static String getCurrentSituation() {
        return sCurrentSituation;
    }

    public static void setCurrentSituation(String currentSituation) {
        if (!sOpenLaunchStat) {
            return;
        }
        DispatcherLog.i("currentSituation   " + currentSituation);
        sCurrentSituation = currentSituation;
        setLaunchStat();
    }

    public static void markTaskDone() {
        sTaskDoneCount.getAndIncrement();
    }

    public static void setLaunchStat() {
        TaskStatBean bean = new TaskStatBean();
        bean.setSituation(sCurrentSituation);
        bean.setCount(sTaskDoneCount.get());
        sBeans.add(bean);
        sTaskDoneCount = new AtomicInteger(0);
    }

}

3.3.5 启动器

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc 启动器调用类
 */
public class TaskDispatcher {
    private long mStartTime;
    private static final int WAITTIME = 10000;
    private static Context sContext;
    private static boolean sIsMainProcess;
    private List<Future> mFutures = new ArrayList<>();
    private static volatile boolean sHasInit;
    private List<Task> mAllTasks = new ArrayList<>();
    private List<Class<? extends Task>> mClsAllTasks = new ArrayList<>();
    private volatile List<Task> mMainThreadTasks = new ArrayList<>();
    private CountDownLatch mCountDownLatch;
    private AtomicInteger mNeedWaitCount = new AtomicInteger();//保存需要Wait的Task的数量
    private List<Task> mNeedWaitTasks = new ArrayList<>();//调用了await的时候还没结束的且需要等待的Task
    private volatile List<Class<? extends Task>> mFinishedTasks = new ArrayList<>(100);//已经结束了的Task
    private HashMap<Class<? extends Task>, ArrayList<Task>> mDependedHashMap = new HashMap<>();
    private AtomicInteger mAnalyseCount = new AtomicInteger();//启动器分析的次数,统计下分析的耗时;

    private TaskDispatcher() {
    }

    public static void init(Context context) {
        if (context != null) {
            sContext = context;
            sHasInit = true;
            sIsMainProcess = SystemUtils.isMainProcess();
        }
    }

    /**
     * 注意:每次获取的都是新对象
     *
     * @return
     */
    public static TaskDispatcher createInstance() {
        if (!sHasInit) {
            throw new RuntimeException("must call TaskDispatcher.init first");
        }
        return new TaskDispatcher();
    }

    public TaskDispatcher addTask(Task task) {
        if (task != null) {
            collectDepends(task);
            mAllTasks.add(task);
            mClsAllTasks.add(task.getClass());
            // 非主线程且需要wait的,主线程不需要CountDownLatch也是同步的
            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();
                }
            }
        }
    }

    private boolean ifNeedWait(Task task) {
        return !task.runOnMainThread() && task.needWait();
    }

    @UiThread
    public void start() {
        mStartTime = System.currentTimeMillis();
        if (Looper.getMainLooper() != Looper.myLooper()) {
            throw new RuntimeException("must be called from UiThread");
        }
        if (mAllTasks.size() > 0) {
            mAnalyseCount.getAndIncrement();
            printDependedMsg();
            mAllTasks = TaskSortUtil.getSortResult(mAllTasks, mClsAllTasks);
            mCountDownLatch = new CountDownLatch(mNeedWaitCount.get());

            sendAndExecuteAsyncTasks();

            DispatcherLog.i("task analyse cost " + (System.currentTimeMillis() - mStartTime) + "  begin main ");
            executeTaskMain();
        }
        DispatcherLog.i("task analyse cost startTime cost " + (System.currentTimeMillis() - mStartTime));
    }

    public void cancel() {
        for (Future future : mFutures) {
            future.cancel(true);
        }
    }

    private void executeTaskMain() {
        mStartTime = System.currentTimeMillis();
        for (Task task : mMainThreadTasks) {
            long time = System.currentTimeMillis();
            new DispatchRunnable(task,this).run();
            DispatcherLog.i("real main " + task.getClass().getSimpleName() + " cost   " +
                    (System.currentTimeMillis() - time));
        }
        DispatcherLog.i("maintask cost " + (System.currentTimeMillis() - mStartTime));
    }

    private void sendAndExecuteAsyncTasks() {
        for (Task task : mAllTasks) {
            if (task.onlyInMainProcess() && !sIsMainProcess) {
                markTaskDone(task);
            } else {
                sendTaskReal(task);
            }
            task.setSend(true);
        }
    }

    /**
     * 查看被依赖的信息
     */
    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());
                }
            }
        }
    }

    /**
     * 通知Children一个前置任务已完成
     *
     * @param launchTask
     */
    public void satisfyChildren(Task launchTask) {
        ArrayList<Task> arrayList = mDependedHashMap.get(launchTask.getClass());
        if (arrayList != null && arrayList.size() > 0) {
            for (Task task : arrayList) {
                task.satisfy();
            }
        }
    }

    public void markTaskDone(Task task) {
        if (ifNeedWait(task)) {
            mFinishedTasks.add(task.getClass());
            mNeedWaitTasks.remove(task);
            mCountDownLatch.countDown();
            mNeedWaitCount.getAndDecrement();
        }
    }

    private void sendTaskReal(final Task task) {
        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);
                        DispatcherLog.i(task.getClass().getSimpleName() + " finish");
                    }
                });
            }
        } else {
            // 直接发,是否执行取决于具体线程池
            Future future = task.runOn().submit(new DispatchRunnable(task,this));
            mFutures.add(future);
        }
    }

    public void executeTask(Task task) {
        if (ifNeedWait(task)) {
            mNeedWaitCount.getAndIncrement();
        }
        task.runOn().execute(new DispatchRunnable(task,this));
    }

    @UiThread
    public void await() {
        try {
            if (DispatcherLog.isDebug()) {
                DispatcherLog.i("still has " + mNeedWaitCount.get());
                for (Task task : mNeedWaitTasks) {
                    DispatcherLog.i("needWait: " + task.getClass().getSimpleName());
                }
            }

            if (mNeedWaitCount.get() > 0) {
                mCountDownLatch.await(WAITTIME, TimeUnit.MILLISECONDS);
            }
        } catch (InterruptedException e) {
        }
    }

    public static Context getContext() {
        return sContext;
    }

    public static boolean isMainProcess() {
        return sIsMainProcess;
    }
}

/**
 * @author Huadao
 * @date Created in 2022/4/28
 * @desc
 */
public class DelayInitDispatcher {

    private Queue<Task> mDelayTasks = new LinkedList<>();

    private MessageQueue.IdleHandler mIdleHandler = new MessageQueue.IdleHandler() {
        @Override
        public boolean queueIdle() {
            if(mDelayTasks.size()>0){
                Task task = mDelayTasks.poll();
                new DispatchRunnable(task).run();
            }
            return !mDelayTasks.isEmpty();
        }
    };

    public DelayInitDispatcher addTask(Task task){
        mDelayTasks.add(task);
        return this;
    }

    public void start(){
        Looper.myQueue().addIdleHandler(mIdleHandler);
    }

}

链接:https://juejin.cn/post/7096013265179770910
作者:复制粘贴改改改

这里给大家分享一份《Android性能优化-大厂实战全解析》,这份《Android性能优化-大厂实战全解析包括有:腾讯、字节、阿里、百度、网易、美团等一线互联网大厂的优化实战解析,更是附赠360°性能调优学习指南有需要的朋友们也可以下载下来随时查漏补缺扫二维码直接领取

Android 性能优化之启动优化解析_第9张图片

图片

资料获取:

文末扫码二维码即可免费领取!

《‍Android性能优化-大厂实战全解析》目录及内容展示

腾讯团队

  • 腾讯光影研究室—Android P之Smart Linkify
  • 腾讯微信—Matrix原理分析系列之开篇
  • 腾讯微信—Mareix源码系列之如何解析应用安装包
  • 腾讯微信—Mareix源码系列之如何监控帧率变化
  • 腾讯Bugly—动态下发 so 库在 Android APK 安装包瘦身方面的应用
  • QQ音乐—彻底弄懂浏览器缓存策略
  • QQ音乐Android编译提速之路
  • 全民k歌适配arm64-v8a方案
  • 全民K歌内存篇1——线上监控与综合治理
  • 全民K歌内存篇2——虚拟内存浅析
  • 全民K歌内存篇3——native内存分析与监控
  • 腾讯Bugly—对字符串匹配算法的一点理解

图片

字节团队

  • 字节跳动技术团队— 深入理解Gradle框架之一:Plugin,Extension, buildSrc

  • 字节跳动技术团队—深入理解gradle框架之二:依赖实现分析

  • 字节跳动技术团队—Scene:Android 开源页面导航和组合框架

  • 字节跳动技术团队—AwCookieManager.nativeGetCookiecrash 排查

  • 字节跳动技术团队—另类 BadTokenException 问题分析和解决

  • 字节跳动技术团队—抖音包大小优化-资源优化

  • 字节跳动技术团队—二维码扫描优化

  • 字节跳动—Android Camera内存问题剖析

  • 抖音BoostMultiDex优化实践:Android低版本上APP首次启动时间减少80%

  • 抖音BoostMultiDex优化实践:Android低版本上APP首次启动时间减少80%(二)

  • 抖音 Android 性能优化系列:Java 内存优化篇

  • 今日头条 Android ‘秒’ 级编译速度优化

图片

阿里团队

  • 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」

  • 支付宝 App 构建优化解析:通过安装包重排布优化 Android 端启动性能

  • 支付宝 App 构建优化解析:Android 包大小极致压缩

  • 解决支付宝包体积优化的遗留问题:运行时获取dexpc

  • 闲鱼技术—曾梦想 if-else 走天涯?看看“责任树模式”优化

  • 闲鱼如何在2个月内实现Android启动速度翻倍的?

  • 高德技术—Android Native 内存泄漏系统化解决方案

  • 天猫精灵技术—史上最全Android渲染机制讲解(长文源码深度剖析)

图片

百度团队

  • 百度APP-Android H5首屏优化实践

  • 百度App技术—一种简单优雅的TextView行间距适配方案

  • 百度App技术—Android 10分区存储介绍及百度APP适配实践

  • 百度App技术—Gradle 与 Android 构建入门

  • 百度App组件化之路

  • 百度App网络深度优化系列《三》弱网优化

图片

网易团队

  • 网易新闻客户端 H5 秒开优化

  • 网易新闻构建优化:如何让你的构建速度“势如闪电”

  • 网易传媒技术团队—AOP技术在客户端的应用与实践

  • 网易大数据|互联网产品决策秘笈: AB测试

图片

美团团队

  • 美团技术团队—Android静态代码扫描效率优化与实践

  • 美团技术团队—Probe:Android线上OOM问题定位组件

  • 美团技术团队—移动端UI一致性解决方案

  • 美团—设计稿(UI视图)自动生成代码方案的探索

  • ……

  • ……

图片

资料获取

文末扫码二维码即可免费领取!

Android 性能优化之启动优化解析_第10张图片

《360°全方面性能调优》目录及内容展示

图片

第一章 设计思想与代码质量优化

1.六大原则

  • 单一职责原则
  • 里氏替换原则
  • 依赖倒转原则
  • 接口隔离原则
  • ……

2.设计模式

  • 结构型模式:桥接模式、适配器模式、装饰器模式、代理模式、门面(外观)模式……

  • 创建型模式:建造者模式、单例模式、抽象工厂模式、工厂方法模式……

  • 数据结构:数组、栈、队列、链表、树……

  • 算法:排序算法、查找算法……

    图片

第二章 程序性能优化

1.启动速度与执行效率优化

  • 冷启动和热启动解析
  • APP 启动黑白屏解决办法
  • APP 卡顿问题分析及解决方案
  • 启动速度与执行效率优化之 StrictMode
  • ……

2.布局检测与优化

  • 布局层级优化
  • 过度渲染
  • ……

3.内存优化

  • 内存抖动和内存泄漏
  • 内存大户
  • Bitmap 内存优化
  • Profile 内存监测工具
  • Mat 大对象与泄漏检测
  • 耗电优化
  • 网络传输与数据存储优化网络传输与数据存储优化
  • APK 大小优化
  • 屏幕适配
  • ……

4.耗电优化

  • Doze&Standby
  • Battery Historian
  • JobScheduler
  • WorkManager

5.网络传输与数据存储优化

  • google 序列化工具 protobuf
  • 7z 极限压缩
  • ……

6.APK 大小优化

  • APK 瘦身
  • 微信资源混淆原理
  • ……

图片

7.屏幕适配

进行适配的原理

屏幕分辨率限定符与 smallestWidth 限定符适配原理

为什么选择 smallestWidth 限定符适配

怎么适配其他 module

常见问题处理

8.OOM 问题原理解析

  • adj 内存管理机制
  • JVM 内存回收机制与 GC 算法解析
  • 生命周期相关问题总结
  • Bitmap 压缩方案总结
  • ……

9.ANR 问题解析

  • AMS 系统时间调节原理
  • 程序等待原理分析
  • ANR 问题解决方案
  • ……

10.Crash 监控方案

  • Java 层监控方案
  • Nativie 层监控方案
  • ……

图片

第三章 开发效率优化

1.分布式版本控制系统 Git

  • 企业高效持续集成平台场景介绍
  • GIT 分布式版本控制系统
  • GIT 分支管理
  • ……

2.自动化构建系统 Gradle:

  • Gradle 与 Android 插件:gradle 与 android gradle 插件的关系、Gradle Transform API 的基本使用……
  • Gradle Transform API 的基本使用:什么是 Transform、Transform 的使用场景、Transform API 学习、输入的类型……
  • 自定义插件开发:Gradle 插件简介、开始准备、实践、自定义 Gradle 插件、buildSrc 模块方式……
  • 插件实战:多渠道打包、发版自动钉钉……

图片
Android 性能优化之启动优化解析_第11张图片

你可能感兴趣的:(Android开发,android,性能优化,adb)