zoukankan      html  css  js  c++  java
  • Android 组件化方案探索与思考

    Android 组件化方案探索与思考

    组件化项目,通过gradle脚本,实现module在编译期隔离,运行期按需加载,实现组件间解耦,高效单独调试。

    本项目github地址

    https://github.com/wang709693972wei/CompontentDemo
    先来一张效果图,建议读者clone项目后跟着项目看这篇文章,有任何不明白的地方可留言或者联系我,我看到后会立刻回复你。


     

    组件化初衷

    • APP版本不断的迭代,新功能的不断增加,业务也会变的越来越复杂,维护成本高。
    • 业务耦合度高,代码越来越臃肿,团队内部多人协作开发困难。
    • Android项目在编译代码的时候电脑会非常卡,又因为单一工程下代码耦合严重,每修改一处代码后都要重新编译打包测试,导致非常耗时。
    • 方便单元测试,改动单独一个业务模块,不需要着重于关注其他模块被影响。

    什么是组件化

    组件化就是将一个app分成多个Module,如下图,每个Module都是一个组件(也可以是一个基础库供组件依赖),开发的过程中我们可以单独调试部分组件,组件间不需要互相依赖,但可以相互调用,最终发布的时候所有组件以lib的形式被主app工程依赖并打包成一个apk。


     

    组件化优势

    • 组件化就是将通用模块独立出来,统一管理,以提高复用,将页面拆分为粒度更小的组件,组件内部除了包含UI实现,还包含数据层和逻辑层。
    • 每个工程都可以独立编译、加快编译速度,独立打包。
    • 每个工程内部的修改,不会影响其他工程。
    • 业务库工程可以快速拆分出来,集成到其他App中。
    • 迭代频繁的业务模块采用组件方式,业务线研发可以互不干扰、提升协作效率,并控制产品质量,加强稳定性。
    • 并行开发,团队成员只关注自己的开发的小模块,降低耦合性,后期维护方便等。

    指导思想

    • 组件拆分:将一个project划分成业务组件、基础组件、路由组件。其中业务组件是相互隔离的,可以单独调试,基础组件提供业务组件所公用的功能,路由组件为业务组件之间通信提供支持。
    • 组件隔离:业务组件之间的隔离,可以单独调试。
    • 核心法则:编译期隔离,运行期按需依赖。

    依赖关系


     

    组件化需要考虑的问题

    • 模式切换:如何使得APP在单独调试跟整体调试自由切换
    • 资源冲突:当我们创建了多个Module的时候,如何解决相同资源文件名合并的冲突
    • 依赖关系:多个Module之间如何引用一些共同的library以及工具类
    • 组件通信:组件化之后,Module之间是相互隔离的,如何进行UI跳转以及方法调用
    • 入口参数:我们知道组件之间是有联系的,所以在单独调试的时候如何拿到其它的Module传递过来的参数

    组件化后项目结构如下图


     

    理论说了那么多,下面开始撸代码

    实现步骤

    1、全局设置Gradle ,每一个业务Module需要的版本都定义在这里方便后期维护多个Module版本号

    ext {
        // Sdk and tools
        minSdkVersion = 16
        targetSdkVersion = 26
        compileSdkVersion = 26
        buildToolsVersion = '26.0.2'
        supportLibraryVersion = '26.1.0'
    
        // App dependencies
        aRouter = '1.2.2'
        leakcanaryVersion = '1.3'
        glideVersion = '3.7.0'
    }
    
     ####每个业务Module编译依赖版本
     compileSdkVersion rootProject.ext.compileSdkVersion
     buildToolsVersion rootProject.ext.buildToolsVersion
     minSdkVersion rootProject.ext.minSdkVersion
     targetSdkVersion rootProject.ext.targetSdkVersion
    
    

    2、模式切换

    组件化后的每一个业务的module都可以是一个单独的APP(isModuleRun=false), release 包的时候各个业务module作为lib依赖,这里完全由一个变量控制,在根项目 gradle.properties里面的 isModuleRun=true。

    isModuleRun状态不同,加载application和AndroidManifest都不一样,以此来区分是独立的APK还是lib,
    实现方式如下
    build.grade里面配置

    if (isModuleRun.toBoolean()) {
        apply plugin: 'com.android.application'
    } else {
        apply plugin: 'com.android.library'
    }
    
    ### 单Module运行需要配置
    sourceSets {
            main {
                if (isModuleRun.toBoolean()) {
                    manifest.srcFile 'src/main/debug/AndroidManifest.xml'
                } else {
                    manifest.srcFile 'src/main/AndroidManifest.xml'
                    java {
                        //全部Module一起编译的时候剔除debug目录
                        exclude '**/debug/**'
                    }
                }
            }
        }
    
    

    3、资源冲突

    业务Module和BaseModule资源文件名称重复会产生冲突,解决方案在
    每个 module 都有 app_name,为了不让资源名重名,在每个组件的 build.gradle 中增加 resourcePrefix “xxx_强行检查资源名称前缀。
    固定每个组件的资源前缀。但是 resourcePrefix 这个值只能限定 xml 里面的资源,并不能限定图片资源。
    个人认为约定大于配置,团队内协定好规范,可以避免冲突。

    4、组件通讯
    不熟悉ARouter基本用法的可以看看我的这篇文章
    阿里巴巴ARouter基本使用方法

    组件通讯框架在github上有star最多的有ARouter和ActivityArouter,前者是个人项目,后者是阿里巴巴开源,权衡之下选择阿里的ARouter,
    各业务Module之前不需要任何依赖可以通过路由跳转,完美解决业务之间耦合
    使用方式如下。

     if (BuildConfig.DEBUG) {   // 这两行必须写在init之前,否则这些配置在init过程中将无效
               ARouter.openLog();     // 打印日志
               ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
            }
    ARouter.init(this); // 尽可能早,推荐在Application中初始化
    
    
    compile "com.alibaba:arouter-api:$rootProject.aRouter"
    每个业务Module都需要添加注解
    annotationProcessor 'com.alibaba:arouter-compiler:1.1.3'
    
    

    跳转方法
    在目标Activity上添加path

    @Route(path = ARouterManager.BModuleActivity)
    public class BModuleActivity extends BaseActivity {
        @Autowired
        public String name;
        @Autowired(name = "age")
        int age;
        TextView txt;
    
        @Override
        protected int getLayoutId() {
            return R.layout.b_module_layout;
        }
    
        @Override
        protected void initView() {
            txt = findViewById(R.id.txt);
            //String name = getIntent().getStringExtra("name"); 也可以这样接受参数
            ARouter.getInstance().inject(this);
            txt.setText("name:" + name + ",age:" + age);
    
      #开始跳转
     btn2.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    // 2. 跳转并携带参数
                    ARouter.getInstance().build(ARouterManager.BModuleActivity)
                            .withString("name", "888")
                            .withInt("age", 11)
                            .navigation();
                }
            });
    
    
    /**
     * 路由管理类
     */
    
    public final class ARouterManager {
    
        public static final String AFragment = "/amodule/AFragment";
        public static final String BFragment = "/bmodule/BFragment";
        public static final String CFragment = "/cmodule/CFragment";
    
        public static final String AModuleActivity = "/amodule/AAModuleActivity";
        public static final String BModuleActivity = "/bmodule/BModuleActivity";
        public static final String CModuleActivity = "/cmodule/CModuleActivity";
    
    }
    
    

    上述只使用了ARouter的简单用法,更多进阶用法请参考ARouter文档,
    ARouter

    5、Application

    当组件单独运行的时候,每个Module自成一个APK,那么就意味着会有多个Application,很显然我们不愿意重复写这么多代码,所以我们只需要定义一个BaseApplication即可,其它的Application直接继承此BaseApplication就OK了,BaseApplication里面还可定义公用的参数。

    链接:https://www.jianshu.com/p/010d946e8f67,转载请注明原创

    微信公众号:终端研发部

    技术
    技术
  • 相关阅读:
    mybatis2入门程序
    mybatis1
    mybeats与jdbc问题分析
    mysqljdbc简单连接释放
    jdbc问题记录
    section,article,div
    HB调试前端开发移动
    HTML,XML,XHTML
    访问地图
    OAuth
  • 原文地址:https://www.cnblogs.com/codeGoogler/p/9017927.html
Copyright © 2011-2022 走看看