Android开发经验谈Android开发技术干货

Android组件化架构实现(二)

2017-09-27  本文已影响465人  码农一颗颗

本篇文章开发一个组件化架构的Gradle Plugin。
插件的作用:
1.每个Module之间实现真正的解耦。
2.在开发阶段每个Module都可以在不经过任何配置的情况下独立运行。

Android组件化架构实现(一)
自定义GradlePlugin
GitHub地址

1.每个Module之间实现真正的解耦。

最初Module之间的依赖是在build.gradledependencies{}进行配置,这样Module之间的类就可以相互调用,虽然在要求上是不允许的,但是没有在编译阶段解决这个问题,没有实现真正的解耦,留下了误操作的隐患。

普通依赖形式.png
在开发阶段使用这个插件可以实现Module之间的真正解耦,在编译的时候Module之间是没有任何依赖的,在Run Module时期自动添加依赖,这样就可以保证在写代码时期Module之间没有依赖,所以就不能相互调用类。
插件依赖形式.png

2.在开发阶段每个Module都可以在不经过任何配置的情况下独立运行。

组件化的另一个意义就是在开发阶段可以单独运行每个Module,这样可以提高调试运行速度,不需要的Module没有编译,省去编译时间。
但是网上很多文章都是需要更改配置,这样在开发阶段太复杂了,使用当前插件可以使每个Module在不更改配置的情况下直接运行。


每个Module都可以单独运行.png

3.实现组件化Gradle Plugin

1.对于如何自定义Gradle Plugin前面的文章已经写到请点击这里本文就不在说明。
2.在每个Module下面都创建一个gradle.properties文件,这个文件设置的是当前Module作用域内的全局属性,在插件中可以获取。
文件中的标志有三种如下:

#调试的Module
mainName = module_1
#调试Module依赖的libModule
libNames = sub_2_module_1
#当前Module是否Debug模式,当前project的住Module也就是app 不需要这个属性
#如果true那么build.gradle中sourceSets选择debug目录下的文件和资源
isDebug = true

3.项目插件的结构如下图:
如图可以看出
1)app在build.gradle使用apply plugin: 'component_gradle_plugin'插件,gradle.properties中配置当前Module名字mainName=app,依赖库Module名字libNames=module_1,module_2
2)Module_1模块gradle.properties中配置当前Module名字mainName=module_1,依赖库Module名字libNames=sub_2_module_1
3)Module_2模块同上解释一样
4)Sub_2_Module_1模块gradle.properties中配置没有libNames属性,表示他不依赖任何Module

项目插件的结构.png
4.插件实现如下代码(代码中有注释)
根据这篇文章创建插件Module,插件在运行时会回调ComponentGradlePlugin类,所以我们在这个类中处理每个Module之间的关系。
public class ComponentGradlePlugin implements Plugin<Project> {
    private static final String MAIN_NAME = "mainName";
    private static final String LIBRARY_NAMES = "libNames";
    private static final String IS_DEBUG = "isDebug";
    @Override
    public void apply(Project project) {
        System.out.println("ComponentGradlePlugin--------- start");
        System.out.println("hello apply");
        //获取当前Module gradle.properties中的MAIN_NAME属性,这个属性表示当前Module的名字
        String mainName = "";
        if (project.hasProperty(MAIN_NAME)) {
            mainName = (String) project.getProperties().get(MAIN_NAME);
            System.out.println(MAIN_NAME + " : " + mainName);
        }
        //获取当前Module gradle.properties中的LIBRARY_NAMES属性,这个属性表示MAIN_NAME依赖的库,可以是个列表
        List<String> libNameList = new ArrayList<>();
        if (project.hasProperty(LIBRARY_NAMES)) {
            String[] libNames = ((String) project.getProperties().get(LIBRARY_NAMES)).split(",");
            System.out.println("libNames : " + Arrays.toString(libNames));
            libNameList = Arrays.asList(libNames);
        }
        //获取当前Module gradle.properties中的IS_DEBUG属性,这个属性表示当前Module是否用Debug目录下的代码和资源
        boolean isDebug = true;
        if (project.hasProperty(IS_DEBUG)) {
            isDebug = Boolean.parseBoolean((String) project.getProperties().get(IS_DEBUG));
        }
        //获取当前gradle 运行的Module 名字
        String currName = project.getName();
        System.out.println("currName : " + currName);
        //获取当前gradle 运行的Module 名字
        String mudule = project.getPath().replace(":", "");
        System.out.println("module : " + mudule);
        //获取gradle执行task列表
        List<String> taskNameList = project.getGradle().getStartParameter().getTaskNames();
        System.out.println(taskNameList);
        //获取运行的Module的名字,运行时:app:assembleRelease或者:app:assembleDebug
        String taskName = "";
        if (taskNameList.size() > 0 && taskNameList.get(0).toUpperCase().contains("ASSEMBLE")) {
            try {
                taskName = taskNameList.get(0).split(":")[1];
                System.out.println("taskName : " + taskName);
                System.out.println("taskName Arrays : " + Arrays.toString(taskName.split(":")));
            } catch (Exception e) {
                e.printStackTrace();
                taskName = "";
            }
        }
        //如果assemble的Module是app,那么app的isDebug = true,其他的libNames的isDebug = false;
        //如果assemble的Module是library 那么library的isDebug = true,其他的libNames的isDebug = false;
        if (!"".equals(taskName)
                && !taskName.equals(mainName)) {
            isDebug = false;
        }
        Map<String, String> applyMap = new HashMap<>();
        if (isDebug) {
            applyMap.clear();
            applyMap.put("plugin", "com.android.application");
            project.apply(applyMap);
            //将main的isDebug = true,build.gradle sourceSets中调试代码起作用
        } else {
            applyMap.clear();
            applyMap.put("plugin", "com.android.library");
            project.apply(applyMap);
            //将library的isDebug = false,build.gradle sourceSets中调试代码不起作用
        }
        System.out.println(currName + " : " + applyMap.get("plugin") + " : " + isDebug);
        //添加依赖库,如果不是assemble运行是不要添加依赖库的,否则会报错
        if (!"".equals(taskName)) {
            for (int i = 0; i < libNameList.size(); i++) {
                System.out.println("Dependencies add : " + libNameList.get(i));
                project.getDependencies().add("compile", project.project(":" + libNameList.get(i)));
            }
        }
        if (project.hasProperty(IS_DEBUG)) {
            project.setProperty(IS_DEBUG, isDebug);
        }
        System.out.println("ComponentGradlePlugin--------- end");
        System.out.println();
    }
}

4.运行插件查看打印日志

点击run app,查看如下日志,所有依赖的Module都被调用了

Executing tasks: [:app:assembleDebug]

Configuration on demand is an incubating feature.
ComponentGradlePlugin--------- start
hello apply
mainName : app
libNames : [module_1, module_2]
currName : app
module : app
[:app:assembleDebug]
taskName : app
taskName Arrays : [app]
app : com.android.application : true
Dependencies add : module_1
Dependencies add : module_2
ComponentGradlePlugin--------- end

ComponentGradlePlugin--------- start
hello apply
mainName : module_1
libNames : [sub_2_module_1]
currName : module_1
module : module_1
[:app:assembleDebug]
taskName : app
taskName Arrays : [app]
module_1 : com.android.library : false
Dependencies add : sub_2_module_1
ComponentGradlePlugin--------- end

ComponentGradlePlugin--------- start
hello apply
mainName : sub_2_module_1
currName : sub_2_module_1
module : sub_2_module_1
[:app:assembleDebug]
taskName : app
taskName Arrays : [app]
sub_2_module_1 : com.android.library : false
ComponentGradlePlugin--------- end

Incremental java compilation is an incubating feature.
ComponentGradlePlugin--------- start
hello apply
mainName : module_2
libNames : [sub_2_module_1]
currName : module_2
module : module_2
[:app:assembleDebug]
taskName : app
taskName Arrays : [app]
module_2 : com.android.library : false
Dependencies add : sub_2_module_1
ComponentGradlePlugin--------- end

点击run Module_1,查看如下日志,只有module_1和他依赖的sub_2_module_1被调用,这样其他的Module就不会编译占用时间,调试的时间会更快

ComponentGradlePlugin--------- start
hello apply
mainName : module_1
libNames : [sub_2_module_1]
currName : module_1
module : module_1
[:module_1:assembleDebug]
taskName : module_1
taskName Arrays : [module_1]
module_1 : com.android.application : true
Dependencies add : sub_2_module_1
ComponentGradlePlugin--------- end

ComponentGradlePlugin--------- start
hello apply
mainName : sub_2_module_1
currName : sub_2_module_1
module : sub_2_module_1
[:module_1:assembleDebug]
taskName : module_1
taskName Arrays : [module_1]
sub_2_module_1 : com.android.library : false
ComponentGradlePlugin--------- end

总结:

这个插件可以帮助我们在开发阶段约束Module的代码,使每个Module之间调用都必须使用Router。可以在不修改配置的情况下单独运行每个Module提高开发调试速度。

Android组件化架构实现(一)
自定义GradlePlugin
GitHub地址

上一篇下一篇

猜你喜欢

热点阅读