http://blog.csdn.net/bd_zengxinxin/article/details/52525781java
本身編寫App的時候,有時會感受界面卡頓,尤爲是自定義View的時候,大多數是由於佈局的層次過多,存在沒必要要的繪製, 或者onDraw等方法中過於耗時。那麼究竟須要多快,才能給用戶一個流暢的體驗呢?那麼就須要簡單瞭解下Android的渲染機制:android
Android系統每隔16ms發出VSYNC信號,觸發對UI進行渲染,那麼整個過程若是保證在16ms之內就能達到一個流暢的畫面。 那麼若是操做超過了16ms就會發生下面的狀況:canvas
若是系統發生的VSYNC信號,而此時沒法進行渲染,還在作別的操做,那麼就會致使丟幀的現象, (你們在察覺到APP卡頓的時候,能夠看看logcat控制檯,會有drop frames相似的警告)。這樣的話,繪製就會在下一個16ms的時候才進行繪製, 即便只丟一幀,用戶也會發現卡頓的。ide
那爲何是16ms,16ms意味着着1000/60hz,至關於60fps,那麼只要解釋爲何是60fps。工具
這是由於人眼與大腦之間的協做沒法感知超過60fps的畫面更新。12fps大概相似手動快速翻動書籍的幀率, 這明顯是能夠感知到不夠順滑的。24fps使得人眼感知的是連續線性的運動,這實際上是歸功於運動模糊的 效果。 24fps是電影膠圈一般使用的幀率,由於這個幀率已經足夠支撐大部分電影畫面須要表達的內容,同時可以最大的減小費用支出。 可是低於30fps是 沒法順暢表現絢麗的畫面內容的,此時就須要用到60fps來達到想要的效果,固然超過60fps是沒有必要的。佈局
有了對Android渲染機制基本的認識之後,那麼咱們的卡頓的緣由就在於沒有辦法在16ms內完成該完成的操做, 而主要因素是在於沒有必要的layouts、invalidations、Overdraw。渲染的過程是由CPU與GPU協做完成, 下面一張圖很好的展現出了CPU和GPU的工做,以及潛在的問題,檢測的工具和解決方案。性能
咱們須要知道: 1.經過Hierarchy Viewer去檢測渲染效率,去除沒必要要的嵌套 2.經過Show GPU Overdraw去檢測Overdraw,最終能夠經過移除沒必要要的背景以及使用canvas.clipRect解決大多數問題。優化
Overdraw(過分繪製)描述的是屏幕上的某個像素在同一幀的時間內被繪製了屢次。在多層次的UI結構裏面, 若是不可見的UI也在作繪製的操做,這就會致使某些像素區域被繪製了屢次。這就浪費大量的CPU以及GPU資源。this
當設計上追求更華麗的視覺效果的時候,咱們就容易陷入採用愈來愈多的層疊組件來實現這種視覺效果的怪圈。這很容易致使大量的性能問題, 爲了得到最佳的性能,咱們必須儘可能減小Overdraw的狀況發生。spa
咱們能夠經過手機設置裏面的開發者選項,打開Show GPU Overdraw的選項,能夠觀察UI上的Overdraw狀況。
藍色,淡綠,淡紅,深紅表明了4種不一樣程度的Overdraw狀況,咱們的目標就是儘可能減小紅色Overdraw,看到更多的藍色區域。
Overdraw有時候是由於你的UI佈局存在大量重疊的部分,還有的時候是由於非必須的重疊背景。例如某個Activity有一個背景, 而後裏面 的Layout又有本身的背景,同時子View又分別有本身的背景。僅僅是經過移除非必須的背景圖片,這就可以減小大量的紅色Overdraw區域, 增長 藍色區域的佔比。這一措施可以顯著提高程序性能。
下面看一個簡單的展現ListView的例子: activity_main
<?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:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:background="@android:color/white" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/narrow_space" android:textSize="@dimen/large_text_size" android:layout_marginBottom="@dimen/wide_space" android:text="@string/header_text"/> <ListView android:id="@+id/id_listview_chats" android:layout_width="match_parent" android:background="@android:color/white" android:layout_height="wrap_content" android:divider="@android:color/transparent" android:dividerHeight="@dimen/divider_height"/> </LinearLayout>
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/id_chat_icon" android:layout_width="@dimen/avatar_dimen" android:layout_height="@dimen/avatar_dimen" android:src="@drawable/joanna" 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:background="@android:color/white" android:textColor="#78A" 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:text="@string/hello_world" android:gravity="bottom" android:id="@+id/id_chat_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:text="@string/hello_world" android:padding="@dimen/narrow_space" android:id="@+id/id_chat_date" /> </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:text="@string/hello_world" android:id="@+id/id_chat_msg" /> </LinearLayout> </LinearLayout>
Activity的代碼
public class OverDrawActivity01 extends AppCompatActivity { private ListView mListView; private LayoutInflater mInflater ; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_overdraw_01); mInflater = LayoutInflater.from(this); mListView = (ListView) findViewById(R.id.id_listview_chats); mListView.setAdapter(new ArrayAdapter<Droid>(this, -1, Droid.generateDatas()) { @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null ; if(convertView == null) { convertView = mInflater.inflate(R.layout.chat_item,parent,false); holder = new ViewHolder(); holder.icon = (ImageView) convertView.findViewById(R.id.id_chat_icon); holder.name = (TextView) convertView.findViewById(R.id.id_chat_name); holder.date = (TextView) convertView.findViewById(R.id.id_chat_date); holder.msg = (TextView) convertView.findViewById(R.id.id_chat_msg); convertView.setTag(holder); }else { holder = (ViewHolder) convertView.getTag(); } Droid droid = getItem(position); holder.icon.setBackgroundColor(0x44ff0000); holder.icon.setImageResource(droid.imageId); holder.date.setText(droid.date); holder