一、启动优化小技巧-Theme 切换
Theme 切换启动速度没变,是让用户感觉上变快
下面是使用方式
1、创建 launcher.xml
-
2、style 文件使用
3、在 manifest 文件中使用
4、在 Activity 中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
//super.onCreate()之前切换回来
setTheme(R.style.AppTheme)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
二、异步优化
核心思想:子线程分担主线程任务,并行减少执行时间
下面以 Application 为例,在 Application 中依赖了很多第三方库,下面是没有异步优化时执行的时间,如下代码所示:
class PerformanceApp : Application() {
override fun onCreate() {
super.onCreate()
sApplication = this
LaunchTimer.startRecord()
initBugly()
initUmeng()
initFresco()
initMap()
LaunchTimer.endRecord("PerformApp")
}
}
执行结果如下图所示:耗时 1370 毫秒
下面是用线程池 异步执行三方库
class PerformanceApp : Application() {
//参考 AsyncTask 源码中线程池设置的核心数,根据 CPU 的核心数确定线程池的数量
private val CPU_COUNT = Runtime.getRuntime().availableProcessors()
private val CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4))
override fun onCreate() {
super.onCreate()
sApplication = this
LaunchTimer.startRecord()
val service = Executors.newFixedThreadPool(CORE_POOL_SIZE)
service.submit {
initBugly()
}
service.submit {
initUmeng()
}
service.submit {
initFresco()
}
service.submit {
initMap()
}
LaunchTimer.endRecord("PerformApp")
}
}
上面代码没有放在一个 service.submit { } 方法中,原因是为了减少资源浪费。理论上可以都放在一块,但是效果不够好,根据 CPU 的核心数确定线程池的数量,如果创建了 3 个,而只用了一个,这是一种浪费,所以对每个方法都 service.submit { } 一下。
执行结果如下图所示:耗时 12 毫秒
通过上面代码分析可知,异步执行效果明显。
异步优化注意:
- 有些代码不符合异步要求(如:Handler());
- 区分 CPU 密集型和 IO 密集型
- 有些代码需要在某阶段完成
如:initMap() 必须要在Application 的 onCreate() 结束掉就执行完成。
解决方案:创建 CountDownLatch 类,代码示例如下:
class PerformanceApp : Application() {
//参考 AsyncTask 源码中线程池设置的核心数,根据 CPU 的核心数确定线程池的数量
private val CPU_COUNT = Runtime.getRuntime().availableProcessors()
private val CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4))
private val mCountDownLatch = CountDownLatch(1)
override fun onCreate() {
super.onCreate()
sApplication = this
LaunchTimer.startRecord()
val service = Executors.newFixedThreadPool(CORE_POOL_SIZE)
service.submit {
initBugly()
}
service.submit {
initUmeng()
}
service.submit {
initFresco()
}
service.submit {
//高德地图
initMap()
mCountDownLatch.countDown()
}
//CountDownLatch不被满足的话会一直等待
mCountDownLatch.await()
LaunchTimer.endRecord("PerformApp")
}
}
缺点:
- 代码不够优雅
- 场景不好处理(依赖关系)
- 特定时间段内结束某个任务不好处理
- 维护成本高
CountDownLatch 参考
java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例
CountDownLatch详解
三、异步优化启动器
核心思想:充分利用CPU多核,自动梳理任务顺序
启动器流程
- 代码 Task 化,启动逻辑抽象为 Task
- 根据所有任务依赖关系排序生成一个有向无环图
- 多线程按照排序后的优先级依次执行
headTask:所有Task执行之前所做的事情
tailTask:所有任务结束之后
idleTask:程序空闲时执行
使用示例:
class PerformanceApp : Application() {
override fun onCreate() {
super.onCreate()
sApplication = this
LaunchTimer.startRecord()
TaskDispatcher.init(this)
val dispatcher = TaskDispatcher.createInstance()
dispatcher.addTask(InitAMapTask())
.addTask(InitStethoTask())
.addTask(InitWeexTask())
.addTask(InitBuglyTask())
.addTask(InitFrescoTask())
.addTask(InitJPushTask())
.addTask(InitUmengTask())
.addTask(GetDeviceIdTask())
.start()
//启动器中配置需要等待的函数没有完成是都会等待
dispatcher.await()
LaunchTimer.endRecord("task")
}
}
四、延迟初始化方案
1、常规方案
- new Handler().postDelayed
- Feed 展示后调用
示例展示:
在 NewsAdapter 中设置数据展示后的监听器
//NewsAdapter 中
public void setOnFeedShowCallBack(OnFeedShowCallBack callBack) {
this.mCallBack = callBack;
}
@Override
public void onBindViewHolder(@NonNull final ViewHolder holder, int position) {
//onBindViewHolder 回调多次,而只统计一次,加个变量标识
if (position == 0 && !mHasRecorded) {
mHasRecorded = true;
holder.layout.getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
holder.layout.getViewTreeObserver().removeOnPreDrawListener(this);
LaunchTimer.Companion.endRecord("FeedShow");
if (mCallBack != null) {
mCallBack.onFeedShow();
}
return true;
}
});
}
}
//MainActivity 中
override fun onFeedShow() {
DispatchRunnable(DelayInitTaskA()).run()
DispatchRunnable(DelayInitTaskB()).run()
}
问题:
- 导致 Feed 卡顿:我们 NewsAdapter 中的回调是在主线程中执行,那么 MainActivity 中的 onFeedShow() 异步执行可能会延迟,造成卡顿
- 时间不便控制
2、优化方案
核心思想:对延迟任务进行分批初始化
- 利用 IdleHandler 特性,空闲执行
示例展示:
首先创建 DelayInitDispatcher 类
public class DelayInitDispatcher {
private Queue mDelayTasks = new LinkedList<>();
/**
* IdleHandler:在系统空闲后执行
*/
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);
}
}
创建 DispatchRunnable 类
/**
* 任务真正执行的地方
*/
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()
);
}
}
}
在 MainActivity 中的使用
override fun onFeedShow() {
val delayInitDispatcher = DelayInitDispatcher()
delayInitDispatcher.addTask(DelayInitTaskA())
.addTask(DelayInitTaskB())
.start()
}
优点:
- 执行时机明确
- 缓解 Feed 卡顿
每次只执行一个 Task,而且是在系统空闲时执行。
实例 demo 如:PerformanceDemo