相关文章
Android绘制优化(一)绘制性能分析javascript
咱们知道一个界面的测量和绘制是经过递归来完成的,减小布局的层数就会减小测量和绘制的时间,从而性能就会获得提高。固然这只是布局优化的一方面,那么如何来进行布局的分析和优化呢?本篇文章会给你一个满意的答案。java
在讲到如何去布局优化前,咱们先来学习两种布局优化的工具。android
Hierarchy Viewer是Android SDK自带的可视化的调试工具,用来检查布局嵌套和绘制的时间。须要注意的是在在Android的官方文档中提到:出于安全考虑,Hierarchy Viewer只能链接Android开发版手机或是模拟器。
首先咱们在Android Studio中选择Tools->Android->Android Device Monitor,在Android Device Monitor中选择Hierarchy Viewer ,以下图所示:canvas
Hierarchy Viewer中有4个四个子窗口,它们的的做用为:安全
根据上面讲到的Hierarchy Viewer的4个四个子窗口,咱们能够很容易的查看咱们布局控件的层级关系。固然Hierarchy Viewer还能够查看某一个View的耗时,咱们能够选择某一个View,而后单击下图红色箭头标识的按钮,这里咱们把他简称为Layout Time按钮。 微信
从图中能够看出,被选中的LinearLayout给出了自身Measure、Layout和Draw的耗时,而且它所包含的View中都有了三个指示灯,分别表明当前View在Measure、Layout和Draw的耗时,绿色表明比其余50%View的同阶段(好比Measure阶段)速度要快,黄色则表明比其余50%View同阶段速度要慢,红色则表明比其余View同阶段都要慢,则须要注意了。若是想要看View的具体耗时,则点击该View就能够了。ide
Android lint是在ADT 16提供的新工具,它是一个代码扫描工具,经过代码静态检查来发现代码出现的潜在问题,并给出优化建议。检查的范围主要有如下几点:工具
Android Lint功能十分强大,这里咱们只关注XML布局检查,咱们能够经过Android Studio的Analyze->Inspect Code来配置检查的范围,以下图所示。
布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent"> ...复制代码
第三行的Namespace 确实没有被用到。若是想要自定义Android Lint的检查提示,能够经过File->Settings->Editor->Inspections中来配置Android Lint,以下图所示。
性能
布局的优化方法不少,主要包括合理运用布局、Include、Merge、ViewStub,下面咱们来一一对这些内容进行讲解。
合理运用布局
咱们经常使用的布局主要有LinearLayout、RelativeLayout和FrameLayout等,合理的使用它们可使得Android绘制工做量变少,性能获得提升。咱们来举个简单的例子。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Merge" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ViewStub" />
</LinearLayout>
</LinearLayout>复制代码
上面的代码用了两个LinearLayout来进行布局,运行效果以下图所示。
能够看到咱们的布局共有3层,一共含有5个View。若是咱们用RelativeLayout来进行改写呢?代码以下所示。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
<TextView
android:id="@+id/tv_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@id/tv_text1"
android:text="Merge" />
<TextView
android:id="@+id/tv_text3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/tv_text2"
android:layout_marginLeft="10dp"
android:layout_toRightOf="@+id/tv_text1"
android:text="ViewStub" />
</RelativeLayout>复制代码
咱们只用了一个RelativeLayout来进行布局。用Hierarchy Viewer来查看层级状况,以下图所示。
布局共有两层,一共含有4个View。从这里咱们就能够看出咱们用RelativeLayout减小了一层的布局,固然这只是一个简单例子,若是布局复杂,那么合理的用RelativeLayout来替代LinearLayout会减小不少层布局。
通常状况下,RelativeLayout的性能是比LinearLayout低,由于RelativeLayout中的View的排列方式是基于彼此依赖的。可是若是布局层数较多时,若是能用RelativeLayout来实现,仍是推荐用RelativeLayout。
一个很常见的场景就是,多个布局须要复用一个相同的布局,好比一个TitleBar。若是这些界面都要加上这个相同布局TitleBar,维护起来就就很麻烦,咱们须要复制TitleBar的布局到每一个须要添加的界面,这样容易发生遗漏。若是须要修改TitleBar则须要去每一个引用TitleBar的布局进行修改。为了解决这些问题,咱们能够用Include标签来解决。
首先咱们先来写一个简单的TitleBar布局:titlebar.xml 以下所示。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@android:color/darker_gray">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ico_left"
android:padding="3dp"
android:layout_gravity="center"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="绘制优化" />
</LinearLayout>复制代码
这个TitleBar由ImageView和TextView组成,下面咱们将TitleBar引入到咱们此前用过的布局中,以下所示。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/titlebar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
...
</RelativeLayout>
</LinearLayout>复制代码
能够看到咱们用include标签引入了titlebar布局,运行效果以下图所示。
Merge意味着合并,在合适的场景使用Merge标签能够减小多余的层级。Merge标签通常和Include标签搭配使用,上面的例子,咱们用Hierarchy Viewer来查看布局层级,以下图所示。
能够看到咱们用Include标签引用的布局的根布局是一个LinearLayout。若是咱们使用Merge标签来替换LinearLayout呢?titlebar.xml 的代码以下所示。
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@android:color/darker_gray
>
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:src="@drawable/ico_left"
android:padding="3dp"
android:layout_gravity="center"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="绘制优化" />
</merge>复制代码
这时咱们再用Hierarchy Viewer来查看布局层级,以下图所示。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.example.liuwangshu.moonlayout.TitleBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@android:color/darker_gray">
</com.example.liuwangshu.moonlayout.TitleBar>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="布局优化" />
...
</RelativeLayout>
</LinearLayout>复制代码
上面布局中TitleBar就是一个自定义View,它继承LinearLayout。咱们在TitleBar标签中添加此前的LinearLayout的属性:android:orientation和android:background。
一个很常见的开发场景就是咱们想要一个布局时,并非全部的控件都须要显示出来,而是显示出一部分,对于这种状况,咱们通常采用的方法就是使用View的GONE和INVISIBLE,可是这种方法效率不高,虽然是达到了隐藏的目的,可是仍在布局当中,系统仍然会解析它们,咱们能够用ViewStub来解决这一问题。
ViewStub是轻量级的View,不可见而且不占布局位置。当ViewStub调用inflate方法或者设置可见时,系统会加载ViewStub指定的布局,而后将这个布局添加到ViewStub中,所以,在对ViewStub调用inflate方法或者设置可见时,它是不占布局空间和系统资源的,它主要的目的就是为目标视图占用一个位置。所以,使用ViewStub能够提升界面初始化的性能,从而提升界面的加载速度。
咱们首先在布局中加入ViewStub标签,布局代码以下所示。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ViewStub android:id="@+id/viewsub" android:layout_width="match_parent" android:layout_height="40dp" android:layout="@layout/titlebar"/> ... </LinearLayout>复制代码
ViewStub标签中用android:layout引用了此前写好的布局titlebar.xml。这时咱们运行程序,ViewStub标签所引用的布局是显示不出来的,由于引该局尚未加载到ViewStub中,接下来在代码中使用ViewStub:
public class MainActivity extends AppCompatActivity {
private ViewStub viewsub;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewsub= (ViewStub) findViewById(R.id.viewsub);
// viewsub.inflate();//1
viewsub.setVisibility(View.VISIBLE);//2
}
}复制代码
可使用注释1和注释2处的代码来将ViewStub引用的布局加载到ViewStub中,这样引用的布局就显示了出来。
在使用ViewStub时须要主要如下问题:
什么是过分绘制呢?咱们来打个比方,假设你要粉刷房子的墙壁,一开始刷了绿色,接着又刷了黄色,这样黄色就将绿色盖住,也就说明第一次的大量粉刷工做白作了。一样手机屏幕绘制也是如此,过分绘制是指在屏幕上某个像素在同一帧的时间内被绘制屡次,从而浪费了GPU和CPU的资源。产生这一缘由主要有两个缘由:
过分绘制是不可避免的,可是过多的过分绘制会浪费不少资源,而且致使性能问题,所以,避免过分绘制是十分必要的。咱们能够用Android系统中自带的工具来检测过分绘制。首先要保证系统版本在Android 4.1以上,接着在开发者选项中打开调试GPU过分绘制选项就能够进入GPU过分绘制模式,以下图所示。
这时屏幕会出现出各类颜色,主要有如下几种,以下图所示。
各个颜色的定义为:
最理想的是蓝色,一个像素只绘制一次,合格的页面绘制是白色、蓝色为主,绿色以上区域不能超过整个的三分之一,颜色越浅越好。
避免过分绘制主要有如下几个方案:
1.移除不须要的background。
2.在自定义View的OnDraw方法中,用canvas.clipRect来指定绘制的区域,防止重叠的组件发生过分绘制。
欢迎关注个人微信公众号,第一时间得到博客更新提醒,以及更多成体系的Android相关原创技术干货。
扫一扫下方二维码或者长按识别二维码,便可关注。