View的刷新
前面学习过,view的刷新过程分为三步,measure,layout和draw。
7.Android View的刷新.jpg
所以一般的卡顿问题都可以从这三个过程分析,通过systrace查看哪一个流程比较耗时。
下面逐个看看:
1.measure
measure方法是为了测量view的大小(宽和高)。
这里耗时的原因大概率跟布局有关,不同类型的布局测量view大小的次数差别会比较大。比如RelativeLayout需要给每一个view的水平和竖直方向都测量一次,或者是嵌套较多的布局,也会需要多次测量大小。
举个例子,我们应该尽量少在每个子view分别设置padding和margin。
可以简单理解说padding为内边距;margin为外边距。
下面给了一张图可以明显看出两者差异:
在measure的时候,对于margin和padding都是需要单独计算的。
2.layout
layout方法主要是为了确定view的位置。
layout方法需要确定子view相对于父容器在上下左右四个方向的距离。而且layout方法还需要从顶层view一级一级逐层确定子view的位置,所以嵌套过多的布局就很容易耗时。
3.draw
draw方法用于绘制每个view自己的内容,同时也是从顶层view逐级向下绘制的。
这里的经验是,不要在UI线程进行太复杂的逻辑运算,而是通过异步的方式,数据加载完成后通过postInvalidate通知UI线程刷新。
ListView
比较常见的卡顿还有列表滑动卡顿,也就是obtainView耗时。
这里很推荐一个做法,就是用RecyclerView代替ListView。或者是将getView中某些耗时操作通过异步实现。
比如下面的代码逻辑,需要在getView中加载应用图标,这是一个比较耗时操作。
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = new PowerProtectInfoView(PowerUsageModelActivity.this);
}
PowerProtectInfoView infoView = (PowerProtectInfoView) convertView;
if (!infoView.isDividerVisible()) {
convertView = new PowerProtectInfoView(PowerUsageModelActivity.this);
infoView = (PowerProtectInfoView) convertView;
}
if ((mAppList != null) && (mAppList.size() > 0)) {
AppInfoWrapper appWrapper = mAppList.get(position);
LoadTask loadTask = new LoadTask();
loadTask.setInfoView(infoView);
loadTask.execute(appWrapper);
infoView.setTitle(appWrapper.label);
infoView.setTitleColor(getResources().getColor(R.color.color_preference_title_color_normal));
infoView.setDotVisible(appWrapper.hasDot);
}
return convertView;
}
就可以通过AsyncTask实现一个异步操作。
private class LoadTask extends AsyncTask<AppInfoWrapper, Integer, Drawable> {
private PowerProtectInfoView mInfoView;
public void setInfoView(PowerProtectInfoView infoView) {
mInfoView = infoView;
}
@Override
protected Drawable doInBackground(AppInfoWrapper... wrappers) {
Log.d(TAG, "doInBackground");
AppInfoWrapper appWrapper = wrappers[0];
ApplicationInfo applicationInfo = appWrapper.appInfo;
//Drawable icon = applicationInfo.loadIcon(getPackageManager());
OppoPackageManager mOpm = new OppoPackageManager(PowerUsageModelActivity.this);
Drawable icon = mOpm.getApplicationIconCache(applicationInfo);
if (null == icon) {
icon = getResources().getDrawable(R.drawable.pm_power_usage_system);
}
if (icon != null) {
icon = OppoIconUtils.getDrawableForListUse(PowerUsageModelActivity.this, icon);
}
return icon;
}
@Override
protected void onPostExecute(Drawable icon) {
mInfoView.setIcon(icon);
Log.d(TAG, "onPostExecute");
}
}
参考
Android 不同布局类型measure、layout、draw耗时对比
android listview 滑动卡顿问题解决
Android 中的卡顿丢帧原因概述 - 应用篇
ListView和GridView列表滑动卡顿问题的优化方法汇总
深入探索Android卡顿优化(上)
Android 中的卡顿丢帧原因概述 - 系统篇
Android性能优化之绘制优化
android:padding和android:margin的区别 详解
从 View 绘制谈性能优化
网友评论