精通安卓性能优化-第八章(二)

来源:互联网 发布:微软程序员认证 编辑:程序博客网 时间:2024/06/07 22:44

复用Layout

和在C/C++中直接#include相似,Android在XML布局中支持<include>标签。简单的使<include>标签包含另外一个XML布局,如Listing 8-7所示。
<include>标签可以为两个目的使用:
(1) 你期望多次使用同样的布局
(2) 你的布局有个通用部分,依赖于设备配置(比如,屏幕方向:landscapte或者protrait)
Listing 8-7给出了当覆盖某些included的布局的属性的时候,如何多次包含一个布局。
Listing 8-7 多次包含布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">        <include android:id="@+id/myid1"             android:layout="@layout/mylayout"             android:layout_margin="9dip" />        <include android:id="@+id/myid2"             android:layout="@layout/mylayout"             android:layout_margin="9dip" />        <include android:id="@+id/myid3"             android:layout="@layout/mylayout"             android:layout_margin="9dip" />    </LinearLayout>

Listing 8-8给出了如何依赖于设备的方向仅包含布局一次,layout-land/mylayout.xml或者layout-port/mylayout.xml将被包含。(假设这里有两个版本的mylayout.xml,一个在res/layout-land目录,一个在res/layout-port目录)
Listing 8-8 依赖屏幕方向包含Layout

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">        <include android:id="@+id/myid"             android:layout="@layout/mylayout"              />    </LinearLayout>

当一个布局被包含,可以覆盖一些布局的属性,比如:
(1) root view的id(android:id)
(2) layout参数(android:layout_XXX)

如Listing 8-8所展示的,当布局被inflated的时候,布局的包含是动态的。包含应该在编译的时候完成,否则Android不知道include哪个布局(layout-land/mylayout.xml或者layout-port/mylayout.xml)。这和在C/C++中直接#include不同,C/C++被预处理器在编译时候处理。

View Stubs

就像我们在第一章看到的,懒汉式初始化是延迟初始化、提升性能、潜在的节省内存(当对象从来不会被创建)的方便的技术。
Android为了这个目的定义了ViewStub类。ViewStub是一个轻量级的不可见的View,可以用在你的布局中,允许懒汉式当你需要的时候inflate布局资源。Listing 8-9给出了Listing 8-8的一个修改的版本,给出如何在XML布局中使用ViewStub。
Listing 8-9 ViewStub In XML

<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/mystubid"        android:inflatedId="@+id/myid"        android:layout="@layout/mylayout"        />    </LinearLayout>

当线性布局被inflate,它仅包含一个子View:一个ViewStub。被ViewStub指向的布局,@layout/mylayout,可能是一个非常复杂的布局,需要很长的时间去inflate,但是之前没有被inflate。为了inflate这个定义在mylayout.xml中的layout,在你的代码中有两个选择,如Listing 8-10和Listing 8-11所示。
Listing 8-10 在代码中inflating Layout

ViewStub stub = (ViewStub)findViewById(R.id.mystubid);View inflatedView = stub.inflate(); // inflatedView将是定义在mylayout.xml中的布局

Listing 8-11 在代码使用setVisibility() inflate布局

View view = findViewById(R.id.mystubid);view.setVisibility(View.VISIBLE); // 使用inflated布局替换view stubview = findViewById(R.id.myid);  // 我们需要获取刚刚得到的inflated view

Inflate布局文件的第一种方式(在Listing 8-10)看起来更加方便,有些人可能会说它有一点问题:代码意识到view是一个stub,需要显式的去inflate它。在大多数情况下,这不是一个问题,它也将是在应用中使用ViewStub的时候推荐的方式。
Inflate布局的第二种方式在Listing 8-11给出,更加的通用的原因是它没有提到ViewStub类。然而,上面的代码仍然意识到它使用一个ViewStub的事实,因为它使用了两个固定id:R.id.mystubid和R.id.myid。为了完全通用,布局文件要定义的像8-12一样,inflate的方式像8-13一样。Listing 8-12和Listing 8-9相同,除了id是被创建的R.id.myid一个,而不是两个。相似的,Listing 8-13和Listing 8-11相同,除了R.id.mystubid被R.id.myid代替。

Listing 8-12 XML中的ViewStub,不覆盖ID

<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/myid"        android:layout="@layout/mylayout"        />    </LinearLayout>

Listing  8-13 在代码中使用setVisibility()  inflate布局

View view = findViewById(R.id.myid);view.setVisibility(View.VISIBLE);  // 使用inflated布局替换view stubview = findViewById(R.id.myid);   // 需要获取刚刚inflated view


作为事实,Listing 8-13不管是不是在布局中使用一个ViewStub都是可用的。如果你的代码通用性很重要,忽略在布局中是不是使用ViewStub,这个方法可以被选择。然而,findViewById()的两次调用会对性能造成负面影响。为了部分修正这个问题,因为当调用setVisibility(View.VISIBLE)的时候ViewStub将被从它的父View移除,你需要在第二次调用findViewById()之前首先检查view是否有一个parent。这样做是可选的,因为当你在你的布局中使用一个ViewStub依然需要第二次调用findViewById(),当没有使用ViewStub可以保证findViewById()只被调用一次。修改的代码Listing 8-14给出。
Listing 8-14 尽可能调用一次findViewById()

View view = findViewById(R.id.myid);view.setVisibility(View.VISIBLE);  // 使用inflated layout替换view stub(如果在布局中使用stub)if (view.getParent() == null) {    // 使用了stub,所以我们要使用新inflated view替换它    view = findViewById(R.id.myid);} else {    // 不做任何事情,我们第一次找到的view是我们期望的}

在Listing 8-13和Listing 8-14给出的不是通用的,你通常不需要这个方式。通常,你的代码了解布局中使用了ViewStub是可接受的,因此简单的使用8-10的代码就够了。

Layout Tools

为了帮助你创建最好的布局,Android SDK提供了两个简单的工具:hierarchyviewer和layoutopt。你可以在SDK tools目录找到这些工具。

Hierarchy Viewer

Android SDK有一个非常有用的工具查看和分析应用的布局:hierarchyviewer。作为事实,Figure 8-1和Figure 8-2都是使用这个工具产生的。除了给你应用布局的细节,这个工具同样测量它需要多长时间去测量、布局、draw每个widget,识别出哪个widget需要比较长的时间去测量、布局和draw。
你可以使用hierarchyviewer作为一个单独的工具,或者直接在Eclipse中使用Hierarchy View视图。

layoutopt(在SDK 16以后使用lint)

Android SDK有另外一个工具可以帮助你分析你的布局:layoutopt。这个工具分析你的布局文件,给出使布局更加有效的改变建议。
比如,对Listing 8-5布局使用layoutopt工具,得到如下的输出:


The root-level <FrameLayout /> can be replaced with <merge/>


事实证明,这是Listing 8-6做的。对Listing 8-6使用layoutopt不会有任何的提醒。

TIP:使用最新的layoutopt工具,以保证你得到最好的结果。

保证layoutopt工具报的所有的问题在发布应用前有认真的对待。不优化布局会使应用变慢,而补救这样的布局通常很简单。

0 0