Android中XML布局文件的使用非常频繁,在加载XML布局的时候,如果对XML文件其进行优化,将会提高加载的效率。

HierarchyViewer工具

再开始介绍之前先说一下HierarchyViewer工具的使用。
不合理的布局会使我们的应用程序UI性能变慢,HierarchyViewer能够可视化的角度直观地获得UI布局设计结构和各种属性的信息,帮助我们优化布局设计。HierarchyViewer是我们优化程序的工具之一,它是Android自带的非常有用的工具,可以帮助我们更好地检视和设计用户界面(UI),绝对是UI检视的利器。

减少XML中的布局嵌套

在android加载XML布局文件,创建View或者ViewGroup的时候,XML文件的布局的深度和广度都会对这个过程造成影响。因此XML文件布局的深度和广度都会使创建View的过程更加的耗时,尤其是在布局文件多层嵌套的情况下,因此我们在进行布局的时候,应该尽量考虑使用扁平化的结构,减少布局的深度和复杂性。

以下面的布局为例:

xml解析 java XML解析性能优化_xml

使用RelativeLayout作为根布局

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="demon.com.xmloptimization.MainActivity">

    <ImageView
        android:id="@+id/icon1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher_round"/>

    <ImageView
        android:id="@+id/icon2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/icon1"
        android:src="@mipmap/ic_launcher_round"/>

    <ImageView
        android:id="@+id/icon3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@id/icon2"
        android:layout_below="@+id/icon2"
        android:src="@mipmap/ic_launcher_round"/>
</RelativeLayout>

使用HierarchyViewer查看布局深度:

xml解析 java XML解析性能优化_布局_02

使用LinearLayout作为根布局

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    tools:context="demon.com.xmloptimization.MainActivity">


    <ImageView
        android:id="@+id/icon1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher_round"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/icon2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher_round"/>


        <ImageView
            android:id="@+id/icon3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/ic_launcher_round"/>
    </LinearLayout>
</LinearLayout>

使用HierarchyViewer查看布局深度:

xml解析 java XML解析性能优化_xml解析 java_03

对比可知道明显使用LinearLayout比使用RelativeLayout多一层结构,更加耗时。

使用include标签

在应用的多个界面中,经常会有几个界面存在相同或者类似的部分,例如标题栏,导航栏之类的。这些重复的界面如果一一写下来,既浪费时间精力,又不方便统一管理,例如需要改动的时候,在这种情况下,可以把这个重复的界面布局独立出来,然后通过include标签,将这个布局导入需要引用的地方,这样就可以只维护这个独立出来的布局文件即可。

我们将一个ImageView独立写出来:
layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:src="@mipmap/ic_launcher_round">

</ImageView>

使用include:
activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="demon.com.xmloptimization.MainActivity">

    <include layout="@layout/layout"/>

    <include
        layout="@layout/layout"/>

    <include layout="@layout/layout"/>

</LinearLayout>

xml解析 java XML解析性能优化_性能优化_04

使用merge标签

merge标签可以用来代替布局文件中的根节点,例如LinearLayout,RelativeLayout,FrameLayout等元素。配合include标签,可以减少布局的嵌套。例如:
layout.xml:

<merge xmlns:android="http://schemas.android.com/apk/res/android">

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/add"/>

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/delete"/>

</merge>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="demon.com.xmloptimization.MainActivity">

    <include layout="@layout/layout"/>

    <include
        layout="@layout/layout"/>
</LinearLayout>

效果:

xml解析 java XML解析性能优化_性能优化_05


使用include标签将以上布局复用的时候,将会直接把两个Button放在include标签的位置,而不会再使用其他容器来包装这两个Button,这样就会减少不必要的嵌套,有利于布局的扁平化。

使用ViewStub

在实际应用中,可能存在一部分界面是处于隐藏状态的,在一定的条件下,才会展示出来。我们一般会将这部分节目使用setVisibility(View.GONE)方法将其隐藏起来,但是我们更推荐使用ViewStub来达到相同的目的。因为ViewStub是一个处于不可见状态的View,初始状态时不会被加载出来,不会浪费资源,而处于View.GONE状态的View也是需要被加载的。ViewStub的使用方法类似include标签,需要在其中声明需要加载的布局文件。
layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:src="@mipmap/ic_launcher_round">

</ImageView>

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="demon.com.xmloptimization.MainActivity">

    <ViewStub
        android:id="@+id/viewstub"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout="@layout/layout"/>

    <include
        layout="@layout/layout"/>
     </LinearLayout>

初始状态:

xml解析 java XML解析性能优化_布局_06

显示ViewStub

  1. 使用inflate():
public class MainActivity extends AppCompatActivity {
    private View view;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (view != null) {
            view.setVisibility(View.VISIBLE);
            return;
        }
        ViewStub viewStub = (ViewStub) findViewById(R.id.viewstub);
        view = viewStub.inflate();

    }
}
  1. 直接setVisibility(View.VISIBLE)
ViewStub viewStub = (ViewStub) findViewById(R.id.viewstub);
        viewStub.setVisibility(View.VISIBLE);

效果:

xml解析 java XML解析性能优化_性能优化_07

值得注意的地方:

  1. 必须指定android:layout_width=”“与android:layout_height=”“
  2. android:layout=”“不能使用merge标签

减少不必要的infalte

对于inflate的布局可以直接缓存,用全部变量代替局部变量,避免下次需再次inflate。
如上代码中:

if (view != null) {
            view.setVisibility(View.VISIBLE);
            return;
        }

再如LsitView中常见的:

if (convertView == null) {
      convertView = inflater.inflate(R.layout.list_item, null);
      holder = new ViewHolder();
      ……
      convertView.setTag(holder);
   } else {
      holder = (ViewHolder)convertView.getTag();
   }

其他

(1) 用SurfaceView或TextureView代替普通View
SurfaceView或TextureView可以通过将绘图操作移动到另一个单独线程上提高性能。
普通View的绘制过程都是在主线程(UI线程)中完成,如果某些绘图操作影响性能就不好优化了,这时我们可以考虑使用SurfaceView和TextureView,他们的绘图操作发生在UI线程之外的另一个线程上。
因为SurfaceView在常规视图系统之外,所以无法像常规试图一样移动、缩放或旋转一个SurfaceView。TextureView是Android4.0引入的,除了与SurfaceView一样在单独线程绘制外,还可以像常规视图一样被改变。

(2) 使用RenderJavascript
RenderScript是Adnroid3.0引进的用来在Android上写高性能代码的一种语言,语法给予C语言的C99标准,他的结构是独立的,所以不需要为不同的CPU或者GPU定制代码代码。

(3) 使用OpenGL绘图
Android支持使用OpenGL API的高性能绘图,这是Android可用的最高级的绘图机制,在游戏类对性能要求较高的应用中得到广泛使用。
Android 4.3最大的改变,就是支持OpenGL ES 3.0。相比2.0,3.0有更多的缓冲区对象、增加了新的着色语言、增加多纹理支持等等,将为Android游戏带来更出色的视觉体验。

(4) 尽量为所有分辨率创建资源
减少不必要的硬件缩放,这会降低UI的绘制速度,可借助Android asset studio