Android性能優化彙總
優化思想:查看自己的佈局,層次是否很深以及渲染比較耗時,然後想辦法能否減少層級以及優化每一個View的渲染時間。
CPU的優化,從減輕加工View對象成Polygons和Texture來下手
如何找出裏面沒用的view呢?或者減少不必要的view嵌套。
工具:Hierarchy Viewer檢測,通過View Hierarchy可以找到沒有用的view,這些view根本就不會顯示在屏幕上面,一旦觸發測量和佈局操作,就會拖累應用的性能表現。
一 Hierarchy Viewer使用
-
大開Android Monitor: 找不到工具的,Android Monitor路徑:打開sdk目錄下的tool文件夾中monitor.bat
-
切換到Hierarchy Viewer窗口
-
選中要查看佈局的Activity,點擊load the view hierarchy into the tree view
-
view Tree 中能看到整個佈局視圖,包括PhoneWindow的
三個圓點分別代表:測量、佈局、繪製三個階段的性能表現。
1)綠色:渲染的管道階段,這個視圖的渲染速度快於至少一半的其他的視圖。
2)黃色:渲染速度比較慢的50%。
3)紅色:渲染速度非常慢。
- 問題:
- Unable to get view server protocol version from device
不顯示的話,關閉CPU顯示過度繪製, - Measure、LayoutDraw時間沒有,不顯示三個圓點
開發者設置關閉、重新獲取刷新一下、選擇父控件、點擊右上角按鈕
二 減少佈局嵌套
-
主要減少Mesure、Layout
方法: merge 和ViewStub、RelativeLayout減少佈局層級和加載 -
merge: 當我們的佈局是用的FrameLayout的時候,我們可以把它改成merge
可以避免自己的幀佈局和系統的ContentFrameLayout幀佈局重疊造成重複計算(measure和layout) -
ViewStub: 當加載的時候纔會佔用。不加載的時候就是隱藏的,僅僅佔用位置。
例如: 一個ListView用wrapcontent,優化前的item如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingBottom="@dimen/chat_padding_bottom">
<ImageView
android:id="@+id/chat_author_avatar"
android:layout_width="@dimen/avatar_dimen"
android:layout_height="@dimen/avatar_dimen"
android:layout_margin="@dimen/avatar_layout_margin" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/darker_gray"
android:orientation="vertical">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#78A"
android:background="@android:color/white"
android:orientation="horizontal">
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:padding="@dimen/narrow_space"
android:gravity="bottom"
android:id="@+id/chat_author_name" />
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textStyle="italic"
android:padding="@dimen/narrow_space"
android:id="@+id/chat_datetime" />
</RelativeLayout>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/narrow_space"
android:background="@android:color/white"
android:id="@+id/chat_text" />
</LinearLayout>
</LinearLayout>
優化前Hierarchy顯示(有紅色、黃色警告)
ListView用MatchParent、Item使用RelativeLayout減少層級優化後
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/chat_author_avatar"
android:layout_width="@dimen/avatar_dimen"
android:layout_height="@dimen/avatar_dimen"
android:src="@drawable/alex" />
<TextView
android:id="@+id/chat_author_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/chat_author_avatar"
android:paddingLeft="@dimen/narrow_space"
android:text="XXX" />
<TextView
android:id="@+id/chat_datetime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:paddingRight="@dimen/narrow_space"
android:text="AAA"
android:textStyle="italic" />
<TextView
android:id="@+id/chat_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/chat_datetime"
android:layout_toRightOf="@id/chat_author_name"
android:paddingBottom="@dimen/chat_padding_bottom"
android:paddingLeft="@dimen/narrow_space"
android:text="BBB" />
</RelativeLayout>
優化後,警告明顯減少了(ListView本身的測量和繪製還是比較耗時的)
三 減少過度繪製
工具: 設置-> 開發人員選項-> 調試GPU過度繪製
**查看:**局域顏色越深,過度繪製越嚴重(要排除控件本身背景色的干擾)
1 背景經常容易造成過度繪製。
解決的辦法: 將主題添加的背景去掉
getWindow().setBackgroundDrawable(null);
2 自定義控件通過裁剪處理過度繪製。
優化前,在Canvas上重複繪製:
public class CardsView extends View {
//圖片與圖片之間的間距
private int mCardSpacing = 150;
//圖片與左側距離的記錄
private int mCardLeft = 10;
private List<Card> mDroidCards = new ArrayList<>();
private Paint paint = new Paint();
public CardsView(Context context) {
super(context);
initCards();
}
public CardsView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initCards();
}
public CardsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCards();
}
/**
* 初始化卡片集合
*/
protected void initCards() {
Resources res = getResources();
mDroidCards.add(new Card(res, R.drawable.alex, mCardLeft));
mCardLeft += mCardSpacing;
mDroidCards.add(new Card(res, R.drawable.claire, mCardLeft));
mCardLeft += mCardSpacing;
mDroidCards.add(new Card(res, R.drawable.kathryn, mCardLeft));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Card c : mDroidCards) {
drawCard(canvas, c);
}
invalidate();
}
private void drawCard(Canvas canvas, Card c) {
canvas.drawBitmap(c.bitmap, c.x, 0f, paint);
}
}
通過裁剪canvas優化,只繪製顯示的區域,注意canvas的恢復
public class OptimizationCardsView extends View {
//圖片與圖片之間的間距
private int mCardSpacing = 150;
//圖片與左側距離的記錄
private int mCardLeft = 10;
private List<Card> mDroidCards = new ArrayList<>();
private Paint paint = new Paint();
public OptimizationCardsView(Context context) {
super(context);
initCards();
}
public OptimizationCardsView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initCards();
}
public OptimizationCardsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initCards();
}
/**
* 初始化卡片集合
*/
protected void initCards() {
Resources res = getResources();
mDroidCards.add(new Card(res, R.drawable.alex, mCardLeft));
mCardLeft += mCardSpacing;
mDroidCards.add(new Card(res, R.drawable.claire, mCardLeft));
mCardLeft += mCardSpacing;
mDroidCards.add(new Card(res, R.drawable.kathryn, mCardLeft));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mDroidCards.size() - 1; i++) {
drawDroidCard(canvas, mDroidCards, i);
}
drawLastDroidCard(canvas, mDroidCards.get(mDroidCards.size() - 1));
invalidate();
}
/**
* 繪製最後一個Card
*
* @param canvas
* @param c
*/
private void drawLastDroidCard(Canvas canvas, Card c) {
canvas.drawBitmap(c.bitmap, c.x, 0f, paint);
}
/**
* 繪製Card
*
* @param canvas
* @param mDroidCards
* @param i
*/
private void drawDroidCard(Canvas canvas, List<Card> mDroidCards, int i) {
Card c = mDroidCards.get(i);
canvas.save();
canvas.clipRect(c.x, 0f, mDroidCards.get(i + 1).x, c.height);
canvas.drawBitmap(c.bitmap, c.x, 0f, paint);
canvas.restore();
}
}