天天看点

【Android】组件化组件化

组件化

1 Gradle

1.1 打印信息

Gradle构建工具,Groovy脚本语言基本Java做了拓展,Gradle = JDK + Groovy

新建工程,在app的build.gradle中,添加:

println("hello gradle")
或
println "hello gradle"
           

同步时,在Build中可以看到打印出来的信息:

> Configure project :app
hello gradle

> Configure project :library
hello gradle

> Task :prepareKotlinBuildScriptModel UP-TO-DATE

BUILD SUCCESSFUL in 525ms
           

1.2 多module环境的管理

新建Android module:在项目上右键新建Android module,出现新的module叫做”library“,和app平级。

Gradle可以用来:

  • 所有module的版本号和依赖的统一管理
  • 生产环境和开发环境的分离

具体做法:

①在项目上右键,新建file,取名config.gradle:

// 添加多个自定义的属性,可以通过ext代码块
ext{
    username = "simon"
}
           

②在根目录下的build.gradle头部加入自定义config.gradle,相当于layout布局中加入include

③在app和library的build.gradle中加入:

println "${username}"
或者
println "${rootProject.ext.username}"
           

④点击同步即可打印:

> Configure project :app
hello gradle
simon

> Configure project :library
hello gradle
simon

> Task :prepareKotlinBuildScriptModel UP-TO-DATE

BUILD SUCCESSFUL in 435ms
           

groovy是弱类型语言,体现在可以自由修改数据类型,并且打印:

rootProject.ext.username = 163  
println "${rootProject.ext.username}"
           

真正的使用在项目中:

①扩展config.gradle的内容:

// 添加多个自定义的属性,可以通过ext代码块
ext{
    username = "simon"

    // 生产/开发环境
    isRelease = true

    // 建立Map存储,对象名,key都可以自定义,groovy糖果语法,灵活
    androidId = [
            compileSdkVersion : 30,
            buildToolsVersion : "30.0.3",
            minSdkVersion : 16,
            targetSdkVersion : 30,
            versionCode : 1,
            versionName : "1.0"
    ]

    appId = [
            applicationId : "com.example.componentizationstudy",
            library : "com.example.library",
    ]

    // 生产/开发环境 URL
    utl = [
            "debug" : "https://11.22.33.44/debug",
            "release" : "https://11.22.33.44/release"
    ]

    supportLibrary = "28.0.0"
    // 第三方库
    dependencies = [
            "appcompat":"androidx.appcompat:appcompat:1.1.0",
            "material":"com.google.android.material:material:1.1.0",
            					"constraintlayout":"androidx.constraintlayout:constraintlayout:1.1.3",
            "junit":"androidx.test.ext:junit:1.1.1",
            "espresso":"androidx.test.espresso:espresso-core:3.2.0"
    ]
}
           

②在app和library的build.gradle中,定义变量:

// 赋值与引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies
           

android部分:

android {
    compileSdkVersion androidId.compileSdkVersion
    buildToolsVersion androidId.buildToolsVersion

    defaultConfig {
        applicationId appId.applicationId
        minSdkVersion androidId.minSdkVersion
        targetSdkVersion androidId.targetSdkVersion
        versionCode androidId.versionCode
        versionName androidId.versionName

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
           

dependencies部分:

dependencies {
    // 依赖library库
    implementation project(":library")
    testImplementation 'junit:junit:4.+'
    implementation support.appcompat
    implementation support.material
    implementation support.constraintlayout
    androidTestImplementation support.junit
    androidTestImplementation support.espresso

    或者:
    implementation project(":library")
    testImplementation 'junit:junit:4.+'
    support.each {k, v->implementation v}  // 最简洁的方法,一句话搞定
}
           

依赖的两种写法:

// 标准写法
implementation group:"androidx.appcompat", name:"appcompat", version:"1.1.0"

// 简写
implementation 'androidx.appcompat:appcompat:1.1.0'
           

1.3 在BuildConfig.java文件中添加一个变量

BuildConfig.java是一个配置文件,会打包到apk中,文件在工程中的位置是:

module下的build-->generated-->source-->buildConfig-->debug-->com.example.componentizationstudy-->BuildConfig.java
           

在app的build.gradle中:

主要是这句话:
buildConfigField("String", "url", "\"${url.debug}\"");

加上之后是:
android{
	buildTypes {
        debug{
            buildConfigField("String", "url", "\"${url.debug}\"");
        }
        release {
            minifyEnabled false
            buildConfigField("String", "url", "\"${url.release}\"");
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
           

2 组件化项目详细部署

2.1 组件化的意义

1-面试

2-开发需求-不互相依赖,可以相互交互,任意组合,高度解耦

3-团队效率:分模块打包、测试,统一版本管理

2.2 Phone Module 和 Android Library的区别、切换

Phone Module:组件化
(1)是个模块,可以独立运行
(2)apply plugin:'com.android.application'
(3)有applicationId


Android Library: 集成化
(1)是个安卓库,不能独立运行
(2)apply plugin:'com.android.library'
(3)无applicationId
           

2.3 组件化和集成化:

组件化,多个模块作为Phone module可以独立运行,一个项目可以打包出多个apk

集成化,多个模块作为Android Library不可以独立运行,一个项目打包出一个apk

两者可以通过gradle切换

①扩展config.gradle的内容:

// 添加多个自定义的属性,可以通过ext代码块
ext{
    username = "simon"

    // 生产/开发环境
    // false:组件化模式,子模块可以独立运行
    // true:集成化模式,子模块不能独立运行
    isRelease = true

    // 建立Map存储,对象名,key都可以自定义,groovy糖果语法,灵活
    androidId = [
            compileSdkVersion : 30,
            buildToolsVersion : "30.0.3",
            minSdkVersion : 16,
            targetSdkVersion : 30,
            versionCode : 1,
            versionName : "1.0"
    ]

    appId = [
            app : "com.example.componentizationstudy",
            library : "com.example.library",
            order : "com.example.order",
            personal : "com.example.personal",
    ]

    // 生产/开发环境 URL
    url = [
            "debug" : "https://11.22.33.44/debug",
            "release" : "https://11.22.33.44/release"
    ]

    // 第三方库
    dependencies = [
            "appcompat":"androidx.appcompat:appcompat:1.1.0",
            "material":"com.google.android.material:material:1.1.0",
            "constraintlayout":"androidx.constraintlayout:constraintlayout:1.1.3",
            "junit":"androidx.test.ext:junit:1.1.1",
            "espresso":"androidx.test.espresso:espresso-core:3.2.0"
    ]
}
           

② order模块build.gradle

if(isRelease) {
    apply plugin:'com.android.library'
}else{
    apply plugin:'com.android.application'
}

// 赋值与引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies

android {
    compileSdkVersion androidId.compileSdkVersion
    buildToolsVersion androidId.buildToolsVersion

    defaultConfig {
        if(!isRelease){ // 组件化模式,可独立运行,有applicationId
            applicationId appId.order
        }
        minSdkVersion androidId.minSdkVersion
        targetSdkVersion androidId.targetSdkVersion
        versionCode androidId.versionCode
        versionName androidId.versionName

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        buildConfigField("boolean", "isRelease", String.valueOf(isRelease))
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    // 循环引入第三方库
    support.each{k, v->implementation v}

    // 公共基础库
    implementation project(":common")
}
           

③ personal模块的build.gradle

if(isRelease){
    apply plugin : 'com.android.library'
}else{
    apply plugin : 'com.android.application'
}

// 赋值与引用
def androidId = rootProject.ext.androidId
def appId = rootProject.ext.appId
def support = rootProject.ext.dependencies

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        if(!isRelease){ // 组件化模式,可独立运行,有applicationId
            applicationId appId.personal
        }
        minSdkVersion 16
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles "consumer-rules.pro"

        buildConfigField("boolean", "isRelease", String.valueOf(isRelease))
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    // 循环引入第三方库
    support.each{k, v->implementation v}

    // 公共基础库
    implementation project(":common")
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
}
           

④ 全局的build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

// 根目录下的build.gradle头部加入自定义config.gradle,相当于layout布局中加入include
apply from:"config.gradle"

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.2"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
           

⑤ 组件化与集成化的切换,通过Gradle动态切换

isRelease = true  点同步后,切成集成化模式

isRelease = false 点同步后,切成组件化模式
           

2.4 动态隔离,不把一些代码加载到apk中

① order和personal的build.gradle

android{
    sourceSets{
        main{
            if(!isRelease){
                // 组件化模式,需要单独运行时
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            }else{
                // 集成化模式,整个项目打包apk时
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java{
                    // release时,debug目录下的文件不需要合并到主工程
                    exclude '**/debug/**'
                }
            }
        }
    }
}
           

② 以order为例

在order模块的src的main上右键,新建debug文件夹,复制order的AndroidManifest.xml到这个文件夹下一份;

在java下的包上右键,新建package,取名debug,把测试代码都放在这个debug文件夹下。

③ 将config.gradle中的isRelease标签改成true,打包成apk,发现apk中没有debug中的代码

2.5 Phone module引用Android Module

在common中写的activity,可以在order中和personal中被继承。

3 Module和Module之间的交互

3.1 Module之间的交互方式

  • EventBus。难以实现:EventBean非常多(一对一),因为一对多就会混乱不堪、难以维护
  • 反射。可以实现,但维护成本高,且易出现高版本@hide限制
  • 隐式意图。维护成本一般,但较为麻烦,需要维护Manifest中的action
  • BroadCastReceiver。需要动态注册(7.0后),需求方发送广播
  • 类加载。需要准确的全类目路径,维护成本高,易出错

建议的实现方案:

3.2 方案1:类加载

从app的MainActivity.java跳转到order和personal两个模块:

public void jumpOrder(View view){
    Intent intent = new Intent(this, Order_MainActivity.class);
    intent.putExtra("name", "simon");
    startActivity(intent);
}

public void jumpPersonal(View view){
    Intent intent = new Intent(this, Personal_MainActivity.class);
    intent.putExtra("name", "simon");
    startActivity(intent);
}
           

从order的Order_MainActivity跳到app和personal两个模块:

public void jumpApp(View view){
    // 类加载方式交互
    try {
        Class targetClass = Class.forName("com.example.componentizationstudy.MainActivity");
        Intent intent = new Intent(this, targetClass);
        intent.putExtra("name", "simon");
        startActivity(intent);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

public void jumpPersonal(View view){
    // 类加载方式交互
    try {
        Class targetClass = Class.forName("com.example.personal.Personal_MainActivity");
        Intent intent = new Intent(this, targetClass);
        intent.putExtra("name", "simon");
        startActivity(intent);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
           

从personal的Personal_MainActivity跳到app和order两个模块:

public void jumpApp(View view){
    // 类加载方式交互
    try {
        Class targetClass = Class.forName("com.example.componentizationstudy.MainActivity");
        Intent intent = new Intent(this, targetClass);
        intent.putExtra("name", "simon");
        startActivity(intent);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

public void jumpOrder(View view){
    // 类加载方式交互
    try {
        Class targetClass = Class.forName("com.example.order.Order_MainActivity");
        Intent intent = new Intent(this, targetClass);
        intent.putExtra("name", "simon");
        startActivity(intent);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
           

3.3 方案2:全局Map记录信息

在common中,新建PathBean.java

package com.example.common;

public class PathBean {
    private String path;
    private Class clazz;

    public PathBean() {
    }

    public PathBean(String path, Class clazz) {
        this.path = path;
        this.clazz = clazz;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }
}
           

在common中,新建RecordPathManager.java

/**
 * 全局路径记录器(根据子模块分组)
 */
public class RecordPathManager {
    // key : "order"组
    // value:order子模块下,对应所有的Activity路径信息
    private static Map<String, List<PathBean>> groupMap = new HashMap<>();

    public static void joinGroup(String groupName, String pathName, Class<?> clazz){
        List<PathBean> list = groupMap.get(groupName);
        if (list == null) {
            list = new ArrayList<>();
            list.add(new PathBean(pathName, clazz));
            groupMap.put(groupName, list);
        } else{
            for (PathBean pathBean : list) {
                if(!pathName.equalsIgnoreCase(pathBean.getPath())){
                    list.add(new PathBean(pathName, clazz));
                    groupMap.put(groupName, list);
                }
            }
        }
    }

    /**
     * 根据组名和路径名获取类对象,达到跳转目的
     * @param groupName 组名
     * @param pathName 路径名
     * @return 跳转目标的class类对象
     */
    public static Class<?> getTargetClass(String groupName, String pathName){
        List<PathBean> list = groupMap.get(groupName);
        if (list == null) {
            return null;
        }
        for (PathBean pathBean : list) {
            if(pathName.equalsIgnoreCase(pathBean.getPath())){
                return pathBean.getClazz();
            }
        }
        return null;
    }
}
           

在app中新建AppApplication.java:

public class AppApplication extends BaseApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        RecordPathManager.joinGroup("app", "MainActivity", MainActivity.class);
        RecordPathManager.joinGroup("order", "Order_MainActivity", Order_MainActivity.class);
        RecordPathManager.joinGroup("personal", "Personal_MainActivity", Personal_MainActivity.class);
    }
}
           

并将其添加到AndroidMenifest.xml中:

android:name=".base.AppApplication"
           

在app中,修改跳转方式:

public void jumpApp(View view){
    // 类加载方式交互
    //        try {
    //            Class targetClass = Class.forName("com.example.componentizationstudy.MainActivity");
    //            Intent intent = new Intent(this, targetClass);
    //            intent.putExtra("name", "simon");
    //            startActivity(intent);
    //        } catch (ClassNotFoundException e) {
    //            e.printStackTrace();
    //        }

    // 全局Map加载
    Class<?> targetClass = RecordPathManager.getTargetClass("app", "MainActivity");
    if (targetClass == null) {
        Log.e("djtest", "获取targetClass为空");
    }
    Intent intent = new Intent(this, targetClass);
    intent.putExtra("name", "simon");
    startActivity(intent);
}

public void jumpOrder(View view){
    //        Intent intent = new Intent(this, Order_MainActivity.class);
    //        intent.putExtra("name", "simon");
    //        startActivity(intent);

    // 全局Map加载
    Class<?> targetClass = RecordPathManager.getTargetClass("order", "Order_MainActivity");
    if (targetClass == null) {
        Log.e("djtest", "获取targetClass为空");
    }
    Intent intent = new Intent(this, targetClass);
    intent.putExtra("name", "simon");
    startActivity(intent);
}

public void jumpPersonal(View view){
    //        Intent intent = new Intent(this, Personal_MainActivity.class);
    //        intent.putExtra("name", "simon");
    //        startActivity(intent);

    // 全局Map加载
    Class<?> targetClass = RecordPathManager.getTargetClass("personal", "Personal_MainActivity");
    if (targetClass == null) {
        Log.e("djtest", "获取targetClass为空");
    }
    Intent intent = new Intent(this, targetClass);
    intent.putExtra("name", "simon");
    startActivity(intent);
}
           

4 组件化APT

注解处理器,Annotation Processing Tool

通俗理解:根据规则帮我们生成代码,生成类文件

结构体语言:element组成的结构体

<html>
	<body>
		<div>...</div>
	<body>
<html>
           

java也是一种结构体语言:

package com.example.xxx; // PackageElement 包元素/节点

public class Main{ // TypeElement 类元素/节点

	private int x; // VariableElement 属性元素/节点
	
	private Main(){ // ExecuteableElement 方法元素/节点
	}
	
	private void print(String msg){
	
	}
}
           

需要掌握的API

getEnclosedElements()		返回该元素直接包含的子元素
getEnclosingElement()		返回包含该element的父element,与上一个方法相反
getKind()					返回element的类型,判断是哪种element
getModifiers()				获取修饰关键字,如 public static final
getSimpleName()				获取名字,不带包名
getQualifiedName()			获取全名,如果是类的话,包含完整的包名路径
getParameters()				获取方法的参数元素,每个元素是一个VariableElement
getReturnType()				获取方法元素的返回值
getConstantValue()			如果属性变量被final修饰,则可以使用该方法获取它的值
           

4.1 手写ARouter注解

① 新建工程,在MainActivity上加注解,此时不会有用,因为还没写注解

@ARouter(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
           

②新建Java Library,取名annotation,在其中新建ARouter,类型为Annotation

package com.example.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Activity使用的布局文件注解
 *
 * @Target(ElementType.TYPE) 接口、类、枚举、注解
 * @Target(ElementType.FIELD) 属性、枚举的常量
 * @Target(ElementType.METHOD) 方法
 * @Target(ElementType.PARAMETER) 方法参数
 * @Target(ElementType.CONSTRUCTOR) 构造函数
 * @Target(ElementType.LOCAL_VARIABLE) 局部变量
 * @Target(ElementType.ANNOTATION_TYPE) 该注解使用在另一个注解上
 * @Target(ElementType.PACKAGE) 包
 * @Retention(RetentionPolicy.RUNTIME) 该注解会在class字节码文件中存在,jvm加载时可以通过反射获取到该注解的内容
 *
 *
 * 生命周期 SOURCE < CLASS < RUNTIME
 * 1.一般如果需要在运行时去动态获取注解信息,用RUNTIME注解
 * 2.要在编译时进行一些预处理操作,如ButterKnife,用CLASS注解,注解会在class文件中存在,但是在运行时会被丢弃
 * 3.要做一些检查性的工作,如@Override,用SOURCE源码注解,注解仅存在源码级别,在编译的时候丢弃该注解
 *
 */
@Target(ElementType.TYPE)  // 作用在类上
@Retention(RetentionPolicy.CLASS)  // 没有执行run之前,在编译期的预编译操作
public @interface ARouter {
    // 详细路由路径(必填),如"/app/MainActivity"
    String path();
    // 从path中截取出来,规范开发者的编码
    String group() default "";
}
           

③ 在app的build.gradle中添加依赖

④ 在根目录下的build.gradle中添加阿里云镜像,加快下载速度:

repositories {
        // 超级实用:阿里云镜像更新
        maven{
            url "http://maven.aliyun.com/nexus/content/groups/public/"
        }
        google()
        jcenter()
    }
           

⑤ 新建Java Library,取名compiler,新建ARouterProcessor类用作ARouter注解的处理器,在注解处理器中读取注解内容,生成对应的类文件。

@AutoService(Processor.class)
@SupportedAnnotationTypes("com.example.annotation.ARouter") // 注解处理器支持的注解类型,需要把ARouter放进来,才能在process中处理, 必须是全类名
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 你需要用到什么jdk的版本来进行编译,生成这个class,必填!
@SupportedOptions("content")
public class ARouterProcessor extends AbstractProcessor {

    private Elements elementUtils; // 操作Element工具类
    private Types typesUtils; // type类信息工具类
    private Messager messager; // 用来输出警告、错误等日志
    private Filer filer; // 文件生成器
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        // 初始化工作
        super.init(processingEnvironment);
        elementUtils = processingEnvironment.getElementUtils();
        typesUtils = processingEnvironment.getTypeUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();

        String content = processingEnvironment.getOptions().get("content");
        // 有坑,不能像Android中Log.e的写法
        messager.printMessage(Diagnostic.Kind.NOTE, content);
    }

    // 通过注解替代
//    @Override
//    public Set<String> getSupportedAnnotationTypes() {
//        // 注解处理器支持的注解类型,需要把ARouter放进来,才能在process中处理
//        return super.getSupportedAnnotationTypes();
//    }
//
//    @Override
//    public SourceVersion getSupportedSourceVersion() {
//        // 你需要用到什么jdk的版本来进行编译,生成这个class,必填!
//        return super.getSupportedSourceVersion();
//    }
//
//    @Override
//    public Set<String> getSupportedOptions() {
//        // 接收外面传来的参数
//        return super.getSupportedOptions();
//    }

    /**
     * 相当于main函数,开始处理注解
     * 注解处理器的核心方法,处理具体的注解,生成java文件
     *
     * @param set 使用了支持处理注解的节点集合(类,上面写了注解)
     * @param roundEnvironment 当前或是之前的运行环境,可以通过该对象查找找到的注解
     * @return true 表示后续处理器不再处理(已经处理完成)
     */
    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if(set.isEmpty()){
            return false;
        }
        // 获取项目中所有使用了ARouter注解的节点
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
        // 遍历所有的类节点
        for (Element element : elements) {
            // 类节点之上,就是包节点
            String packageName = elementUtils.getPackageOf(element).getQualifiedName().toString();
            // 获取简单类名
            String className = element.getSimpleName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE, "备注接的类有:"+className);
            // 最终想要生成的类文件,如“MainActivity$$ARouter”
            String finalClassName = className + "$$ARouter";

            try {
                JavaFileObject sourceFile = filer.createSourceFile(packageName + "." + finalClassName); // 创建源文件
                Writer writer = sourceFile.openWriter();
                // 设置包名
                writer.write("package "+packageName+";\n");
                writer.write("public class "+finalClassName+" {\n");
                writer.write("public static Class<?> findTargetClass(String path) {\n");

                // 获取类之上@ARouter注解的path值
                ARouter aRouter = element.getAnnotation(ARouter.class);

                writer.write("if (path.equalsIgnoreCase(\""+aRouter.path()+"\")) {\n");
                writer.write("return "+className+".class;\n}\n");
                writer.write("return null;\n");
                writer.write("}\n}");

                // 非常重要
                writer.close();

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
}
           

compiler模块的build.gradle:

添加:
// 注册注解,并对其生成META-INF的配置信息
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'

// 引入annotation,让注解处理器,处理注解
implementation project(':annotation')

// java控制台输出中文乱码
tasks.withType(JavaCompiler){
    options.encoding = "UTF-8"
}



添加后全文如下:
import javax.tools.JavaCompiler

plugins {
    id 'java-library'
}

dependencies{
    implementation fileTree(dir:'libs', include:['*.jar'])

    // 注册注解,并对其生成META-INF的配置信息
    compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'

    // 引入annotation,让注解处理器,处理注解
    implementation project(':annotation')
}

// java控制台输出中文乱码
tasks.withType(JavaCompiler){
    options.encoding = "UTF-8"
}

// Jdk编译的版本是1.7
java {
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7
}
           

app模块的build.gradle:

// 第一:在Gradle文件中配置选项参数值(用于APT传参接收)
defaultConfig {
    // 切记:必须写在defaultConfig节点下
        javaCompileOptions{
            annotationProcessorOptions{
            arguments = [content : 'hello apt']
        }
    }
}


// 第二:添加注解处理器
annotationProcessor project(':compiler')
           

⑥ 在app下,新建OrderActivity和PersonalActivity,在两者头上加ARouter注解

@ARouter(path = "/app/OrderActivity")
public class OrderActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_order);
        Log.i("djtest", "OrderActivity");
    }
    public void jump(View view){
        Class<?> targetClass = PersonalActivity$$ARouter.findTargetClass("/app/PersonalActivity");
        startActivity(new Intent(this, targetClass));
    }
}
           
@ARouter(path = "/app/PersonalActivity")
public class PersonalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_personal);
        Log.i("djtest", "PersonalActivity");
    }

    public void jump(View view){
        Class<?> targetClass = MainActivity$$ARouter.findTargetClass("/app/MainActivity");
        startActivity(new Intent(this, targetClass));
    }
}
           

鼠标点击app,Build菜单中Make Module “ComponentAPT.app”,

在app/build/generated/ap_generated_sources/debug/out/com.example.componentapt/下生成:

(1)MainActivity$$ARouter.java

package com.example.componentapt;

public class MainActivity$$ARouter {
    public static Class<?> findTargetClass(String path) {
        if (path.equalsIgnoreCase("/app/MainActivity")) {
            return MainActivity.class;
        }
        return null;
    }
}
           

(2)OrderActivity$$ARouter.java

package com.example.componentapt;

public class OrderActivity$$ARouter {
    public static Class<?> findTargetClass(String path) {
        if (path.equalsIgnoreCase("/app/OrderActivity")) {
            return OrderActivity.class;
        }
        return null;
    }
}
           

(3)PersonalActivity$$ARouter.java

package com.example.componentapt;

public class PersonalActivity$$ARouter {
    public static Class<?> findTargetClass(String path) {
        if (path.equalsIgnoreCase("/app/PersonalActivity")) {
            return PersonalActivity.class;
        }
        return null;
    }
}
           

同理,在app/build/intermediates/javac/debug/classes/com/example/componentapt/下生成:

(1)MainActivity$$ARouter.class

package com.example.componentapt;

public class MainActivity$$ARouter {
    public MainActivity$$ARouter() {
    }

    public static Class<?> findTargetClass(String path) {
        return path.equalsIgnoreCase("/app/MainActivity") ? MainActivity.class : null;
    }
}
           

(2)OrderActivity$$ARouter.class

package com.example.componentapt;

public class OrderActivity$$ARouter {
    public OrderActivity$$ARouter() {
    }

    public static Class<?> findTargetClass(String path) {
        return path.equalsIgnoreCase("/app/OrderActivity") ? OrderActivity.class : null;
    }
}
           

(3)PersonalActivity$$ARouter.class

package com.example.componentapt;

public class PersonalActivity$$ARouter {
    public PersonalActivity$$ARouter() {
    }

    public static Class<?> findTargetClass(String path) {
        return path.equalsIgnoreCase("/app/PersonalActivity") ? PersonalActivity.class : null;
    }
}
           

4.2 组件化APT高级用法JavaPoet

APT + JavaPoet = 超级利刃

JavaPoet的8个常用类

MethodSpec		代表一个构造函数或者方法声明
TypeSpec		代表一个类,接口,或者常量
FieldSpec		代表一个成员变量,一个字段声明
JavaFile		包含一个顶级类的Java文件
ParameterSpec	用来创建参数
AnnotationSpec	用来创建注解
ClassName		用来包装一个类
TypeName		类型,如在添加返回值类型是使用TypeName.VOID
           

JavaPoet字符串格式化规则:

$L	字面量,	"int value=$L",10
$S	字符串,	$S," hello"
$T	类、接口,   $T,MainActivity
$N	变量,		user.$N,name
           

① 新建工程,新建两个java Library,分别为annotation和compiler。

② 在annotation中,新建ARouter注解文件:

package com.example.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) // 作用于类
@Retention(RetentionPolicy.CLASS) // 编译时进行一些预处理工作,存在于class文件中,运行时删除
public @interface ARouter {
    String path();
}
           

③ 在compiler中,新建注ARouterProcessor解处理器文件:

package com.example.compiler;

import com.example.annotation.ARouter;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

@AutoService(Processor.class) // 注册注解处理器
@SupportedAnnotationTypes("com.example.annotation.ARouter") // 对哪种注解进行处理
@SupportedSourceVersion(SourceVersion.RELEASE_7) // 编译器类型
@SupportedOptions("content") // 额外的参数
public class ARouterProcessor extends AbstractProcessor {

    private Elements eLementsUtils;
    private Types typesUtils;
    private Messager messager;
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        eLementsUtils = processingEnvironment.getElementUtils();
        typesUtils = processingEnvironment.getTypeUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();

        String content = processingEnvironment.getOptions().get("content");
        messager.printMessage(Diagnostic.Kind.NOTE, content); // 打印获得的参数
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        // 如果没有任何一处使用了@ARouter注解,那就不做任何处理
        if (set.isEmpty()) {
            return false;
        }

        // 获取所有被@ARouter注解的类节点
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);
        // 遍历这些节点,每个节点生成一个类文件
        for (Element element : elements) {
            // 类节点的上一个节点:包节点
            String packageName = eLementsUtils.getPackageOf(element).getQualifiedName().toString();
            // 获取简单类名
            String className = element.getSimpleName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE, "被@ARouter注解的类有:"+className);
            // 最终生成的类文件名
            String finalClassName = className + "$$ARouter";

            ARouter aRouter = element.getAnnotation(ARouter.class);

            // 方法:public static Class<?> findTargetClass(String path){}
            MethodSpec methodSpec = MethodSpec.methodBuilder("findTargetClass") // 方法名
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(Class.class) // 返回值类型是Class
                    .addParameter(String.class, "path") // 参数类型和参数名
                    // return path.equalsIgnoreCase("app/MainActivity") ? MainActivity.class : null;
                    .addStatement("return path.equalsIgnoreCase($S) ? $T.class : null",
                            aRouter.path(),
                            ClassName.get((TypeElement) element))
                    .build();

            // 类:public class XActivity$$ARouter {}
            TypeSpec typeSpec = TypeSpec.classBuilder(finalClassName) // 类名
                    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                    .addMethod(methodSpec) // 将方法添加到类中
                    .build();

            // 包:package com.example.componetaptjavapoet;
            JavaFile javaFile = JavaFile.builder(packageName, typeSpec) // 包名,类名
                    .build();

            // 写到文件中去
            try {
                javaFile.writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
}
           

写注解生成类文件之前先将想要的类文件写出来:

在app下新建XActivity$$ARouter.java,之后就仿照这个类文件,在注解处理器中用代码生成:

package com.example.componetaptjavapoet;

public class XActivity$$ARouter {
    public static Class<?> findTargetClass(String path){
        return path.equalsIgnoreCase("app/MainActivity") ? MainActivity.class : null;
    }
}
           

compiler的build.gradle:

plugins {
    id 'java-library'
}

dependencies{
    implementation fileTree(dir:'libs', include:['*.jar'])

    // 注册注解,并对其生成META-INF的配置信息
    compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'

    // 帮助我们通过类调用的形式来生成JAVA代码
    implementation 'com.squareup:javapoet:1.9.0'

    // 依赖注解
    implementation project(':annotation')
}
java {
    sourceCompatibility = JavaVersion.VERSION_1_7
    targetCompatibility = JavaVersion.VERSION_1_7
}
           

检查注解是否注册成功:

在compiler/build/classes/java/main/META-INF/services/下,打开文件,看到下列内容,说明注册成功。

com.example.compiler.ARouterProcessor
           

④ app中应用@ARouter注解

app的build.gradle中添加依赖

implementation project(':annotation')
annotationProcessor project(':compiler')
           

app中的MainActivity、OrderActivity和PersonalActivity:

MainActivity.java:

@ARouter(path="/app/MainActivity")
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("djtest", "MainActivity...");
    }

    public void jump(View view){
        Class targetClass = OrderActivity$$ARouter.findTargetClass("/app/OrderActivity");
        startActivity(new Intent(this, targetClass));
    }
}
           

OrderActivity.java:

@ARouter(path="/app/OrderActivity")
public class OrderActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_order);
        Log.i("djtest", "OrderActivity...");
    }

    public void jump(View view){
        Class targetClass = PersonalActivity$$ARouter.findTargetClass("/app/PersonalActivity");
        startActivity(new Intent(this, targetClass));
    }
}
           

PersonalActivity.java:

@ARouter(path="/app/PersonalActivity")
public class PersonalActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_personal);
        Log.i("djtest", "PersonalActivity...");
    }

    public void jump(View view){
        Class targetClass = MainActivity$$ARouter.findTargetClass("/app/MainActivity");
        startActivity(new Intent(this, targetClass));
    }
}
           

5 组件化路由架构设计

组件化项目部署:

  • 配置arouter_api/build.gradle
  • 配置common/build.gradle
  • 完成模拟APT生成的类代码
  • app子模块中,模拟跳转代码
【Android】组件化组件化
  1. 为什么需要组名?

    只加载相关类,节省内存

  2. 生成这些文件有什么用?

    根据path,获取类对象

    根据组名,找到对应的list,根据从组名截取的path,获取类对象。