1. 程式人生 > >[譯]Android UI 效能優化

[譯]Android UI 效能優化

。水平有限,翻譯不妥之處請多多指教。

UI渲染是指從App生成幀並顯示在螢幕上的行為。為了保證使用者體驗的流暢性,App需要在16ms內渲染完一幀以達到60fps的幀率(為什麼是60fps?)。如果你的App UI渲染緩慢,那麼系統會強制跳過某些幀,使用者就會感知到介面的“口吃”,也就是卡頓。

(以下三段Google的廣告)為了幫助開發者提高App的質量,Android自動監控了App的卡頓並且把資訊展示在Android Vitals dashboard上。如果對這些資訊是如何收集的感興趣,參考 Play Console docs

如果你的app有卡頓的情況,這個頁面提供了診斷和解決問題的指引。

PS:Android Vitals dashboard 和Android系統記錄了使用UI Toolkit(App中從Canvas和View繼承樹繪製出來的對使用者可見的部分)的渲染時間統計。如果你的App沒有使用UI Toolkit,比如有的app使用Vulkan

UnityUnreal或者 OpenGL,那麼在Android Vitals dashboard中是無法看到渲染時間統計的,可以通過adb shell dumpsys gfxinfo <package name>來確定裝置是否對某個app記錄了這些資訊。

定位卡頓

精準地定位App中引起卡頓問題的程式碼是非常困難的,本小結介紹一些定位卡頓問題的方法:

  • 直觀分析
  • Systrace
  • 定製效能監控工具

直觀分析可以讓你在短時間內檢視整個App的卡頓情況,但是它不像Systrace可以提供更多卡頓的細節。Systrace可以提供足夠的資訊,但如果對App的所有使用場景運用Systrace分析,你會被大量的資料淹沒以至於難以分析問題。直觀分析和Systrace都可以在你的本地裝置上檢測卡頓問題,但如果沒辦法在本地裝置上重現卡頓問題,你可以構建自定義的效能監控工具,以便在線上執行的裝置上測量App特定部分的效能。

直觀分析

直觀分析可以幫助你定位App使用場景中產生卡頓的地方。你可以簡單地開啟App然後使用它的各個功能來檢視介面是否卡頓。以下是做直觀分析時候的一些建議:

  • 使用release版本(至少是非debuggable)的App。ART執行時為了支援debug的某些特性在debug情況下去掉了好幾個非常重要的效能優化點;因此要確保你分析的App是和使用者使用接近的版本。
  • 開啟Profile GPU RenderingProfile GPU Rendering在螢幕上顯示了各種圖表,可以幫助你直觀地看到繪製UI視窗的每一幀相對16ms每幀的標準花費了多長時間。每個顯示欄有各個不同顏色的元件,每個元件都被對映到渲染pipeline的某個階段,因此你可以看到哪一部分花費了最長的時間。例如,如果某一幀在處理輸入的時候花費了較長時間,那你就應該檢視一下你的程式碼裡面處理使用者輸入的部分。
  • 某些特定的元件,比如 RecyclerView,它們是常見的卡頓根源,如果你的App使用了這些元件,最好分析使用了這些元件的部分。
  • 儘量使用較慢的裝置來惡化卡頓問題以便分析。

一旦發現了產生卡頓的場景,或許你已經猜到了造成卡頓的原因,但如果需要更詳細的資訊來分析問題,可以藉助Systrace。

使用用Systrace

雖然Systrace是展示整個裝置在幹什麼的工具,它對定位卡頓問題也非常有幫助。Systrace有著非常小的執行時開銷,因此你在分析問題的時候可以體驗到真實的卡頓。

使用Systrace來記錄App卡頓場景下的trace(可以通過Systrace WalkThrough來檢視如何做)。systrace的圖表被程序和執行緒分為若干個部分,你的app的trace結果大致長這樣:

systrace

上圖所示的systrace包含著可以定位卡頓的如下資訊:

  1. Systrace顯示了每一幀繪製的時間段,並且給每一幀都有不同顏色,可以突出較慢的渲染幀時間。與直觀分析相比,這可以幫助你更精確地找到單獨的卡頓的某一幀。更詳細的內容可以參考 Inspecting Frames
  2. Systrace會監測你App中的問題並會在單獨幀和警告欄裡面展示警告提示資訊;跟著這些提示的指引來分析問題是最好的選擇。
  3. 某些 Android 框架和庫,比如 RecyclerView有自定義的trace標記,因此systrace的timeline會展示這些方法在何時執行以及執行了多長時間。

在查看了systrace的輸出結果之後,你可能會發現某些可疑的造成卡頓的方法。比如:如果timeline顯示某一幀過慢是由RecyclerView引起的,你可以給相關程式碼 新增Trace標記,然後重新執行systrace來獲取更多的資訊。新版的systrace的timeline會展示你程式碼裡面這些方法的呼叫的時機以及耗費的時間。

如果沒有從systrace中找到為什麼UI執行緒執行較長時間的細節,那麼你可能需要使用Android CPU Profiler來記錄取樣或者插樁的method trace。不過通常情況下,method trace不適合用來定位卡頓問題,因為它們執行時的開銷太高可能會造成誤報,並且它無法看到執行緒是在執行還是處於阻塞狀態。但是,method trace可以幫助你定位程式碼中耗費時間過程的方法;在定位到耗時方法之後,可以新增Trace標記 然後重新執行systrace來檢視是否是因為這些方法引起的卡頓。

PS:當記錄systrace的時候,每一個trace標記(一個開始和結束標記對)會帶來10納秒的開銷,為了避免卡頓誤報,不要在一幀內被呼叫很多次的方法裡面新增trace標記,也不要在呼叫耗時200納秒以下的方法裡面新增標記。

定製效能監控工具

如果你無法在本地裝置上重現卡頓問題,可以在App內構建自定義的效能監控工具,通過線上裝置來幫助定位卡頓問題。

修復卡頓

要修復卡頓問題,首先檢視那些沒有在16.7ms內完成的幀,然後檢視造成這個的原因在哪。是因為View#draw反常地花費了較長時間,又或者是佈局過程耗時?詳細介紹見下文的常見卡頓原因

要避免卡頓問題,耗時較長的任務應該在UI執行緒之外非同步完成;因此需要時刻注意你的程式碼執行在哪個執行緒,並且謹慎地post不重要的任務到主執行緒。

常見的卡頓原因

下面的小結將介紹一些App中常見卡頓的原因,並提供一些定位它們的最佳實踐。

滾動列表

ListView,特別是RecyclerView被廣泛用於複雜的滾動列表裡面,它們是最容易導致卡頓的部分。這兩個控制元件都有Systrace標記,因此你可以藉助systrace來分析它們是否造成了app的卡頓。在獲取RecyclerView以及你自己新增的systrace標記的時候,必須要給systrace傳遞 -a <your-package-name>,不然就不會輸出這些標記的資訊。儘可能跟隨Systrace的提示資訊來分析它的輸出。在systrace裡面,你可以點選RecyclerView的相應標記來看RecyclerView當時在幹什麼。

RecyclerView:notifyDataSetChanged

如果你觀察到在某一幀內RecyclerView中的每個item都被重新綁定了(並因此重新佈局和重新繪製),請確保你沒有對RecyclerView執行區域性更新的時候呼叫 notifyDataSetChanged()setAdaper(Adapter)或者 swapAdaper(Adaper, boolean)。這些方法表示整個列表內容改變了,並且會在systrace裡面顯示為 RV FullInvaludate。在區域性更新或者新增內容的時候應該使用 SortedList或者 DiffUtil生成更輕量級的更新操作。

例如,如果app從服務端收到了新的新聞列表訊息,當你把資訊post到Adapter的時候,可以像下面這樣使用notifyDataSetChanged():

void onNewDataArrived(List<News> news) {
    myAdapter.setNews(news);
    myAdapter.notifyDataSetChanged();
}

但是這麼做有個嚴重的缺陷——如果這是個微不足道的更新(也許是在頂部加一條),RecyclerView並不知道這個資訊——RecyclerView被告知丟掉它所有item快取的狀態,並且需要重新繫結所有東西。

更可取的是使用 DiffUtil,它可以幫你計算和分發細小的更新操作:

void onNewDataArrived(List<News> news) {
    List<News> oldNews = myAdapter.getItems();
    DiffResult result = DiffUtil.calculateDiff(new MyCallback(oldNews, news));
    myAdapter.setNews(news);
    result.dispatchUpdatesTo(myAdapter);
}

只需要自定義一個DiffUtil.Callback實現類告訴DiffUtil如何分析你的item,DiffUtil就能自動幫你完成其他的所有事情。

RecyclerView:巢狀RecyclerViews

巢狀RecyclerView是非常常見的事,特別是一個垂直列表裡面有一個水平滾動列表的時候(比如Google Play store的主頁)。如果你第一次往下滾動頁面的時候,發現有很多內部的item執行inflate操作,那可能就需要檢查你是否在內部(水平)RecyclerView之間共享了 RecyclerView.RecyclerViewPool了。預設情況下,每個RecyclerView有自己堵路的item池。在螢幕上有十幾個itemViews的情況下,如果所有的行的View Type都一樣,而itemViews不能被不同的水平列表共享,那就是有問題的。

class OuterAdapter extends RecyclerView.Adapter<OuterAdapter.ViewHolder> {
    RecyclerView.RecycledViewPool mSharedPool = new RecyclerView.RecycledViewPool();

    ...

    @Override
    public void onCreateViewHolder(ViewGroup parent, int viewType) {
        // inflate inner item, find innerRecyclerView by ID…
        LinearLayoutManager innerLLM = new LinearLayoutManager(parent.getContext(),
                LinearLayoutManager.HORIZONTAL);
        innerRv.setLayoutManager(innerLLM);
        innerRv.setRecycledViewPool(mSharedPool);
        return new OuterAdapter.ViewHolder(innerRv);

    }
    ...

如果你想進行進一步的優化,可以對內部RecyclerView的LinearLayout呼叫 setInitialPrefetchItemCount(int))。比如如果你在每一行都是展示三個半item,可以呼叫 innerLLM.setInitialItemsPrefetchCount(4); 這樣當水平列表將要展示在螢幕上的時候,如果UI執行緒有空閒時間,RecyclerView會嘗試在內部預先把這幾個item取出來。

RecyclerView:Too much inflation/Create taking too long

通過在UI執行緒空閒的時候提前完成任務,RecyclerView的prefetch可以幫助解決大多數情況下inflate的耗時問題。如果你在某一幀內看到inflate過程(並且不在RV Prefectch標記內),請確保你是在最近的裝置上(prefect特性現在只支援Android 5.0,API 21
以上的裝置)進行測試的,並且使用了較新版本的 Support Library

如果你在item顯示在螢幕上的時候頻繁觀察到inflate造成卡頓,需要驗證一下你是否使用了額外的你不需要的View型別。RecyclerView內容的View型別越少,在新item顯示的時候需要的inflation越少。在可能的情況下,可以合併合理的View型別——如果不同型別之間僅僅有圖表,顏色和少許文字不同,你可以在bind的時候動態改變它們,來避免inflate過程。(同時也可以減少記憶體佔用)

如果view的型別是合理的,那麼就嘗試減少inflation耗費的時間。減少不必要的容器類ViewGroup或者用來結構化的View——可以考慮使用 ConstrainLayout,它可以有效減少結構性的View。如果還需要優化效能,並且你item的view繼承樹比較簡單而且不需要複雜的theme和style,可以考慮自己呼叫建構函式(不使用xml)——雖然通常失去XML的簡單和特性是不值的。

RecyclerView:Bind taking too long

繫結過程(也就是 onBindViewHolder(VH, int))應該是非常簡單的,除了及其複雜的item,其他所有的item的繫結過程耗時應該遠小於1毫秒。onBinderViewHolder應該簡單地從adapter裡取出POJO物件,然後對ViewHolder裡面的View呼叫setter方法。如果 RV OnBindView 耗費了較長時間,請驗證一下是否在繫結的程式碼裡面做了別的工作。

如果你在adapter裡使用簡單的POJO物件,那你完全可以藉助Data Binding庫來避免在onBindViewHolder裡面寫繫結程式碼。

RecyclerView or ListView:layout/draw taking too long

對於draw和layout造成的問題,檢視下文的佈局效能渲染效能

ListView:Inflation

ListView中的View複用機制很容易被偶然破壞,如果你看到ListView的每個Item出現在螢幕上的時候都觸發了inflate過程,必須要檢查你的Adapter.getView())是否使用、重新繫結並且返回了convertView這個引數。如果你的getView()實現每次都inflate,那就沒法享受ListView的View複用機制。getView()方法的結構應該永遠是下面這個樣子:

view getView(int position, View convertView, ViewGroup parent) {

    if (convertView == null) {
        // only inflate if no convertView passed
        convertView = mLayoutInflater.inflate(R.layout.my_layout, parent, false)
    }
    // … bind content from position to convertView …
    return convertView;
}

佈局效能

如果Systrace顯示Layout段的Choreographer#doFrame做了大量的工作,或者執行得太頻繁,那麼你可能遇到佈局效能問題。App的佈局效能取決於View繼承樹的哪一部分改變了佈局引數或者輸入。

佈局效能:耗時

如果佈局的每一段都要花費數毫秒,那麼可能是巢狀 RelativeLayout或者 帶weight的LinearLayout。這些佈局都可能觸發子View的多次測量/佈局過程,因此巢狀這些佈局可能會造成佈局時間的時間複雜度為O(2^n)(n為巢狀深度)。因此,除非RelativeLayout或者帶weight的LinearLayout 是View樹的葉子節點,要儘量避免使用它們來佈局。有幾個方式可以做到這一點:

  • 重新組織布局結構
  • 自定義佈局邏輯,詳情可見 優化佈局
  • 嘗試將佈局轉換為 ConstraintLayout,它可以提供類似的特性,但是沒有效能問題。

佈局效能:頻率

佈局過程通常在新內容出現在螢幕上的時候發生,比如RecyclerView中的某個Item滾動到螢幕可見區域上。如果某個重要的佈局在每一幀上都執行了layout過程,那可能是你在移動整個佈局,而這通常會引發掉幀。通常情況下,動畫應該操作View的繪製屬性(比如setTranslationX/Y/Z, setRotation, setAlpha),這些操作比改變View的佈局屬性(padding,或者margin)要廉價得多。通過invalidate()進而在下一幀觸發 draw(Canvas) 會在View被invalidated的時候重新記錄繪製操作,這個過程通常也比layout廉價得多。

渲染效能

Android UI 繪製工作分為兩個階段:執行在在UI執行緒的 View#draw,以及在RenderThread裡執行的DrawFrame。第一個階段會執行被標記為invalidated的View的 draw(Canvas) 方法,這個方法通常會呼叫很多你的App程式碼裡面的自定義View的相關方法;第二個階段發生在native執行緒RenderThread裡面,它會基於第一階段View#draw的呼叫來執行相應的操作。

渲染效能:UI執行緒

如果 View#draw 呼叫花費了較長時間,常見的一種情況是在UI執行緒在繪製一個Bitmap。繪製Bitmap會使用CPU渲染,因此需要儘量避免。你可以通過 Android CPU Profiler 用method tracing來確定是否是這個原因。

通常情況下繪製Bitmap是因為我們想給Bitmap加一個裝飾效果,比如圓角:

Canvas bitmapCanvas = new Canvas(roundedOutputBitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
// draw a round rect to define shape:
bitmapCanvas.drawRoundRect(0, 0,
        roundedOutputBitmap.getWidth(), roundedOutputBitmap.getHeight(), 20, 20, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
// multiply content on top, to make it rounded
bitmapCanvas.drawBitmap(sourceBitmap, 0, 0, paint);
bitmapCanvas.setBitmap(null);
// now roundedOutputBitmap has sourceBitmap inside, but as a circle

如果你的UI執行緒做的是這種工作,你可以在一個後臺執行緒裡面完成解碼然後在UI執行緒繪製。在某些情況下(比如本例),甚至可以直接在draw的時候完成,比如如果你的程式碼長這樣:

void setBitmap(Bitmap bitmap) {
    mBitmap = bitmap;
    invalidate();
}

void onDraw(Canvas canvas) {
    canvas.drawBitmap(mBitmap, null);
}

可以用如下的程式碼來代替:

void setBitmap(Bitmap bitmap) {
    mShaderPaint.setShader(
            new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP));
    invalidate();
}

void onDraw(Canvas canvas) {
    canvas.drawRoundRect(0, 0, mWidth, mHeight, 20, 20, mShaderPaint);
}

要注意的是,上述操作也適用於 background protection(在Bitmap上繪製一個漸變)和 image filtering (用 ColorMatrixColorFilter)這兩個對Bitmap的常見操作。

如果你是因為別的原因而繪製Bitmap,或許你可以使用快取,嘗試在支援硬體加速的Canvas上直接繪製,或必要的時候呼叫 setLayerType) 設定Canvas 為 LAYER_TYPE_HARDWARE 來快取複雜的渲染輸出,這樣也可以享受GPU渲染的優勢。

渲染效能:RenderThread

某些Canvas操作在UI執行緒是非常廉價的,但卻會在RenderThead出發大量昂貴的計算操作。通常Systrace會給這些呼叫給出警告提示。

Canvas.saveLayer()

要儘量避免 Cavas.saveLayer()) 呼叫,這個方法會在每一幀觸發昂貴、未被快取的離屏渲染。雖然在Android 6.0上優化了這個操作的效能(避免了GPU上的渲染目標切換),仍然需要儘可能地避免呼叫這個方法;如果實在需要呼叫它,確保給它傳遞 CLIP_TO_LAYER_SAVE_FLAG

Anmating large Paths

如果在一個支援硬體加速的Canvas上呼叫Canvas.drawPath()), 系統會首先在CPU上繪製這些path,然後把它傳遞給GPU。如果你的path物件很大,那最好避免在每一幀之間修改它,這樣path物件就可以被系統快取起來,使得繪製更加高效。drawPoints()drawLines()drawRect/Circle/Oval/RoundRect() 比 drawPath 更加高效——因此最好使用它們替代相應的drawPath操作,雖然可能用的程式碼量更多。

Canvas.clipPath

clipPath(Path)) 會觸發昂貴的裁剪操作,因此也需要儘量避免。在可能的情況下,應該儘量直接繪製出需要的形狀,而不是裁剪成相應的圖形;這樣效能更高,並且支援反鋸齒;例如下面這個clipPath 操作:

canvas.save();
canvas.clipPath(mCirclePath);
canvas.drawBitmap(mBitmap);
canvas.restore();

可以用如下代替:

// one time init:
mPaint.setShader(new BitmapShader(mBitmap, TileMode.CLAMP, TileMode.CLAMP));
// at draw time:
canvas.drawPath(mCirclePath, mPaint);
Bitmap uploads

Android的顯示系統使用OpenGL,bitmap在底層表現為OpenGL的紋理,因此在bitmap第一次被展示的時候,bitmap會被首先上傳的GPU上。Systrace上標記為 Upload width x height Texture就是這種情況。這個過程可能會花費數毫秒(如下圖),但是這是GPU顯示影象的必要過程。

App在上傳一個180萬畫素的bitmap時花費了10ms,要麼減少圖片的大小,那麼使用prepareToDraw提前完成這個操作

如果這個過程花費了較長時間,首先檢查在trace裡面顯示的圖片的寬和高,確保圖片的大小不比它顯示出來的區域大太多,因為這樣會浪費上傳時間和記憶體。常見的圖片載入庫都會提供一個方便的方式來獲取和請求一個合適大小的Bitmap。

在Android 7.0上,圖片載入程式碼(通常是圖片載入庫)可以呼叫 prepareToDraw)在需要的時候提前觸發Bitmap上傳動作;這種方式可以使Bitmpa在RenderThread空閒的時候提前完成。可以在圖片解碼之後或者在Bitmap繫結到View上的時候完成這個操作——理想情況下,圖片載入庫會幫助你完成這些;如果你想要自己掌控圖片載入,或者需要確保不在繪製的時候觸發Bitmap上傳,可以直接在程式碼裡面呼叫 prepareToDraw

執行緒排程延遲

執行緒排程器是Android作業系統的一部分,作業系統用它來決定系統中的執行緒如何執行、何時執行以及執行多長時間。某些情況下,App卡頓是因為UI執行緒被阻塞或者沒有執行。Systrace用不同的顏色(如下圖)來標記某個執行緒是 Sleep(灰色),Runnable(藍色:可執行狀態,但是排程器沒有選擇讓它執行),Actively running(綠色),或者 Uninterruptible sleep(紅色或者橘黃色),這對解決由於執行緒排程引起的卡頓非常有幫助。

老版本的Android系統頻繁出現執行緒排程問題並不是App自己的鍋,Android開發團隊對這一塊進行的持續的改進,因此在debug執行緒排程的問題的時候,最好使用新版本的Android系統,以確定執行緒問題確實是App的鍋而非系統問題。

UI執行緒處於sleeping狀態

要注意的是,在一幀的某些部分,UI執行緒或者RenderThread是不期望被執行的。比如,當RenderThread 的syncFrameState 執行以及Bitmap上傳的時候UI執行緒處於阻塞狀態——這樣RenderThead可以安全地從UI執行緒copy資料。再舉個例子:當RenderThread使用IPC(內部程序通訊)的時候它自己也可能處於阻塞狀態:比如在一幀開始的時候獲取buffer,從buffer查詢資訊,或者通過eglSwapBuffers 把buffer回傳給合成器。

App執行中的長時間停頓通常情況下是由Binder呼叫(Android中的內部程序通訊機制)引起的。在最近的一些Android版本上,Binder呼叫是UI執行緒暫停執行最常見的原因之一。一般的解決方案是,避免呼叫IPC函式,快取呼叫值,或者把工作放到後臺執行緒執行。隨著程式碼庫越來越大,開發人員很容易就不小心地在某個低層次的方法裡面添加了Binder呼叫的函式——不過我們可以通過tracing很容易滴發現和修復這個問題。

如果app中有binder通訊,可以用如下的adb命令來檢視呼叫棧:

$ adb shell am trace-ipc start
… use the app - scroll/animate ...
$ adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt
$ adb pull /data/local/tmp/ipc-trace.txt

有時候某些看起來無害的的方法(比如getRefreshRate())會觸發Binder通訊,然後如果它們頻繁地被呼叫就會引發嚴重的效能問題。週期性地對App進行tracing可以幫助你在問題出現的時候快速地定位和解決它們。

由於RecylerView的fling觸發的Binder呼叫引起UI執行緒sleeping。保持你的IPC呼叫方法簡單,然後使用trace-ipc來移除不必要的呼叫

如果你沒有發現Binder呼叫,但是UI執行緒依然處於沒在執行的狀態,那可能是因為UI執行緒在等待其他執行緒某個操作的鎖。一貫情況下,UI執行緒不應該等待其他執行緒的執行結果——別的執行緒應該在拿到結果之後post給UI執行緒。

物件分配和垃圾回收

自從Android引入 ART 並且在Android 5.0上成為預設的執行時之後,物件分配和垃圾回收(GC)造成的卡頓已經顯著降低了,但是由於物件分配和GC有額外的開銷,它依然又可能使執行緒負載過重。 在一個呼叫不頻繁的地方(比如按鈕點選)分配物件是沒有問題的,但如果在在一個被頻繁呼叫的緊密的迴圈裡,就需要避免物件分配來降低GC的壓力。

可以通過Systrace來確定是否發生了頻繁的GC,然後用 Android Memory Profier 分析分配的物件都是些什麼。如果你儘可能地做到了避免分配物件(特別是在緊密的迴圈裡),那就幾乎不會遇到這種問題。

發生在HeapTaskThread執行緒上一個耗時94ms的GC

在最近的Android版本上,GC通常執行在一個叫做HeapTaskDaemon的後臺執行緒裡面。如上圖所示,過多的物件分配意味著CPU將在GC上耗費更多的資源。

相關推薦

[]Android UI 效能優化

。水平有限,翻譯不妥之處請多多指教。UI渲染是指從App生成幀並顯示在螢幕上的行為。為了保證使用者體驗的流暢性,App需要在16ms內渲染完一幀以達到60fps的幀率(為什麼是60fps?)。如果你的App UI渲染緩慢,那麼系統會強制跳過某些幀,使用者就會感知到介面的“口吃”,也就是卡頓。(以下三段Goog

Android UI效能優化實戰 識別繪製中的效能問題

                     1、概述2015年初google釋出了Android效能優化典範,發了16個小視訊供大家欣賞,當時我也將其下載,通過微信公眾號給大家推送了百度雲的下載地址(地址在文末,ps:歡迎大家訂閱公眾號),那麼近期google又在udacity上開了系列類的相關課程。有了上述的

Android UI效能優化實戰 解決佈局複雜導致的程式奔潰

1、概述 2015年初google釋出了Android效能優化典範,發了16個小視訊供大家欣賞,當時我也將其下載,通過微信公眾號給大家推送了百度雲的下載地址(地址在文末,ps:歡迎大家訂閱公眾號),那麼近期google又在udacity上開了系列類的相關課程

Android 系統性能優化(34)---Android UI 效能優化

Android官網 Slow rendering;個人覺得非常有價值,比如指出 物件分配、垃圾回收(GC)、執行緒排程以及Binder呼叫 是Android系統中常見的卡頓原因,更重要的是給出了定位和解決這些問題的方案;而非簡單地告訴你避免物件分配,減少佈局層級,減少過度

Android UI效能優化 檢測應用中的UI卡頓

一、概述 在做app效能優化的時候,大家都希望能夠寫出絲滑的UI介面,以前寫過一篇部落格,主要是基於Google當時釋出的效能優化典範,主要提供一些UI優化效能示例: 實際上,由於各種機型的配置不同、程式碼迭代歷史悠久,程式碼中可能會存在很多在U

Android UI效能優化—過度繪製篇

Android UI效能優化——過度繪製篇 過度繪製(overdraw) 過度繪製介紹 每過幾年,就會有傳聞說某個博物館在用x光掃描一副無價的名畫之後,發現畫作的作者其實重用了老的畫布,在名畫的底下還藏著另一副沒有被發現的畫作。有時候,博物館還能用

Android UI效能專項測試及優化

1 UI卡頓(Jank) 內容的快速載入很重要,渲染的流暢性也很重要。android團隊把滯緩,不流暢的動畫定義為jank,一般是由於丟幀引起的。安卓裝置的螢幕重新整理率一般是60幀每秒(1/60fps=16.6ms每幀),所以你想要渲染的內容能在16ms內完

Android App效能優化UI流暢度優化

一、卡頓的問題本質 UI流暢度的優化主要是解決UI卡頓的現象,而UI卡頓的源頭就是渲染效能的問題。佈局太複雜或者是一個元素重複繪製多次等原因,Android系統無法及時完成那些複雜的介面渲染操作,這樣就發生了丟幀,使用者就會感覺到不流暢,卡頓。 Androi

Android效能優化

Android的效能優化 寫在前面: 公司給了我一週的時間去學習Android效能的優化,參考了張明雲老師的一片文章,並且用公司的實際專案進行測試(附有截圖),還進行了一些知識點,注意事項以及很多網址連結的補充,希望這篇博文能讓做效能測試的朋友們少走一些彎路。

Android效能優化

眾所周知,android裝置作為一種移動裝置,不管是cpu還是記憶體都無法跟pc裝置相提並論。這就意味著我們不能無限制地使用記憶體和cpu的資源,過多地使用記憶體會導致oom,過多地使用cpu資源,做大量的耗時任務,會導致手機變得異常卡頓,甚至無法響應出現ANR。所以我們在平常做開發時,一定要注意效

Android效能優化:電量消耗統計

電量的消耗和使用對於移動裝置非常重要,一項調查問卷顯示,電池的容量和壽命是手機最重要的營銷點:所謂“the one thing that you can't do without”。 硬體 從硬體的角度看,Android電量的消耗主要來自螢幕,CPU,網路裝置和各樣的感測器:指紋,亮度

Android效能優化工具之Hierarchy Viewer使用總結

概述 Hierarchy Viewer提供了一個視覺化的介面來觀測佈局的層級, 讓我們可以優化佈局層級, 刪除多餘的不必要的View層級, 提升佈局速度. 應用Hierarchy Viewer 目前Hierarchy Viewer只能在root過的機器才能使用. 主要是在沒有ro

基於Spark UI效能優化與除錯——初級篇

Spark有幾種部署的模式,單機版、叢集版等等,平時單機版在資料量不大的時候可以跟傳統的java程式一樣進行斷電除錯、但是在叢集上除錯就比較麻煩了...遠端斷點不太方便,只能通過Log的形式進行資料分析,利用spark ui做效能調整和優化。 那麼本篇就介紹下如何利用Ui做效能分析,因為本人的經驗也不是很

Android App效能優化系列

Android App效能優化系列 關於Android App的優化,從第一篇的計劃開始,到記憶體優化的系列文結束,不知不覺近三個月的時間,寫了十五六篇相關的博文,算是對自己的知識的一個系統化,也希望能給大家一些幫助。 在此有對此做一個總結。 路線   Andro

Android效能優化問題

1、UI優化 在xml佈局檔案中,合理選擇RelativeLayout、LinearLayout、FrameLayout,其中RelativeLayout會讓子View呼叫兩次onMeasure()方法,並且佈局複雜時,onMeasure也相對複雜,效率比較低;在LinearLayout中

Android LeakCanary效能優化工具

效能優化工具(九)-LeakCanary 一、簡介 使用MAT來分析記憶體問題,有一些門檻,會有一些難度,並且效率也不是很高,對於一個記憶體洩漏問題,可能要進行多次排查和對比才能找到問題原因。 為了能夠簡單迅速的發現記憶體洩漏,Square公司基於MAT開源了L

Android效能優化方法

佈局優化 佈局優化的思想:儘量減少佈局檔案的層級。佈局的層級少了,這就意味著Android的繪製工作量少了,那麼程式的效能自然提高了。 如何進行佈局優化呢? 刪除佈局中無用的控制元件和層級 有選擇地使用效能較低的ViewGroup,比如RelativeL

Android應用效能優化之使用SparseArray替代HashMap

原文地址:http://liuzhichao.com/p/832.html HashMap是java裡比較常用的一個集合類,我比較習慣用來快取一些處理後的結果。最近在做一個Android專案,在程式碼中定義這樣一個變數,例項化時,Eclipse卻給出了一個 pe

[看圖說話] 基於Spark UI效能優化與除錯——初級篇

Spark有幾種部署的模式,單機版、叢集版等等,平時單機版在資料量不大的時候可以跟傳統的java程式一樣進行斷電除錯、但是在叢集上除錯就比較麻煩了...遠端斷點不太方便,只能通過Log的形式進行資料分析,利用spark ui做效能調整和優化。 那麼本篇就介紹下如何利用Ui做效

android專案效能優化之啟動時間

一般來說,判定一個android專案效能優劣,我們有以下幾個指標: 啟動時間 apk大小 UI渲染 穩定性 記憶體佔用 電量消耗 接下來,讓我們就這幾個指標展開來詳述各自究竟應該怎樣去優化。 啟動時間 一般來說,應用啟動時間分為三種 首次啟動