Android组件化和模块化

在这里插入图片描述

组件化和模块化

在这里插入图片描述

组件化是把重复代码进行封装,例如util类的封装

模块化是指把一组功能进行隔离,可以独立运行,独立管理,例如login(登录)和home(首页)

组件化模块化优点开发调试效率高、可维护性强、避免阻断、版本管理更容易

类别目的特点接口成果架构定位
组件重用、解耦高重用、松耦合无统一接口基础库、基础组件纵向分层
模块隔离/封装高内聚、松耦合统一接口业务框架、业务模块横向分块

组件化说白了就是为了解决代码重复问题,把相同代码提取出来,进行封装统一管理

模块化就是封装细节,暴露接口,同层模块不相互依赖,可以随意的拆卸和组装,有利于后期的维护升级

组件化,模块化实战

假说说有一个项目,StudyForum(学习论坛),使用模块化开发的话,在敲代码之前要先构思好项目的功能,并根据功能划分模块,假设说现在需要有登录和首页功能,则划分出loginhome模块,一定要理解的是,模块化的思想是同层不可依赖,只能是上层依赖下层,对于loginhome来说都属于业务模块,属于同层不可依赖的关系。

app模块的意义

app是壳工程,是程序的入口模块,协调下层的业务模块,它不包括任何的业务代码,他是依赖树最上层的节点,依赖于业务模块运行,也就是loginhome模块,全部的业务模块则应该依赖于公共基础模块,比如网络请求,json解析,此类功能应该是被所有的业务模块共同依赖的此时项目的依赖图是

安卓项目是gradle构建的,每个模块下都有gradle文件,包含模块的依赖和配置信息,此时模块增多,许多相同的代码不利于维护,比如依赖库的版本,安卓SDK的编译版本,targetVersion之类的,假设后期想升级编译版本,需要把每个模块的gradle文件都进行修改,效率比较低,此处笔者对这些版本进行组件化,把相同的代码提取出来进行统一管理。

抽离gradle文件中属性的版本

先在项目根目录创建全局配置文件app_config.gradle,然后在项目的build.gradle引用

app_config.gradle

apply from : "app_config.gradle"

先把app模块的下面部分进行抽离

compileSdk 31

defaultConfig {
    applicationId "com.hbsf.app"
    minSdk 21
    targetSdk 31
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

回到app_config.gradle,创建extext是用来拓展gradle属性的

ext {
    app_android = [
            compileSdk : 31,
            applicationId : "com.hbsf.studyforum",
            minSdk : 21,
            targetSdk: 31,
            versionCode : 1,
            versionName : "1.0",
            testInstrumentationRunner : "androidx.test.runner.AndroidJUnitRunner"
    ]
}

接下来修改app模块中的gradle文件的代码:

//自动读取ext下面的拓展属性
compileSdk app_android.compileSdk

defaultConfig {
    applicationId app_android.applicationId
    minSdk app_android.minSdk
    targetSdk app_android.targetSdk
    versionCode app_android.versionCode
    versionName app_android.versionName

    testInstrumentationRunner app_android.testInstrumentationRunner
}

与上述相同的,对loginhome模块也及进行修改

对于依赖的版本,也进行同样的操作,app_config.gradleext中添加

dependencies_version = [
        appcompat : "1.2.0",
        material : "1.3.0",
        constraintlayout : "2.0.4",
        junit : "4.+",
        ext_junit : "1.1.2",
        espresso_core : "3.3.0",
]

loginhome模块的applicationId不同,需要在app_config.gradle中定义

app_appid = [
        "login" : "com.hbsf.login",
        "home" : "com.hbsf.home"
]

修改其他android模块

implementation "androidx.appcompat:appcompat:${dependencies_version.appcompat}"
implementation "com.google.android.material:material:${dependencies_version.material}"
implementation "androidx.constraintlayout:constraintlayout:${dependencies_version.constraintlayout}"
testImplementation "junit:junit:${dependencies_version.junit}"
androidTestImplementation "androidx.test.ext:junit:${dependencies_version.ext_junit}"
androidTestImplementation "androidx.test.espresso:espresso-core:${dependencies_version.espresso_core}"

上述代码修改完毕后则可以做到修改app_config.gradle中的值,其他模块的依赖版本也会随着改变。

debug,release下的gradle配置

安卓程序分为debugrelease两种版本,debug是程序员运行的环境,release是发布版本是用户的运行环境。

为了后期有利于测试和维护,定义的规则如下:

  • debug版本,所有的模块都是可以独立运行的,可以独立运行则可以独立测试。
  • release版本下,所有模块依附于app模块运行,不能单独运行。

此种规则下,不同版本的模块依赖关系不同,上述的依赖图只适用于release,接下来对不同版本间的app,login,home,common的依赖关系进行分析。

debug模式

app,login,home相互独立,三者没有任何依赖,都依赖于common

release模式

app,login,home三者都依赖于commonapp依赖login,home,但login,home相互不依赖

代码变动

若想实现上面的规则,需要在app_config.gradle中去创建一个变量:

isRelease = true;

app模块的gradle依赖中要加上下述代码:

//isRelease为true则依赖login和home
if (isRelease) {
    implementation project(":login")
    implementation project(":home")
}

loginhomegradle中加入下述代码:

if (isRelease) { // 如果是发布版本时,各个模块都不能独立运行
    apply plugin: 'com.android.library'
} else {
    apply plugin: 'com.android.application'
}

library类型和的模块不应该有applicationId

还要加上

if (!isRelease) {
    applicationId app_appid.login
}

login,home模块都加入common依赖

implementation project(":common")

此时版本和依赖处理完毕,但是引出新的问题。

登录成功需要跳转首页,首页没有被登录模块所依赖,引用不到,该如何处理?

模块间通信

上述的跳转属于模块间通信的一种功能,本小节对模块间通信进行解析。

需要理解的是模块间通信是对于release版本来说的,debug版本不需要跳转页面。

市面上有几种方式去实现模块通信:

1. 隐式意图

本篇文章不对隐式意图进行解释。

为什么不同模块,不同的AndroidManifest.xml可以实现呢?

因为不同模块的AndroidManifest.xml最终会合并为一个文件。

2.类加载去实现跳转

try {
    Class clazz = Class.forName("com.hbsf.home.Activity");
    Intent intent = new Intent(this, clazz);
    startActivity(intent);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

有一个疑问 为何homelogin不是一个模块的,类加载却能加载到呢?

因为release版本下app模块依赖了所有的业务模块,因此在打包Dex的时候会打包到一起,当寻找类时,则可以在Dex中找到。

3.广播

广播,最终还是通过类加载去启动的Activity

思路如下:广播接收器,拿到传递的字符串,通过类加载找到Activity的Class,最终再使用intent完成跳转,还不如直接使用类加载快。

缺点如下:类加载和广播的问题都是需要自己拼接包名,不好维护

4.EventBus实现通信

EventBus不支持跳转,支持发送数据,因为没有注册时机

要想实现跳转,需要自己继续封装,也就是进行注册,此种方案类似于全局map(如下)

5.全局map

思路如下:在common中创建一个跳转类,里面维护一个静态mapkey是定义的找到Activity的Class规则,value是包括Activity的Classbean,在app模块的application中对所有需要跳转的Activity进行注册,当别的模块进行跳转时,直接去调用common模块的跳转管理类即可。

缺点如下:问题就是需要写很多注册代码,如果能自动生成注册代码就很完美了。

6.ARouter

阿里的arouter方案解决了上面的问题

其通过APT技术在编译期扫描相应注解,并结合javapoet动态生成代码的方式,来动态的对Activity进行注册

现在仿照ARouter去实现一个ARouter,全部用最原生的代码,兼容性要比阿里的好,相对的功能会少一些,只保留了其最核心的功能

首先了解APT

APT是在执行javac命令时,javac帮助程序员扫描注解,并为程序员提供操作豁口的一项技术,经常用于在编译期生成一些代码

在编写代码之前先了解一下ARouter的使用,看github上的官方例子:

@Route(path = "/test/activity")
public class YourActivity extend Activity {

}

若想和在其他地方跳转YourActivity,调用下面的方法即可。

ARouter.getInstance().build("/test/activity").navigation();

ARouter模块和依赖

新建一个android模块arouterarouter下再创建三个子模块

arouter_annotaton

存放注解类,比如上面的@Route,会传入path和group

arouter_api

存放提供给外部的接口,和一些管理类,比如后续要生成类文件要继承自ARouterGroup,ARouterPath

arouter_compiler

存放APT注解解析器

大概实现思路

当模块注册注解解析器后,此模块在编译时会自动扫描注解,被跳转的Activity必须添加相应的注解,注解有两个参数,path和groupgroup规定不传的情况下,group就是path的前一个节点。

例如@Route(path = "/test/activity"),group就是test

当注解处理器扫描到此注解时,笔者规定处理方式是生成group类和path类(之所以分group和path是为了分层,提高后续的查找效率),两个类中都包含有一个map,注解处理器会解析注解的参数并添加进map,比如上面的例子:

最后会在build文件夹中生成下面的两个类文件

新生成的两个类是通过arouter_api模块中的接口(ARouterGroup,ARouterPath)进行约束规范的

//继承自ARouterPath,是暴露给外部的接口
public class ARouter$$Path$$app implements ARouterPath {
  @Override
  public Map<String, RouterBean> getPathMap() {
    Map<String, RouterBean> pathMap = new HashMap<>();
    //key为path,value为包含Activity.class的bean
    pathMap.put("/app/MainActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, MainActivity.class, "/app/MainActivity", "app"));
    return pathMap;
  }
}
//继承自ARouterGroup,是暴露给外部的接口
public class ARouter$$Group$$app implements ARouterGroup {
  @Override
  public Map<String, Class<? extends ARouterPath>> getGroupMap() {
    //key为group,value为path类
    Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
    groupMap.put("app", ARouter$$Path$$app.class);
    return groupMap;
  }
}

使用StudyForum项目中的业务举个例子:

其中有两个业务模块,login和home

login中有一个LoginActivity

@ARouter(path = "/login/LoginActivity")
public class LoginActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
    }
}

home有一个HomeActivity

@ARouter(path = "/home/HomeActivity")
public class HomeActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);
    }
}

如此使用注解,则会在login模块的build文件夹中生成两个类

public class ARouter$$Group$$login implements ARouterGroup {
  @Override
  public Map<String, Class<? extends ARouterPath>> getGroupMap() {
    Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
    groupMap.put("login", ARouter$$Path$$login.class);
    return groupMap;
  }
}

public class ARouter$$Path$$login implements ARouterPath {
  @Override
  public Map<String, RouterBean> getPathMap() {
    Map<String, RouterBean> pathMap = new HashMap<>();
    pathMap.put("/login/LoginActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, LoginActivity.class, "/login/LoginActivity", "login"));
    return pathMap;
  }
}

相同的在home模块的build文件夹中生成两个类

public class ARouter$$Group$$home implements ARouterGroup {
  @Override
  public Map<String, Class<? extends ARouterPath>> getGroupMap() {
    Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
    groupMap.put("home", ARouter$$Path$$home.class);
    return groupMap;
  }
}

public class ARouter$$Path$$home implements ARouterPath {
  @Override
  public Map<String, RouterBean> getPathMap() {
    Map<String, RouterBean> pathMap = new HashMap<>();
    pathMap.put("/home/HomeActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, HomeActivity.class, "/home/HomeActivity", "home"));
    return pathMap;
  }
}

若此时想跳转,只需要找到相应的ARouter$$Group,通过group拿到ARouter$$Path,再通过path找到bean就可以拿到Activity完成跳转了

思考为什么可以通过下面代码完成跳转?

ARouter.getInstance().build("/login/LoginActivity").navigation();

上面代码封装好了寻找类的操作,就是说传入/login/LoginActivity这个字符串,就可以自动找到ARouter$$Group$$login,然后再通过/login/LoginActivity找到bean

代码实现ARouter

大致原理懂了,下边就开始编写代码

完善各个模块的gradle

首先要知道的是,APT并不知道去扫描那个模块,所以要在gradle中进行指明

比如app,login,home需要APT生成代码,那么在他们的gradle中就导入下面依赖:

annotationProcessor project(':arouter:arouter_compiler')

因为上述模块又需要使用@ARouter注解,因此导入下面依赖:

implementation project(":arouter:arouter_annotation")

这三个模块都会去生成继承自ARouterPath和ARouterGroup的类,所以也要依赖arouter_api模块,为了省事直接在common模块加入依赖

//api是可传递的依赖,不同于implementation不可传递,上述的几个模块的依赖也可以使用api,但是在commom中使用api是不好的
api project(":arouter:arouter_api")

arouter_compiler模块也需要导入一些依赖

//自动构建,不用自己去构建apt运行环境
compileOnly'com.google.auto.service:auto-service:1.0-rc4'
annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'

// javapoet,面向对象的生成Java代码,若不使用此框架,则需要使用字符串拼接的方式生成代码,不符合面向对象的思想
implementation "com.squareup:javapoet:1.9.0"

// 引入annotation,处理@ARouter注解
implementation project(':arouter:arouter_annotation')

依赖导入完毕,APT生成的类名和文件路径,好像和模块名有关系,此时则需要通过gradleapt处理器中传值,在gradledefaultConfig中添加,下面以login模块举例,以此类推

javaCompileOptions {
    annotationProcessorOptions {
        // project.getName() == login, packageNameForAPT是生成的类的包路径
        arguments = [moduleName: project.getName(), packageNameForAPT: packageNameForAPT.login]
    }
}

包路径在app_config.gradleext进行属性拓展

packageNameForAPT = [
        app : "com.hbsf.app",
        login : "com.hbsf.login",
        home : "com.hbsf.home"
];

最终的生成类的目录结构和类名如下:

编写ARouterProcessor

工具类和常量类代码没有贴出

//AutoService则是固定的写法,加个注解即可
//通过auto-service中的@AutoService可以自动生成AutoService注解处理器,用来注册
//用来生成 META-INF/services/javax.annotation.processing.Processor 文件
@AutoService(Processor.class)

//允许/支持的注解类型,让注解处理器处理
@SupportedAnnotationTypes({ProcessorConfig.AROUTER_PACKAGE})

//指定JDK编译版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)

//注解处理器接收的参数
@SupportedOptions({ProcessorConfig.OPTIONS, ProcessorConfig.APT_PACKAGE})

public class ARouterProcessor extends AbstractProcessor {

    //操作Element的工具类(类,函数,属性,其实都是Element)
    private Elements elementTool;

    //type(类信息)的工具类,包含用于操作TypeMirror的工具方法
    private Types typeTool;

    //Message用来打印 日志相关信息
    private Messager messager;

    //文件生成器, 类 资源 等,就是最终要生成的文件 是需要Filer来完成的
    private Filer filer;

    private String options; //各个模块传递过来的模块名 例如:app order personal
    private String aptPackage; //各个模块传递过来的目录 用于统一存放 apt生成的文件

    //生成path文件的中间体,一个组可能有好多路径,所以是list
    private Map<String, List<RouterBean>> mAllPathMap = new HashMap<>(); // 目前是一个

    //生成group文件的中间体,其实后期生成文件只需要String,类名就可以了
    private Map<String, String> mAllGroupMap = new HashMap<>();

    private int i = 0;

    //做初始化工作,就相当于Activity中的onCreate函数一样的作用
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);

        elementTool = processingEnvironment.getElementUtils();
        messager = processingEnvironment.getMessager();
        filer = processingEnvironment.getFiler();
        typeTool = processingEnvironment.getTypeUtils();

        //只有接受到App壳传递过来的值,才能证明APT环境搭建完成
        options = processingEnvironment.getOptions().get(ProcessorConfig.OPTIONS);
        aptPackage = processingEnvironment.getOptions().get(ProcessorConfig.APT_PACKAGE);
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>> options:" + options);
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>>>>>>>>>>>>>>>>>>> aptPackage:" + aptPackage);
        if (options != null && aptPackage != null) {
            messager.printMessage(Diagnostic.Kind.NOTE, "APT Successful");
        } else {
            messager.printMessage(Diagnostic.Kind.NOTE, "APT problem");
        }
    }


    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if (set.isEmpty()) {
            messager.printMessage(Diagnostic.Kind.NOTE, "No place to user ARouter");
            return false;
        }

        //全部被注解的集合,通过这个可以拿到相应的Activity
        Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(ARouter.class);

        //获取Activity,后期进行注解注解地方的判断是否符合要求
        TypeElement activityType = elementTool.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);
        //拿到Activity类信息
        TypeMirror activityMirror = activityType.asType();

        //遍历所有的被ARouter注解的节点
        for (Element element : elements) {

            //获取类名,比如MainActivity
            String className = element.getSimpleName().toString();
            messager.printMessage(Diagnostic.Kind.NOTE, "@ARetuer  : " + className);

            //拿到注解
            ARouter aRouter = element.getAnnotation(ARouter.class);

            //创建路由对象
            RouterBean routerBean = new RouterBean.Builder()
                    .addGroup(aRouter.group())
                    .addPath(aRouter.path())
                    .addElement(element)
                    .build();
            //ARouter注解的类 必须继承 Activity
            TypeMirror elementMirror = element.asType(); //Main2Activity的具体详情 例如:继承了 Activity
            if (typeTool.isSubtype(elementMirror, activityMirror)) {
                routerBean.setTypeEnum(RouterBean.TypeEnum.ACTIVITY);
            }  else {
                throw new RuntimeException("@ARouter must be in ctivity above");
            }

            if (checkRouterPath(routerBean)) {
                messager.printMessage(Diagnostic.Kind.NOTE, "RouterBean Check Success:" + routerBean.toString());

                //赋值mAllPathMap集合里面去
                List<RouterBean> routerBeans = mAllPathMap.get(routerBean.getGroup());

                //如果从Map中找不到key为:bean.getGroup()的数据,就新建List集合再添加进Map
                if (ProcessorUtils.isEmpty(routerBeans)) {
                    routerBeans = new ArrayList<>();
                    routerBeans.add(routerBean);
                    mAllPathMap.put(routerBean.getGroup(), routerBeans);
                } else {
                    routerBeans.add(routerBean);
                }

            } else {
                messager.printMessage(Diagnostic.Kind.ERROR, "path error");
            }
        }

        //两个接口,生成的类要继承这两个接口
        TypeElement pathType = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_PATH); // ARouterPath描述
        TypeElement groupType = elementTool.getTypeElement(ProcessorConfig.AROUTER_API_GROUP); // ARouterGroup描述

        //TODO 第一大步:生成Path类
        try {
            createPathFile(pathType);
        } catch (IOException e) {
            e.printStackTrace();
            messager.printMessage(Diagnostic.Kind.NOTE, "creat path error e:" + e.getMessage());
        }

        //TODO 第二大步:生成Group类
        try {
            createGroupFile(groupType, pathType);
        } catch (IOException e) {
            e.printStackTrace();
            messager.printMessage(Diagnostic.Kind.NOTE, "creat group error e:" + e.getMessage());
        }

        return true;
    }

    /**
     * 生成路由组Group文件,如:ARouter$$Group$$app
     * @param groupType ARouterLoadGroup接口信息
     * @param pathType ARouterLoadPath接口信息
     */
    private void createGroupFile(TypeElement groupType, TypeElement pathType) throws IOException {
        if (ProcessorUtils.isEmpty(mAllGroupMap) || ProcessorUtils.isEmpty(mAllPathMap)) return;
        
        //返回值 这一段 Map<String, Class<? extends ARouterPath>>
        TypeName methodReturns = ParameterizedTypeName.get(
                ClassName.get(Map.class),        // Map
                ClassName.get(String.class),    // Map<String,

                //Class<? extends ARouterPath>> 难度
                ParameterizedTypeName.get(ClassName.get(Class.class),
                        //? extends ARouterPath
                        WildcardTypeName.subtypeOf(ClassName.get(pathType))) // ? extends ARouterLoadPath
                        //WildcardTypeName.supertypeOf() 做实验 ? super

                //最终的:Map<String, Class<? extends ARouterPath>>
        );

        //1.方法 public Map<String, Class<? extends ARouterPath>> getGroupMap() {
        MethodSpec.Builder methodBuidler = MethodSpec.methodBuilder(ProcessorConfig.GROUP_METHOD_NAME) // 方法名
                .addAnnotation(Override.class) // 重写注解 @Override
                .addModifiers(Modifier.PUBLIC) // public修饰符
                .returns(methodReturns); // 方法返回值

        //Map<String, Class<? extends ARouterPath>> groupMap = new HashMap<>();
        methodBuidler.addStatement("$T<$T, $T> $N = new $T<>()",
                ClassName.get(Map.class),
                ClassName.get(String.class),

                //Class<? extends ARouterPath> 难度
                ParameterizedTypeName.get(ClassName.get(Class.class),
                        WildcardTypeName.subtypeOf(ClassName.get(pathType))), // ? extends ARouterPath
                        ProcessorConfig.GROUP_VAR1,
                        ClassName.get(HashMap.class));

        for (Map.Entry<String, String> entry : mAllGroupMap.entrySet()) {
            methodBuidler.addStatement("$N.put($S, $T.class)",
                    ProcessorConfig.GROUP_VAR1, //groupMap.put
                    entry.getKey(), //login,app
                    ClassName.get(aptPackage + ProcessorConfig.PACKAGE_PATH_NAME, entry.getValue()));
        }

        //return groupMap;
        methodBuidler.addStatement("return $N", ProcessorConfig.GROUP_VAR1);

        //最终生成的类文件名 ARouter$$Group$$ + personal
        String finalClassName = ProcessorConfig.GROUP_FILE_NAME + options;

        messager.printMessage(Diagnostic.Kind.NOTE, "APT creat group path :" +
                aptPackage + "." + finalClassName);

        // 生成类文件:ARouter$$Group$$app
        JavaFile.builder(aptPackage + ProcessorConfig.PACKAGE_GEGROUP_NAME, // 包名
                TypeSpec.classBuilder(finalClassName) // 类名
                .addSuperinterface(ClassName.get(groupType)) // 实现ARouterLoadGroup接口 implements ARouterGroup
                .addModifiers(Modifier.PUBLIC) // public修饰符
                .addMethod(methodBuidler.build()) // 方法的构建(方法参数 + 方法体)
                .build()) //类构建完成
                .build() //JavaFile构建完成
                .writeTo(filer); //文件生成器开始生成类文件
    }

    /**
     * 系列Path的类  生成工作
     * @param pathType ARouterPath 高层的标准
     * @throws IOException
     */
    private void createPathFile(TypeElement pathType) throws IOException {
        //判断 map仓库中,是否有需要生成的文件
        if (ProcessorUtils.isEmpty(mAllPathMap)) {
            return;
        }

        //返回值
        TypeName methodReturn = ParameterizedTypeName.get(
                  ClassName.get(Map.class),         // Map
                  ClassName.get(String.class),      // Map<String,
                  ClassName.get(RouterBean.class)   // Map<String, RouterBean>
        );

        //遍历中间map,依次创建类
        for (Map.Entry<String, List<RouterBean>> entry : mAllPathMap.entrySet()) {
            //方法
            MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder(ProcessorConfig.PATH_METHOD_NAME)
                    .addAnnotation(Override.class) //给方法上添加注解  @Override
                    .addModifiers(Modifier.PUBLIC) //public修饰符
                    .returns(methodReturn); //把Map<String, RouterBean> 加入方法返回
                    
            //Map<String, RouterBean> pathMap = new HashMap<>(); // $N == 变量 为什么是这个,因为变量有引用 所以是$N
            methodBuilder.addStatement("$T<$T, $T> $N = new $T<>()",
                    ClassName.get(Map.class),           //Map
                    ClassName.get(String.class),        //Map<String,
                    ClassName.get(RouterBean.class),    //Map<String, RouterBean>
                    ProcessorConfig.PATH_VAR1,          //Map<String, RouterBean> pathMap
                    ClassName.get(HashMap.class)        //Map<String, RouterBean> pathMap = new HashMap<>();
                    );

            // 同组下会有好几个类
            List<RouterBean> pathList = entry.getValue();
            /**
                $N == 变量 变量有引用 所以 N
                $L == TypeEnum.ACTIVITY
             */
            for (RouterBean bean : pathList) {
                methodBuilder.addStatement("$N.put($S, $T.create($T.$L, $T.class, $S, $S))",
                        ProcessorConfig.PATH_VAR1, //pathMap.put
                        bean.getPath(), //"/login/loginActivity"
                        ClassName.get(RouterBean.class), //RouterBean
                        ClassName.get(RouterBean.TypeEnum.class), //RouterBean.Type
                        bean.getTypeEnum(), //枚举类型:ACTIVITY
                        ClassName.get((TypeElement) bean.getElement()), // LoginActivity.class
                        bean.getPath(), //路径名
                        bean.getGroup() //组名
                        );
            } //TODO end for

            //return pathMap;
            methodBuilder.addStatement("return $N", ProcessorConfig.PATH_VAR1);

            //TODO 注意:不能像以前一样,1.方法,2.类  3.包, 因为这里面有implements ,所以 方法和类要合为一体生成才行,这是特殊情况

            //最终生成的类文件名  ARouter$$Path$$login
            String finalClassName = ProcessorConfig.PATH_FILE_NAME + entry.getKey();

            messager.printMessage(Diagnostic.Kind.NOTE, "APT creat path :" +
                    aptPackage + "." + finalClassName);

            //生成类文件:ARouter$$Path$$Login
            JavaFile.builder(aptPackage + ProcessorConfig.PACKAGE_PATH_NAME, //包名  APT 存放的路径
                    TypeSpec.classBuilder(finalClassName) //类名
                            .addSuperinterface(ClassName.get(pathType)) //实现ARouterLoadPath接口  implements ARouterPath==pathType
                            .addModifiers(Modifier.PUBLIC) //public修饰符
                            .addMethod(methodBuilder.build()) //方法的构建(方法参数 + 方法体)
                            .build()) //类构建完成
                    .build() //JavaFile构建完成
                    .writeTo(filer); //文件生成器开始生成类文件

            //给group生成类的中间map赋值
            mAllGroupMap.put(entry.getKey(), finalClassName);
        }
    }

    /**
     * 校验@ARouter注解的值,如果group未填写就从必填项path中截取数据
     * @param bean 路由详细信息,最终实体封装类
     */
    private final boolean checkRouterPath(RouterBean bean) {
        String group = bean.getGroup(); //"app"   "login"
        String path = bean.getPath();   //"/app/MainActivity"

        //必须“/”开头
        if (ProcessorUtils.isEmpty(path) || !path.startsWith("/")) {
            messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter error,Must start with ’/‘");
            return false;
        }

        //比如开发者代码为:path = "/MainActivity",最后一个 / 符号必然在字符串第1位
        if (path.lastIndexOf("/") == 0) {
            //架构师定义规范,让开发者遵循
            messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter error,example : /app/MainActivity");
            return false;
        }

        //从第一个 / 到第二个 / 中间截取,如:/app/MainActivity 截取出 app,order,personal 作为group
        String finalGroup = path.substring(1, path.indexOf("/", 1));

        //app,order,personal == options

        //@ARouter注解中的group有赋值情况
        if (!ProcessorUtils.isEmpty(group) && !group.equals(options)) {
            // 架构师定义规范,让开发者遵循
            messager.printMessage(Diagnostic.Kind.ERROR, "@ARouter error,Package and group names must match");
            return false;
        } else {
            bean.setGroup(finalGroup);
        }

        //如果真的返回ture   RouterBean.group  xxxxx 赋值成功 没有问题
        return true;
    }
}

此时就生成了想要的类,然后还需要一个查找的类,现在先不封装,因为在传递的时肯定需要传递参数,所以说也要把参数的传递封装好。

跳转的代码代码如下:

RouterManager.getInstance()
        .build("/login/LoginActivity")
        .withString("name", "app")
        .navigation(context);

编写路由管理类

职责是找到group和path

/**
 * 整个目标
 * 第一步:查找 ARouter$$Group$$personal ---> ARouter$$Path$$personal
 * 第二步:使用 ARouter$$Group$$personal ---> ARouter$$Path$$personal
 */
public class RouterManager {

    private String group; //路由的组名 app,order,personal ...
    private String path;  //路由的路径  例如:/app/MainActivity

    /**
     * 上面定义的两个成员变量意义:
     * 1.拿到ARouter$$Group$$personal  根据组名 拿到 ARouter$$Path$$personal
     * 2.操作路径,通过路径 拿到  Activity类,就可以实现跳转了
     */

    //单例模式
    private static RouterManager instance;

    public static RouterManager getInstance() {
        if (instance == null) {
            synchronized (RouterManager.class) {
                if (instance == null) {
                    instance = new RouterManager();
                }
            }
        }
        return instance;
    }

    //提供性能  LRU缓存
    private LruCache<String, ARouterGroup> groupLruCache;
    private LruCache<String, ARouterPath> pathLruCache;

    //为了拼接,例如:ARouter$$Group$$personal
    private final static String FILE_GROUP_NAME = "ARouter$$Group$$";

    private RouterManager() {
        groupLruCache = new LruCache<>(100);
        pathLruCache = new LruCache<>(100);
    }

    /***
     * @param path 例如:/order/Order_MainActivity
     *      * @return
     */
    public BundleManager build(String path) {
        if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
            throw new IllegalArgumentException("不按常理出牌 path乱搞的啊,正确写法:如 /order/Order_MainActivity");
        }

        if (path.lastIndexOf("/") == 0) { //只写了一个 /
            throw new IllegalArgumentException("不按常理出牌 path乱搞的啊,正确写法:如 /order/Order_MainActivity");
        }

        // 截取组名  /order/Order_MainActivity  finalGroup=order
        String finalGroup = path.substring(1, path.indexOf("/", 1)); // finalGroup = order

        if (TextUtils.isEmpty(finalGroup)) {
            throw new IllegalArgumentException("不按常理出牌 path乱搞的啊,正确写法:如 /order/Order_MainActivity");
        }

        //证明没有问题,没有抛出异常
        this.path =  path;  //最终的效果:如 /order/Order_MainActivity
        this.group = finalGroup; //例如:order,personal

        //TODO 走到这里后  grooup 和 path 没有任何问题   app,order,personal      /app/MainActivity

        return new BundleManager(); //Builder设计模式 之前是写里面的, 现在写外面吧
    }

    //真正的导航
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    public Object navigation(Context context, BundleManager bundleManager) {
        //例如:寻找 ARouter$$Group$$personal  寻址   ARouter$$Group$$order   ARouter$$Group$$app
        String groupClassName = "com.hbsf." + group + "." + "arouter.group." +FILE_GROUP_NAME + group;
        Log.e("derry >>>", "navigation: groupClassName=" + groupClassName);

        try {
            //TODO 第一步 读取路由组Group类文件
            ARouterGroup loadGroup = groupLruCache.get(group);
            if (null == loadGroup) { // 缓存里面没有东东
                //加载APT路由组Group类文件 例如:ARouter$$Group$$order
                Class<?> aClass = Class.forName(groupClassName);
                //初始化类文件
                loadGroup = (ARouterGroup) aClass.newInstance();

                //保存到缓存
                groupLruCache.put(group, loadGroup);
            }

            if (loadGroup.getGroupMap().isEmpty()) {
                throw new RuntimeException("路由表Group报废了..."); //Group这个类 加载失败
            }

            //TODO 第二步 读取路由Path类文件
            ARouterPath loadPath = pathLruCache.get(path);
            if (null == loadPath) { //缓存里面没有东东 Path
                //1.invoke loadGroup
                //2.Map<String, Class<? extends ARouterLoadPath>>
                Class<? extends ARouterPath> clazz = loadGroup.getGroupMap().get(group);

                //3.从map里面获取 ARouter$$Path$$personal.class
                loadPath = clazz.newInstance();

                //保存到缓存
                pathLruCache.put(path, loadPath);
            }

            //TODO 第三步 跳转
            if (loadPath != null) { // 健壮
                if (loadPath.getPathMap().isEmpty()) { //pathMap.get("key") == null
                    throw new RuntimeException("路由表Path报废了...");
                }

                //最后才执行操作
                RouterBean routerBean = loadPath.getPathMap().get(path);

                if (routerBean != null) {
                    switch (routerBean.getTypeEnum()) {
                        case ACTIVITY:
                            Intent intent = new Intent(context, routerBean.getMyClass()); //例如:getClazz == Order_MainActivity.class
                            intent.putExtras(bundleManager.getBundle()); //携带参数
                            context.startActivity(intent);
                            break;
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

参数管理类BundleManager

建造者设计模式

/**
 * 跳转时 ,用于参数的传递
 */
public class BundleManager {

    //Intent传输  携带的值,保存到这里
    private Bundle bundle = new Bundle();

    private Call call;

    public Call getCall() {
        return call;
    }

    public void setCall(Call call) {
        this.call = call;
    }

    public Bundle getBundle() {
        return this.bundle;
    }

    //对外界提供,可以携带参数的方法
    public BundleManager withString(@NonNull String key, @Nullable String value) {
        bundle.putString(key, value);
        return this; //链式调用效果 模仿开源框架
    }

    public BundleManager withBoolean(@NonNull String key, @Nullable boolean value) {
        bundle.putBoolean(key, value);
        return this;
    }

    public BundleManager withInt(@NonNull String key, @Nullable int value) {
        bundle.putInt(key, value);
        return this;
    }

    public BundleManager withSerializable(@NonNull String key, @Nullable Serializable object) {
        bundle.putSerializable(key, object);
        return this;
    }

    public BundleManager withBundle(Bundle bundle) {
        this.bundle = bundle;
        return this;
    }


    //完成跳转
    public Object navigation(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            return RouterManager.getInstance().navigation(context, this);
        }
        return null;
    }
}

整理下逻辑,上述通过build方法返回了一个BundleManager,此时则可以通过建造者模式不停的拼接参数,最后把他们放到bundle对象中,当调用navigation后,就会回到RouterManager,去寻找group和path,完成跳转

需求升级

假设在LoginActivity有个属性,并用注解进行修饰

@Parameter
String name;

并在oncreat中调用类似的如下的方法,就可以自动拿到value

ParameterManager.getInstance().loadParameter(this);

如果想要自动赋值,还要再编写一个类似的注解解析器,专门用来解析@Parameter注解,并生成下面代码

public class LoginActivity$$Parameter implements ParameterGet {
  @Override
  public void getParameter(Object targetParameter) {
    LoginActivity t = (LoginActivity) targetParameter;
    t.name = t.getIntent().getStringExtra("name");
  }
}

类似的也需要一个接口进行规范约束

public interface ParameterGet {
    /**
     * 目标对象.属性名 = getIntent().属性类型... 完成赋值操作
     * @param targetParameter 目标对象:例如:MainActivity 中的那些属性
     */
    void getParameter(Object targetParameter);

}
编写ParameterProcessor

这个解析器是用来生成参数代码的

@AutoService(Processor.class) //开启
@SupportedAnnotationTypes({ProcessorConfig.PARAMETER_PACKAGE}) //服务的注解
@SupportedSourceVersion(SourceVersion.RELEASE_7) 
public class ParameterProcessor extends AbstractProcessor {

    private Elements elementUtils; //类信息
    private Types typeUtils;  //具体类型
    private Messager messager; //日志
    private Filer filer;  //生成器

    //临时map存储,用来存放被@Parameter注解的属性集合,生成类文件时遍历
    //key:类节点, value:被@Parameter注解的属性集合
    private Map<TypeElement, List<Element>> tempParameterMap = new HashMap<>();

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

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        if (set.isEmpty()) {
            messager.printMessage(Diagnostic.Kind.NOTE, "并没有发现 被@ARouter注解的地方呀");
            return false; //没有机会处理
        }

        //扫描的时候,看那些地方使用到了@Parameter注解
        if (!ProcessorUtils.isEmpty(set)) {
            //获取所有被 @Parameter 注解的 元素(属性)集合
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(Parameter.class);

            if (!ProcessorUtils.isEmpty(elements)) {
                //TODO 给仓库 存储相关信息
                for (Element element : elements) { // element == name, sex,  age

                    //字段节点的上一个节点 类节点==Key
                    //注解在属性的上面,属性节点父节点 是 类节点
                    TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();

                    //enclosingElement == Personal_MainActivity == key

                    if (tempParameterMap.containsKey(enclosingElement)) {
                        tempParameterMap.get(enclosingElement).add(element);
                    } else { // 没有key Personal_MainActivity
                        List<Element> fields = new ArrayList<>();
                        fields.add(element);
                        tempParameterMap.put(enclosingElement, fields); // 加入缓存
                    }
                } //for end  缓存 tempParameterMap有值了

                //TODO 生成类文件
                //判断是否有需要生成的类文件
                if (ProcessorUtils.isEmpty(tempParameterMap)) return true;

                TypeElement activityType = elementUtils.getTypeElement(ProcessorConfig.ACTIVITY_PACKAGE);
                TypeElement parameterType = elementUtils.getTypeElement(ProcessorConfig.AROUTER_AIP_PARAMETER_GET);

                //生成方法
                //Object targetParameter
                ParameterSpec parameterSpec = ParameterSpec.builder(TypeName.OBJECT, ProcessorConfig.PARAMETER_NAME).build();

                //循环遍历 缓存tempParameterMap
                //可能很多地方都使用了 @Parameter注解,那么就需要去遍历 仓库
                for (Map.Entry<TypeElement, List<Element>> entry : tempParameterMap.entrySet()) {
                    //key:   Personal_MainActivity
                    //value: [name,sex,age]
                    TypeElement typeElement = entry.getKey();

                    //非Activity直接报错
                    //如果类名的类型和Activity类型不匹配
                    if (!typeUtils.isSubtype(typeElement.asType(), activityType.asType())) {
                        throw new RuntimeException("@Parameter注解目前仅限用于Activity类之上");
                    }

                    //是Activity
                    //获取类名 == Personal_MainActivity
                    ClassName className = ClassName.get(typeElement);

                    //方法生成成功
                    ParameterFactory factory = new ParameterFactory.Builder(parameterSpec)
                            .setMessager(messager)
                            .setElementUtils(elementUtils)
                            .setTypeUtils(typeUtils)
                            .setClassName(className)
                            .build();

                    //Personal_MainActivity t = (Personal_MainActivity) targetParameter;
                    factory.addFirstStatement();

                    //难点 多行
                    for (Element element : entry.getValue()) {
                        factory.buildStatement(element);
                    }

                    //最终生成的类文件名(类名$$Parameter) 例如:Personal_MainActivity$$Parameter
                    String finalClassName = typeElement.getSimpleName() + ProcessorConfig.PARAMETER_FILE_NAME;
                    messager.printMessage(Diagnostic.Kind.NOTE, "APT生成获取参数类文件:" +
                            className.packageName() + "." + finalClassName);

                    //开始生成文件,例如:PersonalMainActivity$$Parameter
                    try {
                        JavaFile.builder(className.packageName(), //包名
                                TypeSpec.classBuilder(finalClassName) //类名
                                        .addSuperinterface(ClassName.get(parameterType)) //implements ParameterGet 实现ParameterLoad接口
                                        .addModifiers(Modifier.PUBLIC) //public修饰符
                                        .addMethod(factory.build()) //方法的构建(方法参数 + 方法体)
                                        .build()) //类构建完成
                                .build() //JavaFile构建完成
                                .writeTo(filer); //文件生成器开始生成类文件
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return true;
    }
}

所有的代码通过Factory生成

/*
   目的 生成以下代码:
        @Override
        public void getParameter(Object targetParameter) {
              Personal_MainActivity t = (Personal_MainActivity) targetParameter;
              t.name = t.getIntent().getStringExtra("name");
              t.sex = t.getIntent().getStringExtra("sex");
        }
 */
public class ParameterFactory {


    // 方法的构建
    private MethodSpec.Builder method;

    // 类名,如:MainActivity  /  Personal_MainActivity
    private ClassName className;

    // Messager用来报告错误,警告和其他提示信息
    private Messager messager;

    // type(类信息)工具类,包含用于操作TypeMirror的工具方法
    private Types typeUtils;

    private Elements elementUtils;

    // 不想用户使用此构造函数,必须使用Builder设计模式
    private ParameterFactory(Builder builder) {
        this.messager = builder.messager;
        this.className = builder.className;
        this.typeUtils = builder.typeUtils;
        this.elementUtils = builder.elementUtils;
        // 生成此方法
        // 通过方法参数体构建方法体:public void getParameter(Object target) {
        method = MethodSpec.methodBuilder(ProcessorConfig.PARAMETER_METHOD_NAME)
                .addAnnotation(Override.class)
                .addModifiers(Modifier.PUBLIC)
                .addParameter(builder.parameterSpec);
    }

    /** 只有一行
     * Personal_MainActivity t = (Personal_MainActivity) targetParameter;
     */
    public void addFirstStatement() {
        method.addStatement("$T t = ($T) " + ProcessorConfig.PARAMETER_NAME, className, className);
    }

    public MethodSpec build() {
        return method.build();
    }

    /** 多行 循环 复杂
     * 构建方体内容,如:t.s = t.getIntent.getStringExtra("s");
     * @param element 被注解的属性元素
     */
    public void buildStatement(Element element) {
        // 遍历注解的属性节点 生成函数体
        TypeMirror typeMirror = element.asType();

        // 获取 TypeKind 枚举类型的序列号
        int type = typeMirror.getKind().ordinal();

        // 获取属性名  name  age  sex
        String fieldName = element.getSimpleName().toString();

        // 获取注解的值
        String annotationValue = element.getAnnotation(Parameter.class).name();

        // 配合: t.age = t.getIntent().getBooleanExtra("age", t.age ==  9);
        // 判断注解的值为空的情况下的处理(注解中有name值就用注解值)
        annotationValue = ProcessorUtils.isEmpty(annotationValue) ? fieldName : annotationValue;

        // TODO 最终拼接的前缀:
        String finalValue = "t." + fieldName;

        // t.s = t.getIntent().
        // TODO t.name = t.getIntent().getStringExtra("name");
        String methodContent = finalValue + " = t.getIntent().";

        TypeElement serializableElement = elementUtils.getTypeElement(ProcessorConfig.SERIALIZABLE);
        TypeMirror serializableMirror = serializableElement.asType();

        // TypeKind 枚举类型不包含String
        if (type == TypeKind.INT.ordinal()) {
            // t.s = t.getIntent().getIntExtra("age", t.age);
            methodContent += "getIntExtra($S, " + finalValue + ")";  // 有默认值
        } else if (type == TypeKind.BOOLEAN.ordinal()) {
            // t.s = t.getIntent().getBooleanExtra("isSuccess", t.age);
            methodContent += "getBooleanExtra($S, " + finalValue + ")";  // 有默认值
        } else  { // String 类型,没有序列号的提供 需要自己完成
            // t.s = t.getIntent.getStringExtra("s");
            // typeMirror.toString() java.lang.String
            if (typeMirror.toString().equalsIgnoreCase(ProcessorConfig.STRING)) {
                // String类型
                methodContent += "getStringExtra($S)"; // 没有默认值
            }
        }
       if (methodContent.endsWith(")")) { // 抱歉  全部的 getBooleanExtra  getIntExtra   getStringExtra
            // 参数二 9 赋值进去了
            // t.age = t.getIntent().getBooleanExtra("age", t.age ==  9);
            method.addStatement(methodContent, annotationValue);
        } else {
            messager.printMessage(Diagnostic.Kind.ERROR, "目前暂支持String、int、boolean传参");
        }
    }

    /**
     * 为了完成Builder构建者设计模式
     */
    public static class Builder {

        // Messager用来报告错误,警告和其他提示信息
        private Messager messager;

        // 操作Element工具类 (类、函数、属性都是Element)
        private Elements elementUtils;

        // type(类信息)工具类,包含用于操作TypeMirror的工具方法
        private Types typeUtils;

        // 类名,如:MainActivity
        private ClassName className;

        // 方法参数体
        private ParameterSpec parameterSpec;

        public Builder(ParameterSpec parameterSpec) {
            this.parameterSpec = parameterSpec;
        }

        public Builder setMessager(Messager messager) {
            this.messager = messager;
            return this;
        }

        public Builder setClassName(ClassName className) {
            this.className = className;
            return this;
        }

        public Builder setElementUtils(Elements elementUtils) {
            this.elementUtils = elementUtils;
            return this;
        }

        public Builder setTypeUtils(Types typeUtils) {
            this.typeUtils = typeUtils;
            return this;
        }

        public ParameterFactory build() {
            if (parameterSpec == null) {
                throw new IllegalArgumentException("parameterSpec方法参数体为空");
            }

            if (className == null) {
                throw new IllegalArgumentException("方法内容中的className为空");
            }

            if (messager == null) {
                throw new IllegalArgumentException("messager为空,Messager用来报告错误、警告和其他提示信息");
            }

            return new ParameterFactory(this);
        }
    }
}

此时就生成了自动赋值代码

与之前一样,还需要编写寻找类

编写ParameterManager
/**
 * 参数的 加载管理器
 *  第一步:查找 Personal_MainActivity$$Parameter
 *  第二步:使用 Personal_MainActivity$$Parameter  this 给他
 */
public class ParameterManager {

    private static ParameterManager instance;

    // private boolean isCallback;

    public static ParameterManager getInstance() {
        if (instance == null) {
            synchronized (ParameterManager.class) {
                if (instance == null) {
                    instance = new ParameterManager();
                }
            }
        }
        return instance;
    }

    //LRU缓存 key=类名      value=参数加载接口
    private LruCache<String, ParameterGet> cache;

    private ParameterManager() {
        cache = new LruCache<>(100);
    }

    //为什么还要拼接,此次拼接 是为了寻找他
    static final String FILE_SUFFIX_NAME = "$$Parameter"; //为了这个效果:Order_MainActivity + $$Parameter

    //使用者 只需要调用这一个方法,就可以进行参数的接收
    public void loadParameter(Activity activity) { //必须拿到 Personal_MainActivity
        String className = activity.getClass().getName(); //className == Personal_MainActivity

        ParameterGet parameterLoad = cache.get(className); //先从缓存里面拿 如果有  如果没有
        if (null == parameterLoad) { //缓存里面没东东   提高性能
            //拼接 如:Order_MainActivity + $$Parameter
            //类加载Order_MainActivity + $$Parameter
            try {
                //类加载Personal_MainActivity + $$Parameter
                Class<?> aClass = Class.forName(className + FILE_SUFFIX_NAME);
                //用接口parameterLoad = 接口的实现类Personal_MainActivity
                parameterLoad = (ParameterGet) aClass.newInstance();
                cache.put(className, parameterLoad); //保存到缓存
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        parameterLoad.getParameter(activity); //最终的执行  会执行自动生成的类中的赋值方法
    }
}

调用loadParameter就会自动寻找生成的类,完成自动赋值。

需求再升级

现在需要去调用其他模块的方法,此需求肯定需要common在中间当皮条客,在其中定义接口给暴露者实现

此时第一步在api中定义接口规范

public interface Call {
}

app模块调用login模块为例

暴露者(login)

继承common定义的接口,并实现,并使用@ARouter进行注册

第一步需要在common中定义一个login暴露的规范

public interface LoginService extends Call {
    void printf();
}

第二步login实现LoginService

@ARouter(path = "/login/LoginServiceImpl")
public class LoginServiceImpl implements LoginService {
    @Override
    public void printf() {
        Log.e("成功调用", "login");
    }
}

之前做的ARouter注解只支持Activity,现在需要支持Call,所以先需要修改RouterBean的枚举添加CALL

再修改ARouterProcessor

if (typeTool.isSubtype(elementMirror, activityMirror)) {
    routerBean.setTypeEnum(RouterBean.TypeEnum.ACTIVITY);
} else if (typeTool.isSubtype(elementMirror, callMirror)) {
    routerBean.setTypeEnum(RouterBean.TypeEnum.CALL);
} else {
    throw new RuntimeException("@ARouter must be in ctivity above");
}

如下生成的path类如下:

public class ARouter$$Path$$login implements ARouterPath {
  @Override
  public Map<String, RouterBean> getPathMap() {
    Map<String, RouterBean> pathMap = new HashMap<>();
    pathMap.put("/login/LoginActivity", RouterBean.create(RouterBean.TypeEnum.ACTIVITY, LoginActivity.class, "/login/LoginActivity", "login"));
    pathMap.put("/login/LoginServiceImpl", RouterBean.create(RouterBean.TypeEnum.CALL, LoginServiceImpl.class, "/login/LoginServiceImpl", "login"));
    return pathMap;
  }
}
使用者(app)

appMainActivity声明下述属性:

@Parameter(name = "/login/LoginServiceImpl")
LoginService loginService;

想要的效果是使用暴露者定义的path和group,通过路由管理类就可以找到对应的类。

在之前自动给参数赋值中,普通的属性类型如下代码则可拿到相应的Value

t.name = t.getIntent().getStringExtra("name");

如果是Call的话,需要进行升级优化:

t.loginService = (LoginService) RouterManager.getInstance().build("/login/LoginServiceImpl").navigation(t);

优化@Parameter修饰的类型,支持类型加上Call

修改Factory

if (typeMirror.toString().equalsIgnoreCase(ProcessorConfig.STRING)) {
    //String类型
    methodContent += "getStringExtra($S)"; //没有默认值
}  else if (typeUtils.isSubtype(typeMirror, callMirror)) {
    methodContent = "t." + fieldName + " = ($T) $T.getInstance().build($S).navigation(t)";
    method.addStatement(methodContent,
            TypeName.get(typeMirror),
            ClassName.get(ProcessorConfig.AROUTER_API_PACKAGE + ".manager", ProcessorConfig.ROUTER_MANAGER),
            annotationValue);
    return;
}

然后修改路由管理器RouterManager,之前只支持ACTIVITY,现在需要支持CALL

switch (routerBean.getTypeEnum()) {
    case ACTIVITY:
        Intent intent = new Intent(context, routerBean.getMyClass()); //例如:getClazz == Order_MainActivity.class
        intent.putExtras(bundleManager.getBundle()); //携带参数
        context.startActivity(intent);
        break;
    case CALL:
        Class<?> clazz = routerBean.getMyClass();
        Call call = (Call) clazz.newInstance();
        bundleManager.setCall(call);
        return  bundleManager.getCall();

此时就可以通过loginService属性去调用printf方法,使用如下:

public class MainActivity extends AppCompatActivity {
    @Parameter(name = "/login/LoginServiceImpl")
    LoginService loginService;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ParameterManager.getInstance().loadParameter(this);
        loginService.printf();
    }
}

总结

ARouter是很灵活的,不一定只给Activity用,普通的类也可以用,只需要传入路径,就可以在自动在path的map中添加一条数据,在获取此数据的时,动态的去调用RouterManager的找类方法则可获取到其他模块暴露的接口了。

此篇文章学习自享学课堂的组件化课程,笔者只是记录学习的过程和笔记。

此篇文章笔者在自己的本科毕设项目中进行了实现,项目github地址如下:https://github.com/2644065326/StudyForum

等毕业论文审核完毕,笔者会把自己的论文分享出来。

2023年1月2日更新:论文地址如下:论文地址

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值