本实践来至于 workable 的工程师 Pavlos-Petros Tournaris. 介绍了在 Workable 安卓应用中 RecyclerView 滚动不流畅的问题,以及如何使用 Allocation Tracker 来定位问题并优化滚动流畅性。
TableLayout
在 Workable 安卓应用中,使用 TableLayout 来显示候选人的信息布局, Allocation Tracker 报告显示 TableLayout 消耗了很多资源。 最后他们使用 LinearLayout 和 layout_weight 属性来实现同样的布局,性能比 TableLayout 要好一些。
优化之前的布局:
XHTML
android:id="@+id/candidate_browse_job_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="0"
android:stretchColumns="1">
android:id="@+id/candidate_browser_job"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
android:id="@+id/candidate_stage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
android:id="@+id/candidate_browse_job_table"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="0"
android:stretchColumns="1">
android:id="@+id/candidate_browser_job"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
android:id="@+id/candidate_stage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
优化之后的布局:
XHTML
android:id="@+id/candidate_browse_job_table"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:id="@+id/candidate_browser_job"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
android:id="@+id/candidate_stage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
android:id="@+id/candidate_browse_job_table"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:id="@+id/candidate_browser_job"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
android:id="@+id/candidate_stage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
/>
注意:把 TableLayout 替换为 LinearLayout 对性能只有很少的提升。
android:textAllCaps=”true”
在 Allocation Tracker 报告中显示,设置了一个字段的大写字母形式会导致很多内存申请(allocation)。后来使用 String 对象的 .toUpperCase() 函数来转换字符串,发现效果要好很多。
优化之前的布局文件:
XHTML
...
android:textAllCaps="true"
...
/>
1
2
3
4
5
...
android:textAllCaps="true"
...
/>
优化为使用 Java 代码:
Java
this.snoozedText = snoozedText.toUpperCase();
1
2
this.snoozedText=snoozedText.toUpperCase();
下图为 TextView textAllCaps 属性设置为 true 的时候,内存申请的情况报告:
通过查看 TextView 的代码发现,当设置 textAllCaps 为 true 的时候, TextView 内部每次都创建一个新的 Transformation 来处理字符串:
在 RecyclerView 和 ListView 这种滚动布局中使用这种方式是有问题的。
RecyclerView.onViewRecycled()
RecyclerView.onViewRecycled() 函数在一个 View 被回收的时候调用,所以在这个函数里面可以释放资源。 Workable 应用中使用了 DataBinding, 所以在这个函数中取消 OnPropertyChangedCallbacks 并清理 ViewModel 上的数据绑定操作,则可以提高性能。同时还可以释放 Glide 加载的用户头像图片资源。
Java
@Override
public void onViewRecycled(Candidates holder) {
if(holder != null) {
holder.binding.getCandidateVM().removePropertyChangedCallback();
holder.binding.setCandidateVM(null);
holder.binding.setHighlightTerm(null);
holder.binding.setShowJobTitle(false);
holder.binding.setShowStage(false);
holder.binding.executePendingBindings();
Glide.clear(holder.binding.candidateBrowserAvatar);
holder.binding.candidateBrowserAvatar.setImageDrawable(null);
}
super.onViewRecycled(holder);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
publicvoidonViewRecycled(Candidatesholder){
if(holder!=null){
holder.binding.getCandidateVM().removePropertyChangedCallback();
holder.binding.setCandidateVM(null);
holder.binding.setHighlightTerm(null);
holder.binding.setShowJobTitle(false);
holder.binding.setShowStage(false);
holder.binding.executePendingBindings();
Glide.clear(holder.binding.candidateBrowserAvatar);
holder.binding.candidateBrowserAvatar.setImageDrawable(null);
}
super.onViewRecycled(holder);
}
RecyclerView 缓存
为了更好的提高滚动的流畅性,可以加大 RecyclerView 的缓存,用空间换时间:
Java
binding.fragmentCandidateBrowseList.setItemViewCacheSize(30);
binding.fragmentCandidateBrowseList.setDrawingCacheEnabled(true);
binding.fragmentCandidateBrowseList.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
1
2
3
4
binding.fragmentCandidateBrowseList.setItemViewCacheSize(30);
binding.fragmentCandidateBrowseList.setDrawingCacheEnabled(true);
binding.fragmentCandidateBrowseList.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
结论
Allocation Tracker 可以很方便的查看内存情况,对于分析界面卡顿非常有效,每个开发者都应该熟练使用 Allocation Tracker 来解决内存以及性能问题。