电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術(shù)文章
文章詳情頁

詳解Android性能優(yōu)化之啟動優(yōu)化

瀏覽:11日期:2022-09-18 11:38:14
目錄1、為什么要進(jìn)行啟動優(yōu)化2、啟動的分類2.1 冷啟動2.2 熱啟動2.3 溫啟動3、優(yōu)化方向4、啟動時(shí)間的測量方式4.1 使用adb 命令方式(線下使用方便)4.2 手動打點(diǎn)方式5、優(yōu)雅獲取方法耗時(shí)5.1 AOP Aspect Oriented Programming 面向切面編程5.2 aspectj的使用6、啟動優(yōu)化的工具選擇6.1 traceview7、啟動器7.1 啟動器的思想7.2 啟動器的原理7.3啟動器使用方式7.4啟動器核心代碼8、其他優(yōu)化方案8.1 對延遲任務(wù)進(jìn)行分批初始化8.2 提前加載SP 可以放到multidex之前加載,利用此階段的CPU1、為什么要進(jìn)行啟動優(yōu)化

網(wǎng)上流行一種說法,就是8秒定律,意思是說,如果用戶在打開一個(gè)頁面,在8秒的時(shí)間內(nèi)還沒有打開,那么用戶大概的會放棄掉,意味著一個(gè)用戶的流失。從這里就可以看出,啟動優(yōu)化的重要性了。

2、啟動的分類2.1 冷啟動

先來看看冷啟動的流程圖

詳解Android性能優(yōu)化之啟動優(yōu)化

從圖中可以看出,APP啟動的過程是:ActivityManagerProxy 通過IPC來調(diào)用AMS(ActivityManagerService),AMS通過IPC啟動一個(gè)APP進(jìn)程,ApplicationThread通過反射來創(chuàng)建Application并且綁定,最后通過ActivityThread來控制activity的生命周期,在相關(guān)頁面的生命周期中通過ViewRootImpl來對view的實(shí)現(xiàn)。從而完成應(yīng)用的啟動。

2.2 熱啟動

熱啟動的速度是最快的,它就是進(jìn)程從后臺切換到前臺的一個(gè)過程。

2.3 溫啟動

溫啟動只會重新走一遍頁面的生命周期,但是對于進(jìn)程,application不會重新在創(chuàng)建。

3、優(yōu)化方向

上面介紹了啟動的幾種方式可以看出,我們針對啟動優(yōu)化,基本只是優(yōu)化冷啟動就可以了。但是從冷啟動的啟動流程中很多都是系統(tǒng)做的,我們沒有辦法操控。我們能做的,就是application的生命周期和activity的生命周期這部分,啟動優(yōu)化往往就是從這兩塊入手。

4、啟動時(shí)間的測量方式4.1 使用adb 命令方式(線下使用方便)

adb shell am start -W 包名/包名+類名

詳解Android性能優(yōu)化之啟動優(yōu)化

ThisTime:最后一個(gè)activity的啟動耗時(shí)

TotalTime:所有activity的啟動耗時(shí)

WaitTime:AMS啟動activity的總耗時(shí)

這里由于我直接進(jìn)入到主界面,中間并沒有SplashActivity,所有ThisTime 和 TotalTime的時(shí)間是一樣的

優(yōu)勢:在線下使用方便,適合于跑線下的產(chǎn)品,和獲取競品的時(shí)間,然后比對

缺點(diǎn):不能帶到線上,獲取的時(shí)間,只能說是一個(gè)大概時(shí)間,不是很嚴(yán)謹(jǐn)。

4.2 手動打點(diǎn)方式

詳解Android性能優(yōu)化之啟動優(yōu)化

通過System.currentTimeMillis()來打時(shí)間戳

缺點(diǎn):很明顯,對代碼侵入性非常的大,如果說我想要打出每一個(gè)任務(wù)花費(fèi)的時(shí)間,那么代碼看起來就很惡心了

5、優(yōu)雅獲取方法耗時(shí)5.1 AOP Aspect Oriented Programming 面向切面編程

AOP:通過預(yù)編譯方式和運(yùn)行期動態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。它的核心思想就是將應(yīng)用程序中的業(yè)務(wù)邏輯處理部分同對其提供通用服務(wù)部分即“橫切關(guān)注點(diǎn)”進(jìn)行分離。

OOP:引入封裝,繼承,多態(tài)等概念來建立一種對象層次結(jié)構(gòu),它允許開發(fā)者定義縱向的關(guān)系,但并不適合橫向的關(guān)系??梢哉fAOP是OOP的一種補(bǔ)充和完善。

5.2 aspectj的使用

AspectJ是一個(gè)面向切面編程的框架,是對java的擴(kuò)展且兼容java,AspectJ定義了AOP語法,它有一個(gè)專門的編譯器來生成遵守java字節(jié)編碼規(guī)范的Class文件。

在項(xiàng)目的根目錄的build.gradle添加依賴:

dependencies {

    classpath ’com.android.tools.build:gradle:3.5.2’

    classpath ’com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’

    // NOTE: Do not place your application dependencies here; they belong

    // in the individual module build.gradle files

}

在app下的build.gradle添加依賴

apply plugin: ’android-aspectjx’

在dependencies中添加

implementation ’org.aspectj:aspectjrt:1.9.4’

然后創(chuàng)建一個(gè)類

package com.noahedu.myapplication.aspectj;import android.util.Log;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;@Aspectpublic class MyApplicationAspectj { @Around('call(* com.noahedu.myapplication.MyApplication.**(..))') public void getTime(ProceedingJoinPoint joinPoint){Signature signature = joinPoint.getSignature();String name = signature.getName();long time = System.currentTimeMillis();try { joinPoint.proceed();} catch (Throwable throwable) { throwable.printStackTrace();}Log.e('MyApplicationAspectj ' ,(name + ' cost ' + (System.currentTimeMillis() - time))); }}

這樣我們運(yùn)行的時(shí)候,就會直接在logcat中打印出application中的onCreate方法中所有調(diào)用方法的耗時(shí)情況了

2020-07-10 14:22:27.151 1619-1619/? E/MyApplicationAspectj: taskOne cost 150

2020-07-10 14:22:29.203 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskTwo cost 2052

2020-07-10 14:22:29.554 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskThrid cost 351

2020-07-10 14:22:30.556 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskFour cost 1001

這樣我們幾乎沒有碰Application中的任何代碼,也就夠得出各個(gè)方法的耗時(shí),幾乎對代碼無侵入。

6、啟動優(yōu)化的工具選擇6.1 traceview

TraceView是Android SDK中內(nèi)置的一個(gè)工具,他可以加載trace文件,以圖形化的形式展示相應(yīng)代碼的執(zhí)行時(shí)間,次數(shù)及調(diào)用棧,便于我們分析。

Debug.startMethodTracing('MyApplication'); //TODO Debug.stopMethodTracing();

運(yùn)行項(xiàng)目就可以我們的SD卡中找到對應(yīng)的trace文件了,如果是Android studio可以直接在右下角找到DeviceFileExporer -->sdcard --> Android -- > data -->files --->自己項(xiàng)目的包名

然后雙擊即可查看文件了

優(yōu)點(diǎn):使用簡單,圖形形式展示所執(zhí)行的時(shí)間,調(diào)用棧等。

缺點(diǎn):會影響到我們優(yōu)化的方向,由于是圖形化展示,也是比較占用CPU資源的,所以得到的時(shí)間往往是比實(shí)際的要大。

7、啟動器

上面介紹了多了幾個(gè)獲取任務(wù)執(zhí)行時(shí)間的方式和工具,那么當(dāng)我們知道某個(gè)方法耗時(shí)了,我們該怎么處理呢?

package com.noahedu.myapplication;import android.app.Application;import android.os.Debug;import android.util.Log;public class MyApplication extends Application { @Override public void onCreate() {super.onCreate();Debug.startMethodTracing('MyApplication');taskOne();taskTwo();taskThrid();taskFour();Debug.stopMethodTracing(); } public void taskOne(){try { Thread.sleep(150);} catch (InterruptedException e) { e.printStackTrace();} } public void taskTwo(){try { Thread.sleep(2050);} catch (InterruptedException e) { e.printStackTrace();} } public void taskThrid(){try { Thread.sleep(350);} catch (InterruptedException e) { e.printStackTrace();} } public void taskFour(){try { Thread.sleep(1000);} catch (InterruptedException e) { e.printStackTrace();} }}

現(xiàn)在application的onCreate方法中有幾個(gè)任務(wù),各個(gè)耗時(shí)是不一樣的。可能很多同學(xué)就會說了,異步處理啊,開線程,放到線程池中,或者創(chuàng)建一個(gè)IntentService來執(zhí)行。那么我們就要考慮幾個(gè)問題了

第一:異步處理,如果在某個(gè)頁面需要用到某個(gè)SDK,但是又沒有初始化完成呢?

第二:假如說taskTwo需要taskOne的某個(gè)返回值呢?怎么保證taskOne在taskTwo之前執(zhí)行完畢呢?

第三:開線程,開多少個(gè)線程呢?多了會造成資源浪費(fèi),少了資源又沒有合理的利用。

我個(gè)人覺得針對于啟動優(yōu)化,在Application中的onCreate()進(jìn)行初始化任務(wù)操作,我們首先需要對這些任務(wù)進(jìn)行一個(gè)優(yōu)先級劃分,針對于那些優(yōu)先級高的任務(wù),我們可以優(yōu)先進(jìn)行處理,對于那些優(yōu)先級較低的,我們可以適當(dāng)?shù)难舆t進(jìn)行加載。

其次有很多同學(xué)喜歡把那些優(yōu)先級較低的任務(wù)進(jìn)行延遲加載,比如new Handler().postDelayed(),這種我覺得是非常不可取的,假如說放在postDelayed中的任務(wù)耗時(shí)2s,延遲1s進(jìn)行處理,那么在執(zhí)行2s任務(wù)的過程中,有用戶進(jìn)行操作,那豈不是很卡嗎,很明顯,這是指標(biāo)不治本的。

7.1 啟動器的思想

針對上面說的幾個(gè)痛點(diǎn),怎么在處理上面的幾個(gè)痛點(diǎn),又能保證代碼的可維護(hù)性呢?換句話說就是一個(gè)新人不需要理解整個(gè)過程,直接就可以開干呢?那么,啟動器來了。

啟動器核心思想:充分利用CPU多核,自動梳理任務(wù)順序

7.2 啟動器的原理

1、任務(wù)全部封裝成Task對象,傳入到集合中。

2、根據(jù)所有的任務(wù)依賴關(guān)系,形成一個(gè)有向無環(huán)圖,然后通過拓?fù)渑判蚺帕谐鋈蝿?wù)的執(zhí)行流程

3、通過CountDownLatch來控制某一個(gè)任務(wù)是否執(zhí)行完畢才進(jìn)行下一步。

4、線程池創(chuàng)建核心線程的數(shù)量,由手機(jī)的核數(shù)量決定的。

7.3啟動器使用方式

詳解Android性能優(yōu)化之啟動優(yōu)化

7.4啟動器核心代碼

詳解Android性能優(yōu)化之啟動優(yōu)化

進(jìn)行任務(wù)排序

package com.noahedu.launchertool.launchstarter.sort;import com.noahedu.launchertool.launchstarter.task.Task;import com.noahedu.launchertool.launchstarter.utils.DispatcherLog;import java.util.ArrayList;import java.util.List;import java.util.Set;import androidx.annotation.NonNull;import androidx.collection.ArraySet;public class TaskSortUtil { private static List<Task> sNewTasksHigh = new ArrayList<>();// 高優(yōu)先級的Task /** * 任務(wù)的有向無環(huán)圖的拓?fù)渑判? * * @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<>();// 需要提升自己優(yōu)先級的,先執(zhí)行(這個(gè)先是相對于沒有依賴的先)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);} }}// 順序:被別人依賴的————》需要提升自己優(yōu)先級的————》需要被等待的————》沒有依賴的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; } /** * 獲取任務(wù)在任務(wù)列表中的index * * @param originTasks * @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;}// 僅僅是保護(hù)性代碼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; }}

執(zhí)行任務(wù)代碼

package com.noahedu.launchertool.launchstarter.task;import android.os.Looper;import android.os.Process;import com.noahedu.launchertool.launchstarter.TaskDispatcher;import com.noahedu.launchertool.launchstarter.stat.TaskStat;import com.noahedu.launchertool.launchstarter.utils.DispatcherLog;/** * 任務(wù)真正執(zhí)行的地方 */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() {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();// 執(zhí)行TaskmTask.setRunning(true);mTask.run();// 執(zhí)行Task的尾部任務(wù)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');} } /** * 打印出來Task執(zhí)行的日志 * * @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() );} }}

基本核心代碼就是上面這幾個(gè),完整的代碼會在后面的demo給出

8、其他優(yōu)化方案8.1 對延遲任務(wù)進(jìn)行分批初始化

使用IdleHandler特性,進(jìn)行空閑執(zhí)行 (適合優(yōu)先級不是很高,不急于初始化的第三方SDK)

IdleHandler:IdleHandler 可以用來提升性能,主要用在我們希望能夠在當(dāng)前線程消息隊(duì)列空閑時(shí)做些事情(譬如 UI 線程在顯示完成后,如果線程空閑我們就可以提前準(zhǔn)備其他內(nèi)容)的情況下,不過最好不要做耗時(shí)操作。簡單來說就是,looper對象有空的時(shí)候就會執(zhí)行IdleHandler中的任務(wù)。

前面說過,在application的任務(wù)進(jìn)行優(yōu)先級劃分,那么如果優(yōu)先級低的任務(wù),我們是不是可以不再application的onCreate中進(jìn)行初始化呢?是否可以放到activity中進(jìn)行呢?如果可以在activity中,那么在activity那個(gè)階段適合呢?其實(shí)在onCreate(),onResume()都是可以的。當(dāng)然我們也可以使用view中的getViewTreeObserver().addOnPreDrawListener進(jìn)行監(jiān)聽,這個(gè)事件是視圖將要繪制的時(shí)候會回調(diào)該方法。我們可以在回調(diào)的方法中將要初始化的SDK放大IdleHandler中。

8.2 提前加載SP 可以放到multidex之前加載,利用此階段的CPU

SharedPreferences,以鍵值對的形式進(jìn)行數(shù)據(jù)的保存的,會一次性加載到內(nèi)存中,所以我們可以考慮那個(gè)階段的CPU相對來說比較空閑。如可以放到multidex之前加載,充分利用此階段的CPU進(jìn)行

demo地址:[https://github.com/343661629/startOptimization]

以上就是詳解Android性能優(yōu)化之啟動優(yōu)化的詳細(xì)內(nèi)容,更多關(guān)于Android性能優(yōu)化之啟動優(yōu)化的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Android
相關(guān)文章:
主站蜘蛛池模板: 皮带式输送机械|链板式输送机|不锈钢输送机|网带输送机械设备——青岛鸿儒机械有限公司 | 经济师考试_2025中级经济师报名时间_报名入口_考试时间_华课网校经济师培训网站 | 智慧旅游_智慧景区_微景通-智慧旅游景区解决方案提供商 | 上海单片机培训|重庆曙海培训分支机构—CortexM3+uC/OS培训班,北京linux培训,Windows驱动开发培训|上海IC版图设计,西安linux培训,北京汽车电子EMC培训,ARM培训,MTK培训,Android培训 | 重庆小面培训_重庆小面技术培训学习班哪家好【终身免费复学】 | 打孔器,打孔钳厂家【温州新星德牌五金工具】| 安徽泰科检测科技有限公司【官方网站】 | 不锈钢监控杆_监控立杆厂家-廊坊耀星光电科技有限公司 | 圆形振动筛_圆筛_旋振筛_三次元振动筛-河南新乡德诚生产厂家 | 河北中仪伟创试验仪器有限公司是专业生产沥青,土工,水泥,混凝土等试验仪器的厂家,咨询电话:13373070969 | 多功能三相相位伏安表-变压器短路阻抗测试仪-上海妙定电气 | 10吨无线拉力计-2吨拉力计价格-上海佳宜电子科技有限公司 | 苏州同创电子有限公司 - 四探针测试仪源头厂家 | 干法制粒机_智能干法制粒机_张家港市开创机械制造有限公司 | 企业微信scrm管理系统_客户关系管理平台_私域流量运营工具_CRM、ERP、OA软件-腾辉网络 | 温州中研白癜风专科_温州治疗白癜风_温州治疗白癜风医院哪家好_温州哪里治疗白癜风 | ★店家乐|服装销售管理软件|服装店收银系统|内衣店鞋店进销存软件|连锁店管理软件|收银软件手机版|会员管理系统-手机版,云版,App | 钢托盘,钢制托盘,立库钢托盘,金属托盘制造商_南京飞天金属制品实业有限公司 | 防水套管-柔性防水套管-刚性防水套管-上海执品管件有限公司 | 重庆小面培训_重庆小面技术培训学习班哪家好【终身免费复学】 | 齿轮减速机_齿轮减速电机-VEMT蜗轮蜗杆减速机马达生产厂家瓦玛特传动瑞环机电 | 钢托盘,钢制托盘,立库钢托盘,金属托盘制造商_南京飞天金属制品实业有限公司 | 卫浴散热器,卫浴暖气片,卫生间背篓暖气片,华圣格浴室暖气片 | 企业彩铃制作_移动、联通、电信集团彩铃上传开通_彩铃定制_商务彩铃管理平台-集团彩铃网 | 品牌策划-品牌设计-济南之式传媒广告有限公司官网-提供品牌整合丨影视创意丨公关活动丨数字营销丨自媒体运营丨数字营销 | 注塑模具_塑料模具_塑胶模具_范仕达【官网】_东莞模具设计与制造加工厂家 | 膜片万向弹性联轴器-冲压铸造模具「沧州昌运模具」 | 厂房出租_厂房出售_产业园区招商_工业地产&nbsp;-&nbsp;中工招商网 | 骨龄仪_骨龄检测仪_儿童骨龄测试仪_品牌生产厂家【品源医疗】 | 阻垢剂,反渗透阻垢剂,缓蚀阻垢剂-山东普尼奥水处理科技有限公司 真空粉体取样阀,电动楔式闸阀,电动针型阀-耐苛尔(上海)自动化仪表有限公司 | 雄松华章(广州华章MBA)官网-专注MBA/MPA/MPAcc/MEM辅导培训 | 自清洗过滤器,浅层砂过滤器,叠片过滤器厂家-新乡市宇清净化 | 【化妆品备案】进口化妆品备案流程-深圳美尚美化妆品有限公司 | 过滤器_自清洗过滤器_气体过滤器_苏州华凯过滤技术有限公司 | 药品冷藏箱厂家_低温冰箱_洁净工作台-济南欧莱博电子商务有限公司官网 | 首页_欧瑞传动官方网站--主营变频器、伺服系统、新能源、软起动器、PLC、HMI | 红酒招商加盟-葡萄酒加盟-进口红酒代理-青岛枞木酒业有限公司 | 分类168信息网 - 分类信息网 免费发布与查询 | 校园文化空间设计-数字化|中医文化空间设计-党建|法治廉政主题文化空间施工-山东锐尚文化传播公司 | 拖链电缆_柔性电缆_伺服电缆_坦克链电缆-深圳市顺电工业电缆有限公司 | 天坛家具官网 |