Android Performance: Overdraw的定位
Author: nex3z
2016-07-13
Android Performance系列的内容整理自Udacity的Android Performance课程,是对该系列课程的笔记和总结。
过度绘制(Overdraw)指的是一个像素在一帧里被绘制了多次,旧的绘制会被新的绘制覆盖,是无效的,造成对GPU的浪费。Overdraw通常发生于多个元素重合的情况,位于上层的元素会覆盖住下层的元素,使下层的元素不可见,此时如果还对下层元素进行绘制,就会发生Overdraw。
0. 示例代码
下面使用的代码可以在这里找到,应用模拟了一个聊天App的界面,如图1所示。

图1
1. 可视化Overdraw
Android提供了可视化Overdraw的工具,用于定位发生Overdraw的区域,只需在开发者模式的选项中点击“Debug GPU overdraw”,并选择“Show overdraw areas”即可。

此时回到之前的App,界面如图3。

不同的背景颜色代表了不同的Overdraw次数,如图4所示:
- 原色:没有Overdraw
- 蓝色:一次Overdraw
- 绿色:两次Overdraw
- 粉色:三次Overdraw
- 红色:四次以上Overdraw

图4
2. 移除不必要的填充背景
从图3中背景的大片绿色可以猜测这里手动设置了一个纯色的背景,导致整个应用的背景发生Overdraw。在activity_chatum_latinum.xml可以看到,这里设置了一个白色被背景@android:color/white
:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_chatum_latinum_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context=".MainActivity"
tools:ignore="MergeRootFrame" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_chatum_latinum_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context=".MainActivity"
tools:ignore="MergeRootFrame" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_chatum_latinum_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context=".MainActivity"
tools:ignore="MergeRootFrame" />
为了移除这里的背景,除了直接修改上面的android:background
,还可以使用getWindow().setBackgroundDrawable(null)
。修改ChatumLatinumActivity.java的onCreate()
如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatum_latinum);
getWindow().setBackgroundDrawable(null);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_chatum_latinum_container, new ChatsFragment())
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatum_latinum);
getWindow().setBackgroundDrawable(null);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_chatum_latinum_container, new ChatsFragment())
.commit();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chatum_latinum);
getWindow().setBackgroundDrawable(null);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_chatum_latinum_container, new ChatsFragment())
.commit();
}
}
此时App的背景变成蓝色,Overdraw减少了一次,如图5。

图5
除了activity_chatum_latinum.xml,在chat_item.xml和fragment_chats.xml中还有4个不必要的背景,全部删除后,Overdraw大幅减少,如图6。

图6
3. 根据实际需要添加背景
注意图6中的头像大部分是绿的,说明头像发生了两次Overdraw;其中有一个空头像是蓝色的,说明头像为空时发生了一次Overdraw。在ChatAdapter.java的getView()
中可以看到头像的相关逻辑。
public View getView(int position, View view, ViewGroup parent) {
if (chat.getAuthor().getAvatarId() != 0) {
Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into(
chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor());
@Override
public View getView(int position, View view, ViewGroup parent) {
//...
if (chat.getAuthor().getAvatarId() != 0) {
Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into(
chat_author_avatar);
}
chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor());
return view;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
//...
if (chat.getAuthor().getAvatarId() != 0) {
Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into(
chat_author_avatar);
}
chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor());
return view;
}
这里无论是否有头像,都会使用setBackgroundColor()
添加背景,当头像部位空时,就会发生Overdraw,修改这里的逻辑为只在头像为空时才添加背景:
public View getView(int position, View view, ViewGroup parent) {
if (chat.getAuthor().getAvatarId() == 0) {
Picasso.with(getContext()).load(android.R.color.transparent).into(chat_author_avatar);
chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor());
Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into(chat_author_avatar);
chat_author_avatar.setBackgroundColor(Color.TRANSPARENT);
@Override
public View getView(int position, View view, ViewGroup parent) {
//...
if (chat.getAuthor().getAvatarId() == 0) {
Picasso.with(getContext()).load(android.R.color.transparent).into(chat_author_avatar);
chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor());
} else {
Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into(chat_author_avatar);
chat_author_avatar.setBackgroundColor(Color.TRANSPARENT);
}
return view;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
//...
if (chat.getAuthor().getAvatarId() == 0) {
Picasso.with(getContext()).load(android.R.color.transparent).into(chat_author_avatar);
chat_author_avatar.setBackgroundColor(chat.getAuthor().getColor());
} else {
Picasso.with(getContext()).load(chat.getAuthor().getAvatarId()).into(chat_author_avatar);
chat_author_avatar.setBackgroundColor(Color.TRANSPARENT);
}
return view;
}
此时Overdraw进一步减少,如图7。

图7