Skip to main content

Android Layout Inflation 优化

减少布局的嵌套层级

例如,使用 ConstraintLayout 取代 LinearLayout

异步加载

ViewGroup动态添加子View时,我们往往使用一个 layout 的 XML 来 inflate 一个 view,然后将其 add 到父容器。 inflate 包含对 XML 文件的读取和解析(IO 操作),并通过反射创建View树。当XML文件过大或页面层级过深,布局的加载就会较为耗时。 由于这一步并非 UI 操作,可以转移到非主线程执行,为此,官方在扩展包提供了AsyncLayoutInflater

以 inflate 一个简单的 layout 10次为例:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".jank.JankActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="hello" />
</FrameLayout>
for (int i = 0; i < 10; i++) {
layoutInflater.inflate(R.layout.view_jank, container, false);
}

同步 inflate 约需耗时 12 ms。

AsyncLayoutInflater asyncLayoutInflater = new AsyncLayoutInflater(this);
AsyncLayoutInflater.OnInflateFinishedListener onInflateFinishedListener = new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
public void onInflateFinished(@NonNull View view, int resid, @Nullable ViewGroup parent) {
if (parent != null) {
parent.addView(view);
}
}
};

for (int i = 0; i < 10; i++) {
asyncLayoutInflater.inflate(R.layout.view_jank, container, onInflateFinishedListener);
}

使用AsyncLayoutInflater异步 inflate 后,主线程就不再有 inflate 的耗时了。

适用场景

动态加载布局较复杂的View

懒加载

使用ViewStub占位,需要显示时才加载真正的View

<ViewStub
android:id="@+id/stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout="@layout/real_view" />
ViewStub stub = findViewById(R.id.stub);
if (stub != null) {
stub.inflate(); // inflate一次以后,view树中就不再包含这个ViewStub了
}
适用场景

根据条件决定是否显示的View,例如:

  • 网络请求失败的提示
  • 列表为空的提示
  • 新内容、新功能的引导,因为引导基本上只显示一次

延迟加载

在主线程注册IdleHandler回调,当主线程"空闲"时才执行回调中的逻辑。

Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 当该Looper线程没有message要处理时才执行
return false;
}
});
适用场景

次要页面元素的加载和渲染,比如未读信息的红点、新手引导等。