Android性能優(yōu)化 - 消除卡頓

性能優(yōu)化系列閱讀

消除卡頓

  • 什么是卡頓及卡頓的衡量標(biāo)準(zhǔn)
  • 產(chǎn)生卡頓的原因
  • 通用優(yōu)化流程
  • 定位卡頓原因

什么是卡頓

卡頓是人的一種視覺感受,比如我們滑動(dòng)界面時(shí),如果滑動(dòng)不流程我們就會(huì)有卡頓的感覺,這種感覺我們需要有一個(gè)量化指標(biāo),在編程時(shí)如果開發(fā)的程序超過了這個(gè)指標(biāo)我們認(rèn)為其是卡頓的。

FPS(幀率):每秒顯示幀數(shù)(Frames per Second)。表示圖形處理器每秒鐘能夠更新的次數(shù)。高的幀率可以得到更流暢、更逼真的動(dòng)畫。一般來說12fps大概類似手動(dòng)快速翻動(dòng)書籍的幀率,這明顯是可以感知到不夠順滑的。30fps就是可以接受的,但是無法順暢表現(xiàn)絢麗的畫面內(nèi)容。提升至60fps則可以明顯提升交互感和逼真感,但是一般來說超過75fps就不容易察覺到有明顯的流暢度提升了,如果是VR設(shè)備需要高于75fps,才可能消除眩暈的感覺。

開發(fā)app的性能目標(biāo)就是保持60fps,這意味著每一幀你只有16ms≈1000/60的時(shí)間來處理所有的任務(wù)。Android系統(tǒng)每隔16ms發(fā)出VSYNC信號(hào),觸發(fā)對(duì)UI進(jìn)行渲染,如果每次渲染都成功,這樣就能夠達(dá)到流暢的畫面所需要的60fps。

性能優(yōu)化

如果你的某個(gè)操作花費(fèi)時(shí)間是24ms,系統(tǒng)在得到VSYNC信號(hào)的時(shí)候就無法進(jìn)行正常渲染,這樣就發(fā)生了丟幀現(xiàn)象。那么用戶在32ms內(nèi)看到的會(huì)是同一幀畫面。

性能優(yōu)化

如果此時(shí)用戶在看動(dòng)畫的執(zhí)行或者滾動(dòng)屏幕(如RecyclerView),就會(huì)感覺到界面不流暢了(卡了一下)。丟幀導(dǎo)致卡頓產(chǎn)生。

流暢的情況下:

性能優(yōu)化

出現(xiàn)了丟幀現(xiàn)象(卡頓)

性能優(yōu)化

嚴(yán)重丟幀(卡死了)

性能優(yōu)化

給我們一種感覺,如果幀率越低,卡頓就越嚴(yán)重,那么是不是就可以使用幀率來衡量卡頓那?

如何衡量卡頓

FPS的高低不能準(zhǔn)確的反映應(yīng)用的流程度。如下圖所示,只有有更新的時(shí)候才刷新界面。

性能優(yōu)化

當(dāng)界面沒有變動(dòng)的時(shí)候,手機(jī)不需要對(duì)界面進(jìn)行更新,所以此時(shí)的FPS會(huì)很低,如果1秒鐘內(nèi)都沒有變動(dòng)那么FPS=0。所以我們需要利用其他方式來衡量應(yīng)用的流程度,比如可以利用丟幀數(shù)來衡量。

單位時(shí)間內(nèi)丟幀數(shù)可以反映出應(yīng)用是否流程。不丟幀是終極目標(biāo),但每秒丟幀在6-7幀左右可以接受,如果丟10幀以上就需要優(yōu)化了。

丟幀情況(單位時(shí)間內(nèi)均勻分布) 卡頓情況
0-10幀 流暢
10-20幀 較卡
20-40幀 很卡
40-60幀 卡死了

對(duì)于我們開發(fā)人員來說,會(huì)使用一些工具找出卡頓比較集中的地方,找出原因,消除或減弱卡頓。(測(cè)試團(tuán)隊(duì)會(huì)有專門的工具去測(cè)試丟幀的情況)

卡頓產(chǎn)生的原因

核心:分析在16ms中我們的應(yīng)用做了什么工作,那些工作阻止我們?cè)?6ms時(shí)更新界面。

通常情況下,在16ms中我們有那些工作需要處理。

單以XML布局被繪制出來為例進(jìn)行說明。

處理過程:

  1. CPU負(fù)責(zé)把UI組件計(jì)算成多邊形和紋理
  2. OpenGL負(fù)責(zé)繪制圖像(Display List)
  3. GPU柵格化需要顯示內(nèi)容并渲染到屏幕上

而實(shí)際開發(fā)中我們還加入交互、業(yè)務(wù)處理等工作,這些工作都需要在16ms中處理完成。對(duì)于開發(fā)人員來說,需要有一個(gè)工具,很直觀的幫助我們判斷出那些工作占用了多少時(shí)間。

Profile GPU Rendering

通過手機(jī)開發(fā)者選項(xiàng)中提供的Profile GPU Rendering(GPU呈現(xiàn)模式分析)功能,我們可以清楚的看到處理流程中各部分的耗時(shí)。手機(jī)端工具(開發(fā)助手àGPU渲染圖)。建議大家在Android6.0及以上手機(jī)測(cè)試。

打開Profile GPU Rendering操作截圖如下:

性能優(yōu)化

1495598567075

性能優(yōu)化

大家可以拿著真機(jī)配置一下??纯从惺裁醋兓?/p>

性能優(yōu)化

1495598656043

條形圖說明

  1. 水平方向的一根綠線代表16ms。
  2. 每條都代表一幀畫面所有工作內(nèi)容
  3. 每條中不同的顏色代表不同的工作內(nèi)容

Android6.0及以上的手機(jī)顏色對(duì)應(yīng)關(guān)系如下:

性能優(yōu)化

原因分析

通用優(yōu)化流程

第一步:UI層優(yōu)化

1、UI問題比較容易查找
2、一旦出現(xiàn)問題影響范圍廣(xml、mesure、layout、draw、Display List 、柵格化……)

工具:設(shè)備過渡繪制查看功能、HierarchyViewer等

常見問題:過渡繪制、布局復(fù)雜、層級(jí)過深……

過渡繪制

在屏幕一個(gè)像素上繪制多次(超過兩次)。如:文本框,如果設(shè)置了背景顏色(黑色),那么顯示的文字(白色)就需要在背景之上再次繪制。

打開手機(jī)開發(fā)者中的過渡繪制區(qū)域即可查看。藍(lán)色標(biāo)識(shí)這個(gè)區(qū)域繪制了兩次。

性能優(yōu)化

說明:

  1. 如果大面積都是藍(lán)色,屬于正常情況。
  2. 重點(diǎn)關(guān)注大面積綠色及以后的,表示存在過渡繪制。

設(shè)備中的該選項(xiàng)只能直觀的讓我們感受到應(yīng)用的界面是否存在過渡繪制,如果存在,我們需要利用Hierarchy Viewer查找布局中不合理的地方。

過渡繪制小案例

效果圖如下

性能優(yōu)化

大面積存在過渡繪制,文字區(qū)域最嚴(yán)重。查詢Item布局文件找出過渡繪制的原因

性能優(yōu)化

自定義控件繪制優(yōu)化

Clip Rect 與 Quick Reject

  • Clip Rect:識(shí)別可見區(qū)域
  • Quick Reject:控件所在的矩形區(qū)域是否有交集

在Canvas中有上述兩個(gè)方法,幫助我們進(jìn)行判斷,避免出現(xiàn)過渡繪制。

我們可以通過canvas.clipRect()來幫助系統(tǒng)識(shí)別那些可見的區(qū)域,在這個(gè)區(qū)域之外的我們不在進(jìn)行繪制。如側(cè)拉菜單,當(dāng)菜單顯示的時(shí)候被菜單遮擋的部分是不用進(jìn)行繪制的,一旦繪制就會(huì)出現(xiàn)過渡繪制現(xiàn)象。系統(tǒng)的控件會(huì)控制過渡繪制,但我們自己的控件就需要自行管理了。所以在使用側(cè)拉菜單時(shí)就需要優(yōu)先考慮系統(tǒng)提供的了。

性能優(yōu)化

如果系統(tǒng)沒有提供的,我們自己編寫時(shí)也需要注意,避免出現(xiàn)過渡繪制。

自定義控件過渡繪制

案例效果

性能優(yōu)化

編寫自定義控件MyVIew,在布局中引入該控件

<com.itheima.overdrawdemo.MyView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent">
</com.itheima.overdrawdemo.MyView>

圖片資源數(shù)組

private int[] ids = new int[]{R.drawable.img1, R.drawable.img2, R.drawable.img3, R.drawable.img4, R.drawable.img5, R.drawable.img6};

初始化時(shí)加載圖片資源,同時(shí)對(duì)一會(huì)需要使用到的畫筆做初始化

private void init() {
    for (int i = 0; i < 6; i++) {
        imgs[i] = BitmapFactory.decodeResource(getResources(), ids[i]);
    }

    paint=new Paint();
    paint.setAntiAlias(true);
}

先將圖片擺放好

for (int i = 0; i < imgs.length; i++) {
    canvas.drawBitmap(imgs[i],i*20,0,paint);
}

通過過渡繪制的開啟,觀察結(jié)果

性能優(yōu)化

原因比較簡單,對(duì)于“大王”這張牌來說,我們不需要繪制完整的圖片,如果都繪制了就會(huì)出現(xiàn)上面的情況

處理思路:找出牌需要繪制的區(qū)域,讓canvas在繪制這張牌時(shí)僅僅按區(qū)域繪制一部分即可。對(duì)于“大王”這張牌來說我們僅僅繪制如下內(nèi)容

性能優(yōu)化

重點(diǎn)來了,我們?cè)撊绾蝿澏ㄟ@個(gè)區(qū)域? 在Canvas中clipRect方法可以幫助我們劃定一個(gè)區(qū)域,進(jìn)行繪制。

方法參數(shù)說明:

clipRect(int left, int top, int right, int bottom) 

canvas.clipRect(0, 0, 20, imgs[i].getHeight()); 

設(shè)置完成后,我們來繪制大王這張牌。

canvas.drawBitmap(imgs[0],0,0,paint);

再增加循環(huán),快速繪制所有的牌。

for (int i = 0; i < imgs.length; i++) {
    canvas.clipRect(i * 20, 0, (i + 1) * 20, imgs[i].getHeight());
    canvas.drawBitmap(imgs[i],i*20,0,paint);
}

大家會(huì)發(fā)現(xiàn)繪制完成的結(jié)果不是我們想要的。

我們需要借助save和restore來完成裁剪的操作。

save:用來保存Canvas的狀態(tài)。save之后,可以調(diào)用Canvas的平移、放縮、旋轉(zhuǎn)、錯(cuò)切、裁剪等操作。

restore:用來恢復(fù)Canvas之前保存的狀態(tài)。防止save后對(duì)Canvas執(zhí)行的操作對(duì)后續(xù)的繪制有影響。

save和restore要配對(duì)使用(restore可以比save少,但不能多),如果restore調(diào)用次數(shù)比save多,會(huì)引發(fā)Error。save和restore之間,往往夾雜的是對(duì)Canvas的特殊操作

代碼修改如下

for (int i = 0; i < imgs.length; i++) {
    canvas.save();
    canvas.clipRect(i * 20, 0, (i + 1) * 20, imgs[i].getHeight());
    canvas.drawBitmap(imgs[i],i*20,0,paint);
    canvas.restore();
}

效果如下

性能優(yōu)化

剩下最后一個(gè)工作,把最上面的牌繪制完整

for (int i = 0; i < imgs.length; i++) {
    canvas.save();
    if(i<imgs.length-1) {
        canvas.clipRect(i * 20, 0, (i + 1) * 20, imgs[i].getHeight());
    }else if(i==imgs.length-1){
        canvas.clipRect(i * 20, 0, i * 20+imgs[i].getWidth(), imgs[i].getHeight());
    }
    canvas.drawBitmap(imgs[i],i*20,0,paint);
    canvas.restore();
}

Hierarchy Viewer(層級(jí)查看器)工具使用

Hierarchy Viewer可以很直接的呈現(xiàn)布局的層次關(guān)系,視圖組件的各種屬性。我們可以通過紅,黃,綠三種不同的顏色來區(qū)分布局的Measure,Layout,Executive的相對(duì)性能表現(xiàn)如何。

打開工具

性能優(yōu)化

選擇需要查看的內(nèi)容

性能優(yōu)化

查看各個(gè)節(jié)點(diǎn)Measure,Layout,Executive

性能優(yōu)化

三個(gè)小圓點(diǎn), 依次表示Measure, Layout,Draw, 可以理解為對(duì)應(yīng)View的onMeasure, onLayout, onDraw三個(gè)方法

  • 綠色:表示該View的此項(xiàng)性能比該View Tree中超過50%的View都要快

  • 黃色:表示該View的此項(xiàng)性能比該View Tree中超過50%的View都要慢

  • 紅色:表示該View的此項(xiàng)性能是View Tree中最慢的

一般來說

  • Measure紅點(diǎn), 可能是布局中嵌套R(shí)elativeLayout, 或是嵌套LinearLayout都使用了weight屬性.

  • Layout紅點(diǎn), 可能是布局層級(jí)太深

  • Draw紅點(diǎn), 可能是自定義View的繪制有問題, 復(fù)雜計(jì)算等

我們之前的小案例,可以進(jìn)行層級(jí)優(yōu)化

常規(guī)做法

  1. 沒有用的父布局——沒有背景繪制或沒有大小限制的父布局,不會(huì)對(duì)界面效果產(chǎn)生任何影響。特別是進(jìn)來的布局,很容易產(chǎn)生問題??梢酝ㄟ^標(biāo)簽替代。

  2. 在布局層次一樣的情況下,建議使用LinearLayout代替RelativeLayout。

  3. 使用LinearLayout導(dǎo)致的層次變深,可以使用RelativeLayout進(jìn)行替換。同樣的界面我們可以使用不同的方式去實(shí)現(xiàn),選擇一個(gè)層級(jí)最少的方案。

  4. 不常用的UI被設(shè)置成了GONE,嘗試使用代替。

  5. 去掉多余的背景顏色,減少過渡繪制,對(duì)于有多層背景色的布局來說,留最上面的一層即可。謹(jǐn)慎使用alpha,如果后渲染的元素有設(shè)置alpha值,那么這個(gè)元素就會(huì)和屏幕上已經(jīng)渲染好的元素做blend處理,這樣會(huì)導(dǎo)致不少性能問題,特別是出現(xiàn)在列表的Item中。

  6. 對(duì)于使用Selector當(dāng)背景的布局,可以將normal狀態(tài)的color設(shè)置為透明。

  7. 我們不能因?yàn)樘岣咝阅芏雎粤私缑嫘枰_(dá)到的效果(平衡Design與Performance)

第二步:代碼問題查找

工具:Lint

常見問題:我們重點(diǎn)關(guān)注Performance和Xml中的一些建議

  • 在繪制時(shí)實(shí)例化對(duì)象(onDraw)
  • 手機(jī)不能進(jìn)入休眠狀態(tài)(Wake lock)
  • 資源忘記回收
  • Handler使用不當(dāng)?shù)怪脙?nèi)存泄漏
  • 沒有使用SparseArray代替HashMap
  • 未被使用的資源
  • 布局中無用的參數(shù)
  • 可優(yōu)化布局(如:ImageView與TextView的組合是否可以使用TextView獨(dú)立完成)
  • 效率低下的 無用的命名空間等

Lint工具使用

Android Studio中開啟Lint工具

選中需要分析的Module,點(diǎn)擊工具欄中Analyze中的Inspect Code選項(xiàng)。

性能優(yōu)化

選擇需要分析的Module或整個(gè)項(xiàng)目

性能優(yōu)化

我們可以逐一閱讀一下,但是重點(diǎn)關(guān)注性能問題,xml中的一些問題也盡可能進(jìn)行修復(fù)。

性能優(yōu)化

問題處理

1、案例中性能問題處理

性能優(yōu)化

其他的一些性能問題

性能優(yōu)化

建議使用concate方法進(jìn)行連接字符串,會(huì)比append的方式性能好。

2、案例中xml提到的內(nèi)容如下

性能優(yōu)化

其他問題:

無效的命名空間

性能優(yōu)化

無效的布局參數(shù)

比如在線性布局中的控件使用到了相對(duì)布局中的屬性,運(yùn)行時(shí)需要處理,影響代碼的執(zhí)行效率。

3、案例中關(guān)于定義聲明變量的警告

性能優(yōu)化

意見或建議

  • 不斷關(guān)注Lint中提到的問題,將公司中命名規(guī)范中沒有提到的內(nèi)容逐一補(bǔ)全。
  • Lint不是萬能的。

第三步:優(yōu)化App的邏輯層

工具:Traceview

常見問題:主線程耗時(shí)大的函數(shù)、滑動(dòng)過程中的CPU工作問題,工具可以提供每個(gè)函數(shù)的耗時(shí)和調(diào)用次數(shù),我們重點(diǎn)關(guān)注兩種類型的函數(shù):

  1. 主線程里占用CUP時(shí)間很長的函數(shù),特別關(guān)注IO操作(文件IO、網(wǎng)絡(luò)IO、數(shù)據(jù)庫操作等)
  2. 主線程調(diào)用次數(shù)多的函數(shù)

使用Traceview找出卡住主線程的地方

Traceview工具使用

通過Android Studio打開里面的Android Device Monitor,切換到DDMS窗口,點(diǎn)擊左邊欄上面想要跟蹤的進(jìn)程,再點(diǎn)擊上面的Start Method Profiling的按鈕,如下圖所示:

性能優(yōu)化

啟動(dòng)跟蹤之后,再操控app,做一些你想要跟蹤的事件,例如滑動(dòng)RecyclerView,點(diǎn)擊某些視圖進(jìn)入另外一個(gè)頁面等等。操作完之后,回到Android Device Monitor,再次點(diǎn)擊相同的按鈕停止跟蹤。此時(shí)工具會(huì)為剛才的操作生成TraceView的詳細(xì)視圖。

性能優(yōu)化

重點(diǎn)關(guān)注Incl Cpu Time、Call+Recur Calls/Total、Real Time/Call

通過降序排序,我們可以分別找到這兩列中數(shù)值比較大的內(nèi)容。

指標(biāo)說明:

指標(biāo) 說明
Incl(Inclusive) Cpu Time 方法本身和其調(diào)用的所有子方法占用CPU時(shí)間
Excl(Exclusive) Cpu Time 方法本身占用CPU時(shí)間
Incl Real Time 方法(包含子方法)開始到結(jié)束用時(shí)
Excl Real Time 方法本身開始到結(jié)束用時(shí)
Call + Recursion Calls/Total 方法被調(diào)用次數(shù) + 方法被遞歸調(diào)用次數(shù)
Cpu Time/Call 方法調(diào)用一次占用CPU時(shí)間. 方法實(shí)際執(zhí)行時(shí)間(不包括io等待時(shí)間)
Real Time/Call 方法調(diào)用一次實(shí)際執(zhí)行時(shí)間. 方法開始結(jié)束時(shí)間差(包括等待時(shí)間)

小案例:

我們可以在ViewHolder的設(shè)置數(shù)據(jù)中做點(diǎn)手腳,比如睡幾毫秒(8ms),通過監(jiān)控滾動(dòng),我們是否可以定位到問題代碼。

好的做法

  1. 不要阻塞UI線程,占用CUP較多的工作盡可能放在子線程中執(zhí)行。
  2. 需要結(jié)合使用場(chǎng)景選擇不同的線程處理方案

AsyncTask:為UI線程與工作線程之間進(jìn)行快速的切換提供一種簡單便捷的機(jī)制。適用于當(dāng)下立即需要啟動(dòng),但是異步執(zhí)行的生命周期短暫的使用場(chǎng)景。

HandlerThread: 為某些回調(diào)方法或者等待某些任務(wù)的執(zhí)行設(shè)置一個(gè)專屬的線程,并提供線程任務(wù)的調(diào)度機(jī)制。

ThreadPool: 把任務(wù)分解成不同的單元,分發(fā)到各個(gè)不同的線程上,進(jìn)行同時(shí)并發(fā)處理。

IntentService: 適合于執(zhí)行由UI觸發(fā)的后臺(tái)Service任務(wù),并可以把后臺(tái)任務(wù)執(zhí)行的情況通過一定的機(jī)制反饋給UI。

如果大量操作數(shù)據(jù)庫數(shù)據(jù)時(shí)建議使用批處理操作。如:批量添加數(shù)據(jù)。

綜合案例

應(yīng)用啟動(dòng)性能優(yōu)化。

使用NoHttp獲取應(yīng)用列表

問題表現(xiàn):通常從用戶點(diǎn)擊到應(yīng)用完全展示完首頁,需要用戶等待一段時(shí)間。我們?nèi)绾慰s短時(shí)間并提高用戶體驗(yàn)。

分析:應(yīng)用在啟動(dòng)的過程中我們的代碼能夠影響啟動(dòng)速度的地方如下

  • Application的onCreate
  • 首屏Activity的渲染

步驟:

  1. 利用Traceview工具觀察啟動(dòng)過程方法耗時(shí)情況,重點(diǎn)關(guān)注onCreate方法(自定義Application和首頁Activity)。問題:Traceview工具如何在應(yīng)用啟動(dòng)時(shí)監(jiān)控?cái)?shù)據(jù)?

  2. 分析自定義Application耗時(shí)操作,判斷onCreate方法中的內(nèi)容(如:第三方的工具是否可以不占用主線程進(jìn)行初始化)。

  3. 查看界面是否存在過渡繪制。

  4. 利用Hierarchy Viewer工具查看界面需要優(yōu)化的點(diǎn)。

  5. 啟動(dòng)過程中的白屏優(yōu)化。

第一步:觀察耗時(shí)情況

1、在onCreate開始和結(jié)尾打上trace

Debug.startMethodTracing("POApp");
Debug.stopMethodTracing();

運(yùn)行程序, 會(huì)在sdcard上生成一個(gè)"POApp.trace"的文件.

注意:需要給程序加上寫存儲(chǔ)的權(quán)限

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Android6.0以后的模擬器需要為應(yīng)用打開讀寫權(quán)限

2、通過adb pull將其導(dǎo)出到本地

adb pull /sdcard/ POApp.trace 存放文件路徑

第二步:分析數(shù)據(jù)

通過DDMS的FileàOpen File

性能優(yōu)化

查詢結(jié)果如下

性能優(yōu)化

說明:我們使用Real Time/Call進(jìn)行排序可以得到上圖內(nèi)容

大家可以發(fā)現(xiàn)在Application中阻塞主線程干的工作都是NoHttp的初始化工作。為了提高應(yīng)用的啟動(dòng)速度,我們可以將這個(gè)工作放到子線程中完成,通常我們會(huì)使用IntentService來處理這個(gè)工作。

代碼如下:

/**
 * Created by itheima.
 */

public class MyApplication extends Application {
    @Override
    public void onCreate() {

        // 如果沒有辦法手動(dòng)操作監(jiān)控,可以使用如下代碼重點(diǎn)關(guān)注我們感興趣的方法
        // 監(jiān)控的結(jié)果會(huì)生成文件存儲(chǔ)SDCard上
//        Debug.startMethodTracing("AppStartupDemo");// 文件的名稱
        super.onCreate();

        InitService.start(this);

        SystemClock.sleep(1000);

//        Debug.stopMethodTracing();
    }
}
/**
 * 將MyApplication中onCreate方法內(nèi)容耗時(shí)的初始化工作移動(dòng)到該類中
 */

public class InitService extends IntentService {

    // 問題:由于將NoHttp的初始化工作移動(dòng)到了子線程,當(dāng)主線程使用NoHttp發(fā)現(xiàn)沒有初始化完成,報(bào)異常了。

    // 方案一:使用boolean值進(jìn)行初始化工作的標(biāo)記,如果完成boolean為true,可以在使用該工具的地方每隔一個(gè)時(shí)間段判斷一下。
    // 方案二:當(dāng)初始化工作完成后,發(fā)出一個(gè)通知,如果有觀察者,則進(jìn)行后續(xù)工作的處理

    public static boolean isInit=false;// 標(biāo)記是否初始化完成

    public InitService() {
        super("init");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        // 耗時(shí)操作

        Logger.setTag("NoHttp");
        Logger.setDebug(true);

        NoHttp.initialize(this, new NoHttp.Config()
                .setConnectTimeout(30 * 1000)
                .setReadTimeout(30 * 1000)
        );

        isInit=true;
    }

    /**
     * 啟動(dòng)service
     * @param myApplication
     */
    public static void start(MyApplication myApplication) {
        Intent intent = new Intent(myApplication, InitService.class);
        myApplication.startService(intent);
    }
}

修改完成后,會(huì)引發(fā)一個(gè)問題,及在首頁訪問網(wǎng)絡(luò)時(shí),由于NoHttp的初始化還沒有完成會(huì)報(bào)出如下異常:

性能優(yōu)化

如果我們?cè)谑醉摼托枰⒓丛L問網(wǎng)絡(luò),就需要對(duì)初始化進(jìn)行監(jiān)控,可以簡單的使用一個(gè)boolean值,進(jìn)行判斷,當(dāng)初始化完成后boolean值修改為true。我們?cè)贛ainActivity中可以使用Handler間隔一段時(shí)間就檢查一下boolean即可。

第三步:過渡繪制

進(jìn)入首頁后,應(yīng)用的啟動(dòng)速度限制就集中在首頁的界面渲染上了。因此我們開始對(duì)界面進(jìn)行優(yōu)化處理。

過渡繪制查看結(jié)果。

性能優(yōu)化

表現(xiàn)還好,我們可以檢查一下Item,看看是否可以優(yōu)化掉一次繪制。

性能優(yōu)化

第四步:優(yōu)化界面布局

Hierarchy Viewer工具派上用場(chǎng)了,我們可以檢查一下布局是否合理。

性能優(yōu)化

重點(diǎn)觀察其中一個(gè)條目

性能優(yōu)化

優(yōu)化完成后的結(jié)構(gòu)圖

我們先優(yōu)化掉兩個(gè)用處不大的LinearLayout,然后在考慮是否可以繼續(xù)優(yōu)化掉條目中的LinearLayout。

性能優(yōu)化

第五步:Launch screens設(shè)置

兩種處理方案:

方案一:設(shè)置一個(gè)背景圖

<item name="android:windowBackground">@drawable/splash</item>
<item name="android:windowNoTitle">true</item>

注意:當(dāng)界面加載完成后需要將背景改成白色。

方案二:設(shè)置成透明的界面,制造延時(shí)啟動(dòng)效果

<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>

AndroidPerformanceMonitor

GitHub BlockCanary — 輕松找出Android App界面卡頓元兇

BlockCanary是一個(gè)Android平臺(tái)的一個(gè)非侵入式的性能監(jiān)控組件,應(yīng)用只需要實(shí)現(xiàn)一個(gè)抽象類,提供一些該組件需要的上下文環(huán)境,就可以在平時(shí)使用應(yīng)用的時(shí)候檢測(cè)主線程上的各種卡慢問題,并通過組件提供的各種信息分析出原因并進(jìn)行修復(fù)。

取名為BlockCanary則是為了向LeakCanary致敬,順便本庫的UI部分是從LeakCanary改來的,之后可能會(huì)做一些調(diào)整。

性能優(yōu)化
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容