CPU 處理邏輯計算和記憶體管理,顯示操作。 GPU CPU無法顯示覆雜的圖形,GPU用於顯示覆雜圖形,分擔CPU的任務 xml佈局到屏幕的顯示流程:xml 通過 LayoutInflater 載入到記憶體中,然後經過CPU計算處理為多維圖形,在通過 OpenGL 調用GPU,GPU對圖形進行柵格化顯示 ...
CPU 處理邏輯計算和記憶體管理,顯示操作。
GPU CPU無法顯示覆雜的圖形,GPU用於顯示覆雜圖形,分擔CPU的任務
xml佈局到屏幕的顯示流程:xml 通過 LayoutInflater 載入到記憶體中,然後經過CPU計算處理為多維圖形,在通過 OpenGL 調用GPU,GPU對圖形進行柵格化顯示到屏幕上,此時如果上面流程在16毫秒內完成,則直接顯示到顯示器,如果沒能完成,則垂直同步等待下一幀完成。
由於人類的眼睛看到畫面之幀率高於每秒約10-12幀的時候,就會認為是連貫的;
對於有聲電影的拍攝和播放幀率均為每秒24幀,對人是可以接受的,但是早期的高動態電子游戲幀率如果少於每秒30幀的話就會不連貫,因為沒有動態模糊使流暢度降低,在於手機交互的過程中,如果觸摸反饋60幀以下人是可以感覺出來的,60幀以上不能察覺變化,當手機上幀率低於60fps的時候會感覺畫面的卡頓和遲滯現象;
Android系統每隔16ms發出信號,觸發對UI進行渲染,如果每次渲染都成功,就能達到流程的畫面所需要的60fps,為了能夠實現60fps,這意味著計算渲染的太多數操作都必須在16ms內完成。
當一幀畫面渲染時間超過16ms的時候,垂直同步機制(每隔16ms刷新幀率)會讓顯示器硬體等待GPU完成柵格化渲染操作,這樣會讓一幀畫面多停留16ms甚至更多,這樣就造成了用戶看起來畫面停頓。
優化:
CPU 減少xml轉換成對象的時間,比如減少層級
GPU 減少重覆繪製
GPU的繪製過程根據CPU傳遞的指令來繪製,16ms繪製一次,指令來了就繪製,哪怕是同樣的繪製指令,如果層次太深,用戶看不到的區域也會被繪製,以及自定義控制項中,onDraw方法做了過多的繪製。
Android手機開發者選項中,打開GPU過度繪製調試,藍色 表示繪製了一次,淡綠色表示兩次,淡紅色表示三次,深紅色表示四次。
註意:Android的Theme-主題中,是自帶背景的,可以設置windowBackground為null
下麵是一個過度繪製的離職,註釋部分為過度繪製,打開部分為優化後的繪製,打開GPU調試後運行查看過度繪製區域,作比較
import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.util.AttributeSet; import android.view.View; import java.util.ArrayList; import java.util.List; import androidx.annotation.Nullable; public class DroidCardsView extends View { private int mCardSpacing = 150;//圖片間隔 private int mCardLeft = 50;//圖片左側距離 private List<DroidCard> mDroidCards = new ArrayList<>(); private Paint paint = new Paint(); public DroidCardsView(Context context) { super(context); initCards(); } public DroidCardsView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initCards(); } private void initCards(){ Resources res = getResources(); mDroidCards.add(new DroidCard(res,R.mipmap.testpic1,mCardLeft)); mCardLeft += mCardSpacing; mDroidCards.add(new DroidCard(res,R.mipmap.testpic2,mCardLeft)); mCardLeft += mCardSpacing; mDroidCards.add(new DroidCard(res,R.mipmap.testpic1,mCardLeft)); mCardLeft += mCardSpacing; mDroidCards.add(new DroidCard(res,R.mipmap.testpic2,mCardLeft)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // for (DroidCard card : mDroidCards) { // drawDroidCard(canvas,card); // } // invalidate(); for (int i = 0;i < mDroidCards.size() -1;i++) {//最後一張不裁剪 drawDroidCard(canvas,mDroidCards,i); } drawLeftDroidCard(canvas,mDroidCards.get(mDroidCards.size() -1)); invalidate(); } private void drawDroidCard(Canvas canvas,DroidCard card){ canvas.drawBitmap(card.bitmap,card.left,0f,paint); } private void drawLeftDroidCard(Canvas canvas,DroidCard card){ canvas.drawBitmap(card.bitmap,card.left,0f,paint); } private void drawDroidCard(Canvas canvas,List<DroidCard> list,int i){ DroidCard card = list.get(i); /*Canvas中當前路徑不屬於保存狀態,狀態包含線條,平移,顏色,漸變,陰影等*/ canvas.save();//保存畫布狀態,第二次繪製在起點,因為這裡每一個的x坐標是以左側為起點累加的 //獲取當前圖片的 left 值,裁剪出當前圖片顯示的區域,並且繪製 canvas.clipRect(card.left,0f,list.get(i+1).left,card.height); canvas.drawBitmap(card.bitmap,card.left,0f,paint); canvas.restore(); } }
import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class DroidCard { public Bitmap bitmap; public int left,width,height; public DroidCard(Resources resources,int resId,int left){ this.bitmap = BitmapFactory.decodeResource(resources,resId); this.left = left; this.width = bitmap.getWidth(); this.height = bitmap.getHeight(); } }
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.example.testdemo.DroidCardsView android:layout_width="match_parent" android:layout_height="150dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> </RelativeLayout>