文章目录
CPU与GPU
CPU 的任务繁多,做逻辑计算外,还要做内存管理、显示操作,因此
在实际运算的时候性能会大打折扣,在没有 GPU 的时代,不能显示复
杂的图形,其运算速度远跟不上今天复杂三维游戏的要求。即使 CPU
的工作频率超过 2GHz 或更高,对它绘制图形提高也不大。这时 GPU
的设计就出来了, CPU 的控制器较为复杂,擅长各种复杂的逻辑运算,但不擅长数学尤其是浮点运算。
简单来说就是CPU将图形的计算公式传给GPU,也可理解为显卡,然后GPU拿到这个公式后就开始矢量图的绘制工作,Android 系统每隔 16ms 发出 VSYNC 信号 (1000ms/60=16.66ms) ,触发对 UI 进行渲染, 如果每次渲染都成功这样就能够达到流畅的画面所需要的 60fps ,为了能够实现 60fps ,这意味着计算渲染的大多数操作都必须在 16ms 内完成。如果很多帧都没有在这个时间内绘制完成,就会出来视觉上的卡顿现象。这也是60Hz 刷新频率由来。
过度绘制
概念:
GPU的绘制过程,类似于装修刷墙,一层一层的进行,并且16ms刷一次,每刷一遍就是一个图层,这样就会有图层覆盖的情况,下边的图层用户看不到,但其实GPU也已经进行了绘制。
过度绘制出现的原因
- 自定义控件中的 onDraw 方法做了多重绘制
- 布局层次太深,例如xml文件使用了迷之缩进,布局层叠太多,用户看不到的区域也进行了绘制,导致耗时增加
过渡绘制查看
开发者工具打开调试GPU过渡绘制
可以看到上边有很多颜色,颜色越浅,说明过度绘制越少,
- 蓝色部分,过渡绘制一次,就是无过渡绘制,只有一层
- 淡绿色 过渡绘制两次,说明有两层
- 淡红色 过度绘制三次
- 深红 过度绘制四次及以上
然后就各个击破进行优化就好了
过度绘制优化
- 减少背景重复
去掉单个activity的主题设置的属性
可以在setContentView之前getWindow().setBackgroundDrawable(null);
去掉所有activity主题设置中的属性
直接在styles.xml中设置<item name="android:windowBackground">@null</item>
- 使用裁减减少控件之间的重合部分
- 注意点:
1.能在一个平面显示的内容,尽量只用一个容器
2.尽可能把相同的容器合并merge
3.能复用的代码,用include处理,可以减少GPU重复工作
hierarchy view 查看布局结构图
在你的AndroidSDK的安装目录找到 Android\sdk\tools\monitor.bat,双击运行(运行前先在模拟器中启动项目,真机好像不太好使),切换到Hierarchy View ,找到自己的要查看的页面
这样就可以看到你的当前页面每个布局下一共有几层view了,试图减少view的层级可以提高GPU的渲染效率
自定义view裁剪被遮挡布局
public class DroidCardsView extends View {
//图片与图片之间的间距
private int mCardSpacing = 150;
//图片与左侧距离的记录
private int mCardLeft = 10;
private List<DroidCard> mDroidCards = new ArrayList<DroidCard>();
private Paint paint = new Paint();
public DroidCardsView(Context context) {
super(context);
initCards();
}
public DroidCardsView(Context context, AttributeSet attrs) {
super(context, attrs);
initCards();
}
/**
* 初始化卡片集合
*/
protected void initCards(){
Resources res = getResources();
mDroidCards.add(new DroidCard(res, R.drawable.alex,mCardLeft));
mCardLeft+=mCardSpacing;
mDroidCards.add(new DroidCard(res, R.drawable.claire,mCardLeft));
mCardLeft+=mCardSpacing;
mDroidCards.add(new DroidCard(res, R.drawable.kathryn,mCardLeft));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (DroidCard c : mDroidCards) {
drawDroidCard(canvas,c);
}
for (int i = 0; i < mDroidCards.size() - 1; i++){
drawDroidCard(canvas, mDroidCards,i);
}
drawLastDroidCard(canvas,mDroidCards.get(mDroidCards.size()-1));
invalidate();
}
private void drawDroidCard(Canvas canvas, DroidCard c) {
canvas.drawBitmap(c.bitmap,c.x,0f,paint);
}
/**
* 绘制最后一个DroidCard
* @param canvas
* @param c
*/
private void drawLastDroidCard(Canvas canvas,DroidCard c) {
canvas.drawBitmap(c.bitmap,c.x,0f,paint);
}
/**
* 绘制DroidCard
* @param canvas
* @param mDroidCards
* @param i
*/
private void drawDroidCard(Canvas canvas,List<DroidCard> mDroidCards,int i) {
DroidCard c = mDroidCards.get(i);
canvas.save();//备份画布
//裁剪画布
canvas.clipRect((float)c.x,0f,(float)(mDroidCards.get(i+1).x),(float)c.height);
canvas.drawBitmap(c.bitmap,c.x,0f,paint);
canvas.restore();//释放画布
}
}
include、ViewStub、merge
- include就是为了解决重复定义相同布局的问题
- ViewStub就是一个宽高都为0的一个View,它默认是不可见的。
只有通过调用 setVisibility() 函数或者 Inflate() 函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果。在ViewStub布局可显示之前,系统不会消耗资源去实例化里面的布局,可以节省系统资源消耗。
<ViewStub
android:id="@+id/view_stub"
android:layout_width="fill_parent"
android:layout_height="49dp"
android:layout="@layout/my_layout"
android:inflatedId="@+id/view_stub_inflated_id"/>
// 第一种使用方法:
//使用android:layout="@layout/my_layout"设置布局
ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);
//设置setVisibility,使布局文件实例化
viewStub .setVisibility(View.VISIBLE);
// 通过ViewStub的xml中的属性 inflatedId 来获取View
LinearLayout linearLayout =(LinearLayout) findViewById(R.id.view_stub_inflated_id);
if ( viewStub.getVisibility() == View.VISIBLE ) {
// 加载成功
}
// 第二种使用方法:
ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);
viewStub.setLayoutResource(R.layout.my_layout);
//使用 inflate,使布局文件实例化
LinearLayout linearLayout= (LinearLayout)viewStub.inflate();
if (linearLayout!= null){
//加载成功
}
- merge它可以删减多余的层级,优化UI,例如你的主布局文件是垂直的LinearLayout,这时使用include将 my_layout.xml 引入进来。新布局也是垂直的LinearLayout,那么这个新的LinearLayout就没有任何意义了。使用的话反而增加反应时间。这时可以使用标签优化。
merge 原理就是在解析xml时候,如果是 标签,那么直接将其中的子元素添加到merge 标签parent中,这样就保证了不会引入额外的层级