Android X2J - 让你的 XML 布局变成 Java 来运行

4,982 阅读3分钟

X2J 即 XML to Java。

Android X2J是一个 Gradle 插件,它可以在 APK 编译期间 自动 将你的 XML 布局翻译成 Java 代码,并 自动 注入到 Activity 和自定义布局中。

传统的布局加载方案都是在运行时解析 XML(通过 LayoutInflater),而 Android X2J 则可以将 XML 的解析提前到编译时,从而可以让布局加载速度提升 200%。

了不起的 X2C

不知道你是否了解过 X2C 这个库。简单来说,X2C 会在编译时,将 XML 布局翻译成对应的 Java 代码。

原始的 xml

<?xml version="1.0" encoding="utf-8"?>
<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"
  android:paddingLeft="10dp">

  <include
      android:id="@+id/head"
      layout="@layout/head"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_centerHorizontal="true" />

  <ImageView
      android:id="@+id/ccc"
      style="@style/bb"
      android:layout_below="@id/head" />
</RelativeLayout>

X2C 生成的 Java 文件

public class X2C_2131296281_Activity_Main implements IViewCreator {
  @Override
  public View createView(Context ctx, int layoutId) {
    	Resources res = ctx.getResources();

        RelativeLayout relativeLayout0 = new RelativeLayout(ctx);
        relativeLayout0.setPadding((int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,res.getDisplayMetrics())),0,0,0);

        View view1 =(View) new X2C_2131296283_Head().createView(ctx,0);
        RelativeLayout.LayoutParams layoutParam1 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
        view1.setLayoutParams(layoutParam1);
        relativeLayout0.addView(view1);
        view1.setId(R.id.head);
        layoutParam1.addRule(RelativeLayout.CENTER_HORIZONTAL,RelativeLayout.TRUE);

        ImageView imageView2 = new ImageView(ctx);
        RelativeLayout.LayoutParams layoutParam2 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,(int)(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,1,res.getDisplayMetrics())));
        imageView2.setLayoutParams(layoutParam2);
        relativeLayout0.addView(imageView2);
        imageView2.setId(R.id.ccc);
        layoutParam2.addRule(RelativeLayout.BELOW,R.id.head);

        return relativeLayout0;
  }
}

X2C 的想法很大胆,既保留了 XML 的优点,又解决了它带来的性能问题。但是它使用起来就不那么友好了。

// this.setContentView(R.layout.activity_main);
X2C.setContentView(this, R.layout.activity_main);

和原来的写法相比,X2C 明显具有较强的侵入性。

为了解决这个问题,你可以试试 Android X2J。

Android X2J 的特点

Android X2J 是一个 Gradle 插件,它可以让你 零成本 使用 X2C。 它甚至可以让你在完全不了解 X2C 的 API 和用法情况下,就可以享受到 X2C 的功能。

首先,Android X2J 会自动为你在 dependencies 中添加 X2C 的相关依赖,并生成相关的 @Xml 注解。

其次,当然也是最重要的一点,Android X2J 使用了 gradle transform api,在 APP 构建期间自动将 java 字节码做如下转换。

activity.setContentView(R.layout.activity_main)
=>> X2J.setContentView(activity, R.layout.activity_main)

layoutInflater.inflate(R.layout.view_item, parent)
=>> X2J.inflate(layoutInflater, R.layout.view_item, parent)

View.inflate(context, R.layout.view_item, parent)
=>> X2J.inflate(context, R.layout.view_item, parent)

X2J 类是对 X2C 类的封装。

下面是项目里 sample 模块里面的 MainActivity 的源码:

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        LayoutInflater.from(this).inflate(R.layout.fragmetn_layout, null);
        LayoutInflater.from(this).inflate(R.layout.fragmetn_layout, null, false);
    }
}

而下面则是使用了 X2J 插件打包后的 apk 反编译后的对应代码。

public class MainActivity extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        X2J.setContentView(this, R.layout.activity_main);
        X2J.inflate(LayoutInflater.from(this), (int) R.layout.fragmetn_layout, null);
        X2J.inflate(LayoutInflater.from(this), (int) R.layout.fragmetn_layout, null, false);
    }
}

最后,如果你已经使用了 Android X2J 插件然后又想弃坑,这很容易就能做到。 因为它只不过是一个 gradle 插件而已,没有任何侵入性。

!> 注意,因为 X2C 自身的原因,目前 Android X2J 并不支持 kotlin-kapt。

使用方法

  1. 配置根目录的 build/gradle。
buildscript {
    repositories {
        // ...
        maven { url "https://jitpack.io" }
    }
    dependencies {
        classpath 'com.github.7hens:android-x2j:1.0'
    }
}
  1. 在 app/build.gradle 中使用插件。
// 必须在 android {} 代码块之后添加插件。
// 如果你不想使用 X2J 了,把这行注释掉就行了。
apply plugin: 'android-x2j'
  1. 好了,接下来 Android X2J 会自动把 XML 布局翻译成 Java 代码(当然,这归功于 X2C),并打包到 APK 中。

This is amazing !! Github 项目地址

后记:理想是丰满的,现实是骨感的。我拿 Android X2J 到已有的项目中试了一下,发现编译不通过。 看了一下,原来是因为 X2C 的原因,它翻译出来的 Java 代码有问题。 目前 X2C 无法识别 XML 中的类似于 ?android:attr/windowBackground@android:color/white 的系统属性。