Github地址:新聞?lì)怉pp (MVP + RxJava + Retrofit+Dagger+ARouter)
App啟動(dòng)優(yōu)化
- 冷啟動(dòng)之前
啟動(dòng)app->加載空白window->創(chuàng)建進(jìn)程 - 隨后
創(chuàng)建Application->啟動(dòng)主線程->MainActivity->加載布局->布置屏幕->首幀繪制
啟動(dòng)分類
冷啟動(dòng)
click event->IPC->Process.start->ActivityThread->bindApplication->LifeCycle->ViewRootImpl熱啟動(dòng)
后臺(tái)->前臺(tái)溫啟動(dòng)
從lifeCycle開始
優(yōu)化方向:Application和Activity啟動(dòng)流程
啟動(dòng)時(shí)間測量方式
adb 命令方式:無法帶到線上
adb shell am start -W 包名/啟動(dòng)的activity
ThisTime:最后一個(gè)Activity啟動(dòng)耗時(shí)
TotalTime:所有Activity啟動(dòng)耗時(shí)
WaitTime:Ams啟動(dòng)Activity的總耗時(shí)
埋點(diǎn)的方式
首先工具類
public class LaunchTimer {
private static long sTime;
public static void startRecord() {
sTime = System.currentTimeMillis();
}
public static void endRecord() {
long cost = System.currentTimeMillis()-sTime;
Log.e("TAG","cost time"+cost);
}
}
在Application中調(diào)用
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
LaunchTimer.startRecord();
}
在首頁顯示的第一條數(shù)據(jù)的時(shí)候去調(diào)用endRecord,比如我這里首頁顯示的是知乎缰猴,所以去知乎頁面的recycleview中的adapter中調(diào)用
if (holder.getAdapterPosition() == 0 && !mHasRecord) {
mHasRecord=true;
holder.getView(R.id.card_stories).getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
holder.getView(R.id.card_stories).getViewTreeObserver().removeOnPreDrawListener(this);
LaunchTimer.endRecord();
return true;
}
});
}
注意onWindowFocusChanged只是首幀的時(shí)間
啟動(dòng)優(yōu)化工具選擇
traceView
圖形的形式展示執(zhí)行時(shí)間,調(diào)用棧等;信息全面,包含所有的線程
使用方式
- Debug.startMethodTracing("");
- Debug.stopMethodTracing();
-
生成文件的位置,比如我這里設(shè)置的名字是App,選擇右下角Device File Expleroer然后找到sdcard目錄下就可以了
image.png
AOP實(shí)現(xiàn)獲取方法耗時(shí)
通常做法,
long start=System.currentTimeMillis();
initApplicationComponent();
long cost=System.currentTimeMillis()-start;
start=System.currentTimeMillis();
intARouter();
cost=System.currentTimeMillis()-start;
AOP實(shí)現(xiàn)
添加依賴
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
apply plugin: 'android-aspectjx'
implementation 'org.aspectj:aspectjrt:1.8.+'
代碼
//com.peakmain.gankzhihu.App自己的包名+Application名字
@Aspect
public class SectionAspect {
@Around("call (* com.peakmain.gankzhihu.App.**(..))")
public void getTime(ProceedingJoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
long time=System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
//signature.getName(方法的名字
Log.e("SectionAspect",signature.getName()+" cost Time:"+(System.currentTimeMillis()-time));
}
}
AOP的優(yōu)點(diǎn):無侵略性惧磺,修改方便
異步優(yōu)化
一般異步優(yōu)化
首先定義線程池
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 static final int KEEP_ALIVE_SECONDS = 30;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "App #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
private CountDownLatch mCountDownLatch = new CountDownLatch(1);
Application中的onCreate方法中去調(diào)用
LaunchTimer.startRecord();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
initApplicationComponent();
}
});
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
intARouter();
}
});
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
initUtils();
//只是模擬
mCountDownLatch.countDown();
}
});
handler = new Handler(Looper.getMainLooper());
mainThreadId = android.os.Process.myTid();//獲取當(dāng)前線程的id
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
LaunchTimer.endRecord();
CountDownLatch 的作用:解決假設(shè)你在Application的onCreate中做了異步,但是一些異步的任務(wù)需要在onCreate執(zhí)行完成之前就結(jié)束做裙,因?yàn)镾plash界面可能會(huì)直接使用到這些任務(wù),比如初始化阿里巴巴的weex庫匙隔。
存在問題:1.代碼不優(yōu)雅,維護(hù)成本高阁簸。2.存在依賴關(guān)系不好處理,比如初始化極光推送之前需要獲取設(shè)備的id
啟動(dòng)器
(工具類大家可以看我的Github:https://github.com/Peakmain/gankzhihu中的launchstarter中獲群哒伞)
- 使用一:一般情況启妹,即指的是默認(rèn)的情況下,繼承Task就可以了
public class ARouterTasks extends Task {
@Override
public void run() {
// 這兩行必須寫在init之前,否則這些配置在init過程中將無效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 開啟調(diào)試模式(如果在InstantRun模式下運(yùn)行醉旦,必須開啟調(diào)試模式饶米!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
ARouter.init((App)mContext); // 盡可能早,推薦在Application中初始化
}
}
- 使用二:有依賴關(guān)系的车胡,比如初始化極光推送
public class GetDeviceIdTask extends Task {
private String mDeviceId;
@Override
public void run() {
// 真正自己的代碼
TelephonyManager tManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
mDeviceId = tManager.getDeviceId();
App app = (App ) mContext;
app.setDeviceId(mDeviceId);
}
}
public class JPushTask extends Task {
@Override
public List<Class<? extends Task>> dependsOn() {
List<Class<? extends Task>> task = new ArrayList<>();
task.add(GetDeviceIdTask.class);
return task;
}
@Override
public void run() {
JPushInterface.init(mContext);
App app = (App ) mContext;
JPushInterface.setAlias(mContext, 0, app.getDeviceId());
}
}
- 使用三:執(zhí)行在主線程檬输,且需要在onCreate執(zhí)行完成之前就結(jié)束
public class WeexTask extends MainTask {
@Override
public boolean needWait() {
return true;
}
@Override
public void run() {
InitConfig config = new InitConfig.Builder().build();
WXSDKEngine.initialize((Application) mContext, config);
}
}
最后在App中使用:初始化然后添加自己的添加的Task就可以了
LaunchTimer.startRecord();
TaskDispatcher.init(App.this);
TaskDispatcher dispatcher = TaskDispatcher.createInstance();
dispatcher.addTask(new ARouterTasks())
.addTask(new UtilsTasks())
.start();
dispatcher.await();
LaunchTimer.endRecord();
延遲任務(wù)進(jìn)行初始化
我們都知道我們可以將部分不重要的任務(wù)挪到啟動(dòng)結(jié)束之后再去執(zhí)行,這種方法可以減少啟動(dòng)速度匈棘。但是存在一個(gè)問題丧慈,如果延遲執(zhí)行時(shí)間過長,那么主線程卡頓時(shí)間也會(huì)過長主卫,導(dǎo)致用戶體驗(yàn)極差伊滋。
核心思想:對延遲任務(wù)進(jìn)行分批初始化
利用idleHandler特性,空閑執(zhí)行
在前面的啟動(dòng)時(shí)間的埋點(diǎn)方法中队秩,繼續(xù)進(jìn)行修改
public interface OnFeedShowCallBack {
void onFeedShow();
}
private OnFeedShowCallBack mCallBack;
public void setOnFeedShowCallBack(OnFeedShowCallBack callBack) {
this.mCallBack = callBack;
}
if (holder.getAdapterPosition() == 0 && !mHasRecord) {
mHasRecord = true;
holder.getView(R.id.card_stories).getViewTreeObserver()
.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
holder.getView(R.id.card_stories).getViewTreeObserver().removeOnPreDrawListener(this);
LaunchTimer.endRecord();
if (mCallBack!=null){
mCallBack.onFeedShow();
}
return true;
}
});
}
延遲加載調(diào)用類
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);
}
}
在設(shè)置adapter的類中去setOnFeedShowCallBack方法即可,我的是在ZhihuFragment中
adapter.setOnFeedShowCallBack(() -> {
DelayInitDispatcher delayInitDispatcher = new DelayInitDispatcher();
delayInitDispatcher.addTask(new DelayInitTaskA())
.addTask(new DelayInitTaskB())
.start();
});
其他優(yōu)化
提前加載SharedPreferences
- Multidex之前加載,利用此階段的CPU
- 復(fù)寫getApplication()返回this
啟動(dòng)階段不啟動(dòng)子進(jìn)程
- 子進(jìn)程會(huì)共享CPU資源笑旺,導(dǎo)致主進(jìn)程CPU緊張
- 注意啟動(dòng)順序:App onCreate之前調(diào)用的ContentProvider
提前異步類加載