赞
踩
应用启动分类:
冷启动:耗时最多、衡量标准。
ClickEvent->IPC->Process.start->ActivityThread->bindApplication->LifeCycle->ViewRootImpl
热启动:最快,后台->前台
温启动:较快
冷启动的相关任务
冷启动之前:
启动App -> 加载空白Window-> 创建进程
随后任务:
创建Application -> 启动主线程 -> 创建入口Acitivity -> 加载布局 -> 布置屏幕 -> 首帧绘制
优化方向:
Application和Activity生命周期
adb shell am start -W packagename/首屏Activity
Status:ok(启动无异常) LaunchState:COLD(冷启动) Activity:.SplashActivity(目标Activity)
TotalTime:所有Activity启动耗时 ThisTime:最后一个Activity启动耗时 WaitTime:AMS启动Activity的总耗时
当前为application->SplashActivity,中间无中转Activity,因此TotalTime与ThisTime一致。
线下使用,不能带到线上 非严谨、精确时间
启动时埋点,启动后结束埋点,二者差值即为启动时间。
从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); } }); } };
精确,可带到线上,推荐使用。 注意避开误区,采用Feed第一条展示。 addOnDrawListener 要求 API16,版本低于16可选择使用addOnPreDrawListener。
图形化展示执行时间、调用栈等,信息全面,包含所有线程。
缺点:运行时开销严重,程序整体变慢,可能会带偏优化方向。
使用方式:
Debug.startMethodTracing("");
Debug.stoptMethodTracing();
生成文件在sd卡:Android/data/packagename/files
WallClockTime:代码执行的所需时间,包括阻塞耗时如等待锁时
ThreadTime:CPU执行时间,CPU确实在该线程工作的时间
CallChart: 由上至下即是调用者到被调用者,橙色为系统api的调用,绿色为应用自身api的调用,蓝色为第三方api的调用
FlameChart: 收集重复调用的函数之类的
TopDown: 很清晰的函数调用列表,可右键跳转到详细代码中
Total:函数执行总时间
self:函数自身代码执行时间
Children:函数内部所调用函数的执行时间
Total=self+Children
BottomUp: 也是函数调用列表,只不过与TopDown的展示相反
TraceView是Android平台一个很好的性能分析的工具,能够以图形的形式显示跟踪日志,但是已弃用。另外TraceView的性能消耗太大,得到的结果不真实。
结合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
Systrace 允许你收集和检查设备上运行的所有进程的计时信息。它包括AndroidKernel的一些数据(例如CPU调度程序,IO和App Thread),并且会生成HTML报告,方便用户查看分析trace内容。但是不支持应用程序代码的耗时分析,如果需要分析程序代码的执行时间,那就要结合函数插桩的方式,对细节进行分析。
代替Traceview的,便是CPU Profiler。
它可以检查通过使用Debug类对应用进行插桩检测而捕获的.trace 文件、记录新方法跟踪信息、保存.trace 文件以及检查应用进程的实时CPU使用情况。
具体使用方式,与Traceview大同小异。
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;
这种常规获取方法耗时的方式就很不合理,侵入性强、工作量大。
Aspect Oriented Programing,面向切面编程。
应用场景:针对同一类问题的统一处理时。
优点:无侵入添加代码,修改简单。
使用AspectJ辅助实现
程序运行时的执行点,可以作为切面的地方:
带条件的JoinPoints
一种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)); } }
核心思想:子线程分担主线程任务,并行减少时间.
需要注意:不符合异步优化的,需要在某阶段完成的,要区分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("第三方框架初始化耗时异常"); } } }
常规异步的缺点:代码不优雅,场景不好处理(有的初始化任务会存在依赖关系),维护成本高
启动器优化
核心思想:充分利用CPU多核,自动梳理任务顺序
启动器流程:
主线程:相当于常规初始化
并发:相当于异步初始化
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();
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; } }); } }
针对以上场景时:
常规方案:
new Handler().postDelayed,Feed展示后调用
时机不便控制导致Feed卡顿
更优方案
核心思想:对延迟任务进行分批初始化
利用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();
}
}
在用户点击手机桌面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);
}
}
优化总方针:异步、延迟、懒加载,技术、业务相结合。
收敛启动代码的的修改权限:结合CI,修改启动代码需要Review或通知,防止维护好的启动启动代码被破坏。
提前加载SharedPreferences
SharedPreferences是IO操作,可在Multidex之前加载,利用此阶段的CPU,此时的CPU是肯定尚未跑满的。SharedPreferences是系统类,在这之前提前加载初始化SharedPreferences不会有异常,是少有的可以在Multidex之前加载优化的类。
覆写getApplicationContext(),返回this。
启动阶段不启动子进程
子进程会共享CPU资源。
注意启动顺序:App onCreate之前是ContentProvider。
类加载优化:提前异步类加载
Class.forName()只加载类本身及其静态变量引用类。
new 类实例,可以额外加载类成员变量的引用类。
启动阶段抑制GC
CPU锁频
你的启动优化是怎么做的?
是启动优化的负责人
分析现状、确认问题
比如启动速度的监听、竞品分析
针对性优化:思路从小到大
长期保持优化效果:启动器代码的封装;收敛启动代码的修改权限
是怎么异步的,异步遇到问题没有?
体现演进过程:线程池->启动器
详细介绍启动器
你做了启动优化,觉得有哪些容易忽略的注意点?
cpu time与wall time的区别,cpu time是优化方向
注意延迟初始化的优化:不用常规方式,用idleHandler
介绍下黑科技:类加载,抑制GC,CPU锁频
版本迭代导致的启动变慢,有好的解决方式吗?
启动器
结合CI
新加代码后,上线前就先及时测量启动速度,监控完善
Wall Duration:代码执行时间 CPU Duration:代码消耗CUP的时间(重点指标,优化方向)
比如锁的冲突:线程执行的方法本身也许并不耗时,但是由于要等待锁的释放,而这便会加长代码的执行时间,等待锁处于阻塞的过程,CPU并不会在在该线程的方法上消耗时间。
获取当前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 [包名]
/** * @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; } }
/** * @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();
}
/** * @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; } }
/** * @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); } }
/** * @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性能优化-大厂实战全解析》目录及内容展示
腾讯团队
字节团队
字节跳动技术团队— 深入理解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视图)自动生成代码方案的探索
……
……
资料获取
文末扫码二维码即可免费领取!
《360°全方面性能调优》目录及内容展示
1.六大原则
2.设计模式
结构型模式:桥接模式、适配器模式、装饰器模式、代理模式、门面(外观)模式……
创建型模式:建造者模式、单例模式、抽象工厂模式、工厂方法模式……
数据结构:数组、栈、队列、链表、树……
算法:排序算法、查找算法……
1.启动速度与执行效率优化
2.布局检测与优化
3.内存优化
4.耗电优化
5.网络传输与数据存储优化
6.APK 大小优化
7.屏幕适配
进行适配的原理
屏幕分辨率限定符与 smallestWidth 限定符适配原理
为什么选择 smallestWidth 限定符适配
怎么适配其他 module
常见问题处理
…
8.OOM 问题原理解析
9.ANR 问题解析
10.Crash 监控方案
1.分布式版本控制系统 Git
2.自动化构建系统 Gradle:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。