Android Performance: Overdraw的定位
Android Performance系列的内容整理自Udacity的Android Performance课程,是对该系列课程的笔记和总结。
过度绘制(Overdraw)指的是一个像素在一帧里被绘制了多次,旧的绘制会被新的绘制覆盖,是无效的,造成对GPU的浪费。Overdraw通常发生于多个元素重合的情况,位于上层的元素会覆盖住下层的元素,使下层的元素不可见,此时如果还对下层元素进行绘制,就会发生Overdraw。
0. 示例代码
下面使用的代码可以在这里找到,应用模拟了一个聊天App的界面,如图1所示。
1. 可视化Overdraw
Android提供了可视化Overdraw的工具,用于定位发生Overdraw的区域,只需在开发者模式的选项中点击“Debug GPU overdraw”,并选择“Show overdraw areas”即可。
此时回到之前的App,界面如图3。
不同的背景颜色代表了不同的Overdraw次数,如图4所示:
- 原色:没有Overdraw
- 蓝色:一次Overdraw
- 绿色:两次Overdraw
- 粉色:三次Overdraw
- 红色:四次以上Overdraw
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" />
为了移除这里的背景,除了直接修改上面的android:background
,还可以使用getWindow().setBackgroundDrawable(null)
。修改ChatumLatinumActivity.java的onCreate()
如下:
@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。
除了activity_chatum_latinum.xml,在chat_item.xml和fragment_chats.xml中还有4个不必要的背景,全部删除后,Overdraw大幅减少,如图6。
3. 根据实际需要添加背景
注意图6中的头像大部分是绿的,说明头像发生了两次Overdraw;其中有一个空头像是蓝色的,说明头像为空时发生了一次Overdraw。在ChatAdapter.java的getView()
中可以看到头像的相关逻辑。
@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,修改这里的逻辑为只在头像为空时才添加背景:
@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。