精华内容
下载资源
问答
  • 【Android 组件化】从模块化到组件化

    千次阅读 2021-05-14 12:22:50
    一、从模块化到组件化、 二、build.gradle 构建脚本分析、





    一、从模块化到组件化



    Android 应用项目 , 都存在一个应用模块 ( Application Module ) , 在 build.gradle 构建脚本中 , 第一个插件配置 com.android.application , 表明 该 Module 编译打包后的输出是 APK 安装包 ; 该项目可以直接运行 ;

    plugins {
        id 'com.android.application'
        id 'kotlin-android'
    }
    

    如果在 build.gradle 配置的是 com.android.library 插件 , 那么 编译 Module 打包后输出的是 aar 依赖库 ; 该项目不能直接运行 ;

    plugins {
        id 'com.android.library'
        id 'kotlin-android'
    }
    

    模块化 :

    随着应用业务增加 , 功能变得越来越复杂 , 不能将所有的功能放在一个 Application 模块中 ;

    大型项目的开发不能只有一个 Module , 大多数情况下 , Android 工程中 , 除了有一个 Application 模块外 , 还有若干 Library 模块提供给应用模块引用 ;

    应用中还可能存在一个基础的 SDK 依赖库 , 提供给 Library 模块引用 , Application 再引用这些 Library 模块 ;


    模块化的缺点 :

    Library 模块中实现了一个功能 , 如果要运行的话 , 需要借助 Application 模块 , 这就需要将整个项目全部编译一遍 , 如果项目有几百个模块 , 调试运行就很困难 ;

    单个开发者可能只负责几个模块 , 还涉及了与其它模块开发人者进行协作 ;


    组件化 :

    组件化是在模块化的基础上 , 可以 动态切换其模块类型 , 将 Library 模块切换成 Application 模块 , 这样独立的模块可以直接运行 ;

    在进行 组件模式 开发时 , 将其变成 Application 模块 , 在 集成模式 开发时 , 将其变成 Library 模块 ;

    组件开发时 , 单个 Library 模块变成 Application 模块 , 可以生成独立运行的 APK 安装包 ;





    二、build.gradle 构建脚本分析



    组件化实现需要依赖 Gradle ;


    build.gradle 脚本都是使用 Groovy 语言编写的代码 , Groovy 也是 JVM 上语言 , 与 Java 语言完全兼容 , 其调用的 api 都是 Java 语言的 ;


    Android Studio 中的 Android 工程 , 在 Project 层级下有一个 build.gradle 构建脚本 , 在 Application 模块 和 Library 模块 中 , 也都各自存在一个 Module 级别的 build.gradle 构建脚本 ;

    Project 下的 build.gradle 编译时会被翻译成 Project.java 类对象 , 该类路径是 gradle-6.5\src\core-api\org\gradle\api\Project.java ;

    // Top-level build file where you can add configuration options common to all sub-projects/modules.
    buildscript {
        ext.kotlin_version = "1.4.31"
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath "com.android.tools.build:gradle:4.1.2"
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    
            // 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
    }
    

    其中的 buildscript , allprojects 等都是 Project.java 中的函数 ;

    @HasInternalProtocol
    public interface Project extends Comparable<Project>, ExtensionAware, PluginAware {
    
        /**
         * <p>Configures the build script classpath for this project.
         *
         * <p>The given closure is executed against this project's {@link ScriptHandler}. The {@link ScriptHandler} is
         * passed to the closure as the closure's delegate.
         *
         * @param configureClosure the closure to use to configure the build script classpath.
         */
        void buildscript(Closure configureClosure);
    
        /**
         * <p>Configures this project and each of its sub-projects.</p>
         *
         * <p>This method executes the given closure against this project and its sub-projects. The target {@link Project}
         * is passed to the closure as the closure's delegate.</p>
         *
         * @param configureClosure The closure to execute.
         */
        void allprojects(Closure configureClosure);
    }
    
    展开全文
  • Android组件化开发之二:组件化架构

    千次阅读 2018-01-16 15:32:19
    组件化开发系列文章 1. Android组件化开发之一:为什么要进行组件化开发 2. Android组件化开发之二:组件化架构 3. Android组件化开发之三:组件化开发手册 4. Android组件化开发之四:组件化填坑之旅 5. Android...

    我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版,欢迎购买。点击进入详情

    组件化开发系列文章

    1. Android组件化开发之一:为什么要进行组件化开发
    2. Android组件化开发之二:组件化架构
    3. Android组件化开发之三:组件化开发手册
    4. Android组件化开发之四:组件化填坑之旅

    5. Android组件化开发之五:组件化开发实战Demo

    从头对一个现有的APP项目进行组件化改造,需要按计划按步骤逐步实现。

    这里我们分为四期计划进行改造,每一期用下面的示例图展示:

    第一期

    主要工作内容:

    1、对APP结构进行划分:APP壳工程、业务组件、Base组件库;

    2、每个结构功能定义清晰;

    3、业务组件路由选型:如Arouter;

    第二期

    主要工作内容:

    1、每个业务组件通过添加application方式能够独立成一个单独module,并且可以独立运行;

    第三期

    主要工作内容:

    1、进一步细分Base基础库,将Base基础库分为基础组件、Base组件;

    2、基础组件是我们业务相关的、能够在内部项目中通用的组件;Base组件是基础功能组件;

    第四期

    主要工作内容:

    1、通过maven将基础组件和Base组件管理起来;

     

    展开全文
  • 文章目录组件化开发系列1. 组件化项目结构图2. 组件化项目模块3. 注意事项4. git地址 组件化开发系列 Android组件化开发之一:为什么要进行组件化开发 Android组件化开发之二:组件化架构 Android组件化开发之三:...

    我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版,欢迎购买。点击进入详情

    组件化开发系列文章

    1. Android组件化开发之一:为什么要进行组件化开发
    2. Android组件化开发之二:组件化架构
    3. Android组件化开发之三:组件化开发手册
    4. Android组件化开发之四:组件化填坑之旅
    5. Android组件化开发之五:组件化开发实战Demo

    1. 组件化项目结构图

    在这里插入图片描述

    2. 组件化项目模块

    1. app模块
      app模块不再是我们原来放置所有业务、所有功能的模块,而是一个“壳”工程。
      "壳"工程的作用有几个:
      1. 统一配置。
        比如需要引入哪些业务组件模块,可以在build.gradle中进行配置。
        在这里插入图片描述
      2. 初始化数据
        可以在app的application类中,进行app数据的初始化操作,比如网络、图片、数据、日志等,还有包括ARouter的初始化;
      3. 业务入口
        通常这个app模块的MainActivity可以作为一个启动页Splash Page的功能,然后在启动页中跳转到各个业务模块中去,从而完成一个app的启动流程。
    2. base模块
      base模块是基础库工程,是壳工程和业务模块工程所引用的工程,其中包括基础框架比如MVP、MVVM等,还有各种第三方库的引用,比如RxJava、Retrofit、Glide、RxPermission等等。
      包括ARouter需要各组件共用的部分,比如继承了IProvider的接口。
      在这里插入图片描述
      在这里插入图片描述
    3. 业务组件
      业务组件这个比较好理解,就是将app拆分成各个比较独立的业务,每个业务可以就可以称之为一个业务组件,比如登录业务module_login、首页业务module_main。
      这些业务组件可以通过配置参数,来决定是否作为一个独立的app运行,也可以参与到整个项目的集成环境中运行。我们通过一个参数比如useModule来控制:
      在这里插入图片描述
      业务模块的控制:
      在这里插入图片描述
      1. ARouter使用
        ARouter使用的时候注意以下4点即可:
        1. @Route声明,从而表明当前类是可以被其它组件打开的;
        2. 通过inject方法,将当前类注入进ARoute,这样当前类被调用才能生效;
        3. 通过navigation方法,可以带上参数等,跳转到其它的声明过@Route的组件;
        4. 通过navigation方法,可以访问其它组件暴露出的方法,当然这个方法需要实现继承IProvider接口;
          在这里插入图片描述

    3. 注意事项

    集成环境的时候,组件的AndroidManifest.xml文件里面不用声明Launcher,不让会在桌面出现这个组件的图标。
    在这里插入图片描述

    4. git地址

    https://github.com/ddnosh/android-demo-componentization

    展开全文
  • Android组件化 三、使用ARouter组件化项目及实现原理

    组件化的实现,主要解决的就是模块的划分,以及划分后的交互问题。
    另外在组件化的过程中,也是一个,代码Review的过程,比如是否使用了通用的父类,以及对业务逻辑是否进行了友好的封装,总之,组件化可以说一面照妖镜,让之前代码存在的耦合问题充分暴露出来。
    这次使用的组件化样例是一个即时通讯软件,以下是组件化之前的样子,其中SuperHelper是底层通用帮助类可以看成CommonBase,封装了一些如数据库和网络访问类型的第三方库,方便程序快速开发。上层是业务实现层,业务实现层包含了程序的所有模块,由于SuperHelper是通用模块,所以SuperHelper中并不存在即时通讯的SDK.由于程序简单,自然SDK就放到上层了。
    我们可以通过程序的包名看到里面实现了一个即时通讯软件几乎都有的模块:
    登录 呼叫 联系人 历史记录 短信
    在这里插入图片描述
    其中登录模块是其他模块可以正常运行的基础,只有登录成功后,且用户在线,才能正常的进行呼叫和短信类的业务,同时登录模块也是需要和所有其他模块产生交互的模块,这主要体现在,登录状态和登录信息相当于是一个常量,在各个模块中都有可能被调用。

    登录之后,另外一个和其他模块有较多交互的是通讯录模块,因为我们进行呼叫业务和短信业务是需要通讯录进行支持的,而且生成历史也是需要通讯录里面的对象。

    以上的分析,就是大致的我们在实现组件化过程中需要考虑的交互接口设计。当然这只是一个大概的设计。
    当前的程序模块图是这样的:
    在这里插入图片描述
    项目比较简单,也做了一定的模块化和内聚,也包括在思想上的代码隔离。这里面对于登录,其他模块都需要他的状态和缓存进行自己的业务,而主要的功能模块,在这个项目中是一个整体,基本上这几个模块和其他模块或多或少的都有关系,把他们放到一起可以很方便的调用彼此的方法和数据对象,这样的调用就会发生在一个模块包下,对其他包的类有引用。这就产生了模块间的耦合,在项目不断发展下,就会发生耦合越来越严重,理清思维越来越困难的问题。

    此时就是引入组件化来解决这个问题的时候了。

    组件化第一步:划分组件与命名

    1.组件的划分

    对于这一步我认为是一个较为简单的过程。快速划分,一般是根据效果图首页的叶签进行划分,这是一个相对想说较为粗糙的划分方法,不过,大多数时候他可以快速的确定一些功能模块。当然这是一个比较偷懒的方法,其实较为好的方法是画出程序的流程图,或者功能模块图,此时再次划分就会是一个相对准确,且有说服力的划分方案了。另外根据项目的大小和功能性能,我们还有一些特殊的划分要求,比如说一个显示组件后面包含着一整套复杂的逻辑,那么我们也可以将这个显示组件和其后面的逻辑进行一个组件化。又或者,大多数应用都存在我的页面,而设置一般也会放到我的页面中,那么有的应用设置非常简单,此时就可以让他和我的在一个组件中,而有的设置十分复杂,那么可能一个设置项就变成了一个组件,如像微信中的扫一扫,朋友圈,摇一摇,他们都至少会是一个组件。所以对于组件的划分我们可以从功能的大小出发,如果是几个小功能组合成一个大功能,且这个功能不是很大,那么这个大功能就是一个组件,小功能做的很大时,也可能自己就是一个组件了。
    下面给出上面项目的组件化之后的结构:
    在这里插入图片描述
    此时已经经过相对成熟的模块划分。其中SuperHelper是作为通用三方出现的,里面包含了常用的三方和一些工具类,这里他不在以module的形式存在,为了保证他的不可修改性,已经将其变为aar的形式引入项目中。
    这里面重点要说的是CommonSDK这个底层通用依赖,他的内部包含了自己的通用三方(SuperHelper.aar)和接入的其他业务SDK,并且根据组件化做了部分业务下沉,主要体现在

    • 对外暴露接口
    • 数据模型
    • 事件总线Event
    • 路径

    而在其之上是现有的业务组件,将所有的功能模块尽可能的划分,然后暴露出组件化过程中存在的问题。和之前的结构图相比,其功能组件层,彼此不在存在关系,此时彼此独立。

    最上层的是App壳层,这一层也不在存在具体的业务类,它主要实现Application对组件的初始化和方便打包。

    2.组件之间的命名

    2.1先说一下组件层的命名建议策略:

    在这里插入图片描述
    此处建议将组件类module加上前缀module,底层依赖不加前缀,以此来区分组件和底层库。

    2.2组件内类的命名

    这里面除了对外开发的接口外,都可以按照正常的方式命名了(这些类已经可以正常的实现具体业务了)。
    这里面关于接口建议如下形式:
    接口声明:组件名称+Service,代表是这个组件对外开放暴露的方法。

    public interface CallService extends IProvider {
    
        public String path="/call/CallService";
    
        public void call(int number);
    
    }
    

    组件内的实现:组件名称+Service+Impl

    @Route(path = CallService.path)
    public class CallServiceImpl implements CallService {
        @Override
        public void call(int number) {
            CallUtil.call(number);
        }
    }
    

    由于组件间不可见,当我们使用别的组件的对外暴露的接口时,需要一个接口管理类:
    组件名称+Service+Manager 代表对所有接入的服务统一管理

    public class CallServiceManager {
    
        private static final String TAG = "CallServiceManager";
    
        private static Context context;
        
        private static LoginService loginService;
        
    	public static void init(Context c){
            context=c;
            
            loginService=ARouter.getInstance().navigation(LoginService.class);
        }
         public static Context getContext() {
            return context;
        }
        
        public static LoginService getLoginService(){return loginService;}
        
    }
    

    这样通过静态服务管理,就可以在组件内较为方便调用其他组件的方法了。

    2.3组件内资源的命名

    首先Android对于同名资源文件的和参数的打包策略是,上层资源覆盖下层,好比我们的底层库有一个颜色资源声明为

    <color name="white">#ffffff</color>
    

    依赖于他的module也声明了一个同名的资源:

    <color name="white">#000000</color>
    

    那么此时我们引用到这个white资源的地方在实际运行的时候会体现为黑色。

    根据这个特性,我们可以在App外壳层最后决定我们的应用的一些整体资源的属性值。

    一般来说,一个应用的整体风格应该是一致,这体现在交付设计图时,会给出字体的大小,和主题颜色,以及一些通用的属性,此时我们可以将这些资源直接放到CommonSDK中,然后供上层组件调用,以此来实现整体风格统一的效果。

    不过仍然存在一些特殊情况,以美团为例,首先美团应用整体风格目前定义为黄色,假设他的导航里面每个分类都是一个组件,那么我希望打开药店的时候整体风格是绿色,打开酒店的时候是淡金色,即进入组件后根据组件的实际业务更换整体风格。此时同样还需要考虑如果这个组件承载的业务量膨胀,进而发展成独立的应用,如美团外卖。显然此时应该将这个组件内的资源独立化即组件内有自己完整的风格不在依赖于整体,也不能被上层覆盖,此时建议的命名方式是:

    <!--组件名称_实际名称-->
    <color name="login_white">#000000</color>
    

    进行命名。

    另外为了更好迎接变换,可以先以组件名称_实际名称此方式命名,在整体风格统一的时候,指向CommonSDK层的资源,在不统一的时候,直接修改指向即可。

    组件化第二部:组件依赖

    这个部分先介绍关于Android Studio在3.0之前和3.0之后关于依赖关键字的使用和意义。
    Android3.0之前:

    名称 功能
    compile 普通依赖,无依赖限制
    provided 只在编译时有效,不会参与打包
    apk 只在生成apk的时候参与打包,编译时不会参与
    test compile 只在单元测试代码的编译以及最终打包测试apk时有效
    debug compile debugCompile 只在debug模式的编译和最终的debug apk打包时有效
    release compile Release 模式的编译和最终的Release apk打包

    Android3.0之后

    名称 功能
    implementation 只有当前的module可以使用此依赖
    api 普通依赖,无依赖限制
    compile only 只在编译时有效,不会参与打包
    runtime only 只在生成apk的时候参与打包,编译时不会参与
    test implementation 只在单元测试代码的编译以及最终打包测试apk时有效
    debug implementation debugCompile 只在debug模式的编译和最终的debug apk打包时有效
    release implementation Release 模式的编译和最终的Release apk打包

    Compile和 Api:功能类似,这种引用的方式可能会引起,相同依赖包,由于版本不同,而导致打包的时候失败,这个时候可以对冲突的包进行版本同以解决,也可以通过implementation解决。

    Provided和Compile Only:在依赖一些定制系统或者和定制系统相关的包的时候可以使用这个策略,也可以在自己的module中使用该方式依赖一些比如com.android.support,gson这些使用者常用的库,避免冲突。

    这里重点说下比较坑的版本冲突,导致的打包报错:
    同样的配置下的版本冲突,会自动使用最新版;而不同配置下的版本冲突,gradle同步时会直接报错。可使用exclude、force解决冲突。

    implementation 'com.android.support:appcompat-v7:27.0.0'
    
    implementation 'com.android.support:appcompat-v7:28.0.0' 
    

    最后会使用28.0.0版本。

    但是如

    implementation 'com.android.support:appcompat-v7:23.1.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:2.1'
    

    所依赖的

    com.android.support:support-annotations
    

    版本不同,就会导致冲突。除了可以用exclude、force解决外,也可以自己统一为所有依赖指定support包的版本,不需要为每个依赖单独排除了:

    configurations.all {   
    
            resolutionStrategy.eachDependency { DependencyResolveDetails details ->       
                    def requested = details.requested       
                    if (requested.group == 'com.android.support') {           
                            if (!requested.name.startsWith("multidex")) {               
                                    details.useVersion '26.1.0'           
                                }       
                        }  
             }
    } 
    

    组件内的依赖请,尽量使用implementation。

    组件化第三步:组件间交互:

    组件间交互我认为可以分为两个方向,一个是通过路由进行跳转,这种利用框架可以很好的为我们解决问题;另外一种是方法调用,已登录模块为例,我需要得知当前的登录账号,或者程序掉线的时候控制是否重新登录等,这些功能性的交互。

    3.1Activtiy跳转Activity

    第一步定义PATH:

    public class ActivityPath {
        public static final String MAIN_PATH = "/main/";
    
        public static final String MainActivity=MAIN_PATH+"MainActivity";
    }
    

    第二步添加注解

    @Route(path = ActivityPath.MainActivity)
    public class MainActivity extends BaseActivity {
    }
    

    第三步跳转:

    //通过路径传值跳转
    ARouter.getInstance().build(ActivityPath.MainActivity)
                    .with(new Bundle())
                    .navigation();
    
    //根据类型跳转
    ARouter.getInstance().navigation(MainActivity.class);
    

    3.2实例化Fragment

    第一步定义PATH

    public class FragmentPath {
    
        public static final String SETTINVG_PATH = "/setting/";
        
       public static final String SettingFragment=SETTINVG_PATH+"SettingFragment";
    
    }
    

    第二步添加注解

    @Route(path = FragmentPath.SettingFragment)
    public class SettingFragment extends BaseFragment {
    }
    

    第三步实例化

    Fragment fragment = (Fragment) ARouter.getInstance().build(FragmentPath.SettingFragment).navigation();
    

    3.3IProvider暴露接口

    对于方法要求度不高的可以采用接口下沉的方式,具体实现参考《2.2组件内类的命名》的实现过程。

    组件化第四步:开放接口的处理

    在阐述接口交付这个概念之前先说两个例子:

    1.AIDL的实现。对于在一台手机上的两个应用如果想使用一方的服务,那么就需要拿到AIDL开放的接口和接口涉及的数据对象。

    2.服务端和客户端之间交互,客户端根据服务端告知的接口和参数去访问服务端,然后服务端返回数据,无论是xml还是json格式都是key&value的形式。

    通过上面两个例子,不难发现已经隔离的业务,要想进行交互,最小代价就是告知对方接口和数据对象。那么在组件化设计当中,我们必须要做的也是数据和接口的抽离。

    如果想让其他组件了解自己组件提供那些功能有两种解决方案,一种是将这些接口和数据对象进行下沉,下沉后,接口方法和数据对象彼此可见,在注册服务后,组件就可以使用彼此的功能了。这种形式是比较方便简单的,而且更新方法和数据对象之后,上面可以通过报错的形式被通知。不过他是存在缺点的。

    • 第一如果两个组件存在同名的类和方法时,容易造成引用的错误。
    • 第二如果组件是对安全性要求较高的,或者说这个模块,是不希望随便对其他部分可见的,这个时候显然下沉不是一个好的选择。
    • 第三如果未经过协商,组件间直接调用彼此的功能,对发生问题的排查也会造成困扰。
    • 第四所有组件的接口的下沉,一定程度上造成了底层的膨胀,这种显然也不利于通用层的维护。

    这里给出的解决方案是,将组件对外开放的功能和数据对象打成jar包,哪个组件需要,那么通过申请的形式获取这个jar包,那么对于开放方是知晓那些组件使用了自己的功能的,发生问题时,只需要在这几个组件里面排查就可以了。此时由于不在经过通用层,通用层膨胀的问题也解决了。

    另外有一些组件可能是所有组件都要使用的例如登录和设置,像这种就建议直接放到通用层中,减少不必要的工作量。

    组件化第五步:组件化的一些问题

    5.1切换androidX的问题

    引入组件化的项目,一般是存在了一段时间,随着版本的迭代发现现有的架构不在能很好的为项目开发服务了,才引入组件化架构,由于项目的创建时间可能略久,而gradle中的依赖由于一般不会改动,可能还停留在创建时的版本。而组件化的过程对现有项目来说是一个很好的重构过程,那么自然涉及到对依赖的更新,那么这里就说下关于切换AndroidX的步骤和一点注意事项。
    其实切换AndroidX是十分方便的,切换后也不会产生太多的问题。
    当前使用的Android Studio版本3.6.1
    项目build.gradle版本:

    classpath 'com.android.tools.build:gradle:3.6.1'
    

    项目gradle-wrapper.properties版本:

     distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
    

    切换第一步:
    在这里插入图片描述
    选择这个后,Studio会自动的将项目整体切换到AndroidX的依赖上去。切换后一般会报错。
    这里说两个我遇到的问题,第一个就是将项目中的moudel的:

    compileSdkVersion 28
    targetSdkVersion 28
    

    进行对齐,统一到28或者以上,不修改28以下的module可能在资源上出错。

    第二记得修改以下Fragment将之前的依赖切换到:

    import androidx.fragment.app.Fragment;
    

    5.2Module使用Butterknife的问题

    Butterknife作为快速实现View初始化的工具,可以很大的提高我们的开发效率。不过在Butterknife的发展过程中,存在着一段时间,其无法很好的在module层使用的问题,如果无法在module使用只能在应用层使用的话,其在组件化的开发中就失去了意义,所以此部分特别说一下,目前的解决和使用状态。

    首先不管现在使用的是什么版本的Butterknife,直接去Github上更新到最新的Butterknife。
    然后在项目build.gradle中更换成最的新插件

    classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1'
    

    对于使用的项目先接入插件:
    在使用Butterknife的组件中添加:

    apply plugin: 'com.jakewharton.butterknife'
    

    然后在依赖中接入依赖:

    implementation 'com.jakewharton:butterknife:10.2.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1'
    

    然后rebuild。
    对于应用层的module不会产生影响,这里说下组件层需要处理的事情。
    首先我们的module层会出现两个R文件的引用,一个.R一个是.R2。
    在这里插入图片描述
    简单来说涉及到android原生部分我们使用R.的资源文件。
    对于涉及到Butterknife的我们使用R2.的资源文件。
    其具体体现就是原生方法如:setContentView 使用R.的资源:

    setContentView(R.layout.activity);
    

    涉及到Butterknife的

    @BindView(R2.id.view)
    view view;
    @OnClick({R2.id.view}
    public void onViewClicked(View view) {
            switch (view.getId()) {
                case R2.id.view;
                    break;
           }
    }
    

    5.3Module使用GreenDao

    引入此三方主要的原因是,GreenDao是一种对于原生数据库和数据存取速度处理的折中方案。在对Android原生Sqlite的处理能力上,GreenDao无疑是非常优秀的,所以考虑兼容时,其还是项目首选。

    不过GreenDao也引入了编译时注解,所以在组件使用时,这里还是需要稍微提及一下,避免由于切换失败导致整体换数据库三方。(这事发生过,是十分🥚疼的)

    此处我们面临的场景是,一般只有数据存处理组件会设计到数据库的操作,比如联系人这个模块,其就高度依赖于数据库。

    那么我们对于联系人这个模块主要修改module的build.gradle:

    先引入插件:

    apply plugin: 'org.greenrobot.greendao'
    

    然后在(注意此处module间的版本需要统一!):

    android{
    	schemaVersion 1 //当前数据库版本,
    }
    

    最后添加依赖即可:

    implementation 'org.greenrobot:greendao:3.2.2'
    

    之后拆分数据库按照之前的模式拆分即可,rebuild之后数据Dao和DaoSession就都有了。

    5.4 数据对象

    这个模块主要是承接上面的问题,此时我们已经解决了GreenDao在module中使用的问题。不过新的问题出现了,前面说了只有数据提供方需要使用数据库三方,而不涉及到数据库操作的组件,显然是不应该引入的(引入会降低编译速度),那么此时引入GreenDao的组件其数据对象由于要进行数据库操作,会使用GreenDao提供的注解。而注解是不在不使用数据库的组件中出现的!显然,此时的数据对象是不能拿来直接给外部的。

    对于这种的解决方案是,将数据处理对象DBBean去掉注解后,在生成一个不包含注解的数据Bean,此数据对象和DBBean需要完全一致,用于数据库回存。然后由数据提供方增加工厂方法,实现DBBean<->Bean的互转。

    命名建议:

    数据处理对象更改为:

    com.dbbean.DBDataBean 
    

    方便其和通用数据Bean区别。

    5.5公用Adapter的问题

    问题:组件A自己有一个开发完毕的Adapter,这个时候为了用户方便需要在组件B中引入一个一样的Adapter。由于组件化,组件A的Adapter对组件B不可见,此时就产生了Adapter的复用问题。
    解决方案有下面几种:

    1. 组件A的Adapter下沉到CommonBase里面,这样对B可见。这在组件中有一个问题,这个Adapter如果下沉了,他应该有谁来维护?显然CommonBase是不合适的,但是组件A的开发人员也不应该有权限去开发CommonBase。显然这种并不是很合理。

    2. 将组件A的Adapter粘贴一份到组件B,这种也不太好,第一个原因是代码冗余,第二个原因是组件A如果更新了Adapter,就需要通知B去同步,如果增加了其他组件那么就要都通知一遍。这个感觉也不是很合理。

    3. 最后是我想到的解决方案,一般对于Adapter我们都会进行封装一个BaseAdapter做为父类,那么我们采用公共父类的形式声明Adapter,这样最大的保证了Adapter的功能,但是子类Adapter独有的其他功能却不可见了,此时Adapter变得不在有业务处理能力!只是单纯的显示帮助者。

    另外作为Adapter还需要处理点击事件,因为两个组件中的Adapter虽然显示一样,但是点击逻辑可能不同。显然如果Adapter由一个组件维护,那么这个组件对应的layout文件也应该在组件A中,同时对组件B不可见。这在处理点击时使用view.getId()的形式已经不能满足了,可能有些同学会想到,使用getTag()的形式来实现,这种实现方式的缺点是,这个Tag是你需要写到文档中告知对方的,Tag修改的时候同理。这样就有点像方法2那样的困境了,告诉一个和告诉两个的成本其实是一样的。

    此处就产生了新的问题如何解决点击事件?
    这里给出的解决方案是:
    给出接口,回调接口,在获取BaseAdapter的时候作为参数传进来。
    其在和组件A获取Adapter的时候是这样的:

    public BaseAdapter getCallAdapter(Context context,CallAdapterCallback callAdapterCallback);
    

    关于接口的定义如下:

    public interface CallAdapterCallback {
    
        public void onTermianlClick(int position, View view,CallListBean callListBean);
    
        public void onAcceptlick(int position, View view,CallListBean callListBean);
    
        public void onSwitchClick(int position, View view,CallListBean callListBean);
    }
    

    5.6如何进行组件化而不产生报错

    在组件化过程中,发现一个十分难受的问题,就是做一个组件的时候,由于这部分组件的代码其实对于之前说的App层代码是下沉的,这就会导致,资源,以及和对其他功能的引用报错,这个时候,不得不对另外一个功能进行组件化或者让其设计开放接口,在这个功能组件化的时候同样面临相同的问题,最后很可能把整个项目都组件化了,而这段期间,项目都是无法编译的,而当整体组件化完成的时候,就产生了大量的代码修改,这种时候一般会产生大量问题,不利于项目维护!
    这里给出的解决方案是,首先将APP层变成Library,然后建立设计好的组件,再在组件的上层建立一个壳。这样在我们开发组件的时候就是代码上移,这样就不会产生因为引用造成的问题了,随时都可以编译了。
    当然这种办法可能产生组件化划分的不完全,即可能存在遗漏的文件留在了下层。
    此时的解决方案是:当我们组件化基本完成的时候,我们用App覆盖掉壳,根据报错,在将遗漏的文件进行组件划分处理。

    5.7MainActivity的组件化

    其实一开始是没打算将MainActivity组件化的,开始时打算将MainActivity作为上层打包时的壳,那么他就可以拼装相关的Fragment了,还可以看见每个组件的方法,基本没有太的改动。不过考虑到,其实MainActivity模块也有可能承载很多的业务量,如自动登录,语言设置,当前网络状态回调,以及账号的登录状态等,你会发现其实MainActivity和很多模块有关联成了业务耦合的重灾区。由于耦合严重MainActivity往往代码量较大,不易于维护。

    另外对于自动登录这个功能来说,他应该由登录组件进行维护,对于MainActivity来说就不应该存在他的逻辑了。其他的功能同理。

    MainActivity组件化后,主要体现在需要增加了很多的接口,和修改某些回调为生命周期回调,此时可以很好的解决MainActivity的耦合问题。同时由于去掉了接口的直接回调处理,MainActivity会变小,也很难膨胀变大。

    5.8关于业务生命周期的开放接口

    先举个栗子🌰:
    业务场景如下,登录模块除了在登录页使用,一般也会在首页使用进行自动登录业务,那么此时很正常的就是,首页组件调用登录组件的登录功能。此时有一个要求,就是首页在登录成功前,不希望用户进行业务,需要加一个挡板,用于防止用户进行操作。

    显然挡板是发起业务方的业务,而生命周期是被调用方的事情。那么我们只需在登录组件中暴露他的声明周期接口就可以了:

    public interface LoginCallback {
    
        public void onLoginStart();
    
        public void onLoginSuccess();
    
        public void onLoginError(String errorMessage);
    
        public void onLoginEnd();
        
    }
    

    其实此处只是想提醒大家,在处理这种可能涉及到请求有回调的方法时,应该记得开放对应的周期接口,方便调用方完成业务。

    5.9多语言文件的处理

    组件化之后的多语言文件已经被打散分布在各个组件当中,那么当我们在开发末期,需要整体提交需要翻译的语言时就成为了一个问题,需要各组件进行整理,然后汇总,这是一个易出错且效率地下的方式。此处可以依靠插件化的方式去解决,即在编译时自动汇总组件内的strings。

    展开全文
  • 前端组件化基础知识

    千次阅读 多人点赞 2020-12-31 22:45:17
    这里我们一起来学习前端组件化的知识,而组件化在前端架构里面是最重要的一个部分。
  • 组件化开发系列文章 1. Android组件化开发之一:为什么要进行组件化开发 2. Android组件化开发之二:组件化架构 3. Android组件化开发之三:组件化开发手册 4. Android组件化开发之四:组件化填坑之旅 5. Android...
  • android组件化

    千次阅读 2021-01-27 16:03:20
    模块化是业务导向,组件化是功能导向。 模块化 模块化编程将程序按照功能拆分成相互独立的若干模块。
  • iOS开发组件化之组件的国际化处理

    千次阅读 2019-07-16 21:48:51
      在进行组件化开发的时候,有时候需要对业务组件进行国际化的配置操作,但是又不想把这些文件放到主app中,针对这种情况我们可以把组件化的国际化配置文件放到组件中,由相关的组件开发人员进行配置,这样稳定性...
  • 组件化开发系列文章 1. Android组件化开发之一:为什么要进行组件化开发 2. Android组件化开发之二:组件化架构 3. Android组件化开发之三:组件化开发手册 4. Android组件化开发之四:组件化填坑之旅 5. Android...
  • 关于组件化的文章很多,各方大神更是提出了各种的组件化方案,我也看了很多相关文章。但是学习新东西看的再多,不如动手做一次,先不考虑复杂的东西,先动手做个简单的Demo更有助于理解组件化的思想。组件化相关理论...
  • /*最外层透明*/ .el-table, .el-table__expanded-cell{ background-color: transparent; } /* 表格内背景颜色 */ ...组件化开发 less则需要加上/deep/生效,还有注意作用域要是scoped<style scoped&
  • 前端模块化、组件化的理解

    千次阅读 2019-02-19 23:02:51
    前端模块化、组件化的理解 到底什么是前端工程化、模块化、组件化 前端组件化思想 浅谈前端架构的工程化、模块化、组件化、规范化
  • Android组件化方案

    万次阅读 多人点赞 2017-02-15 19:01:52
    上图是组件化工程模型,为了方便理解这张架构图,下面会列举一些组件化工程中用到的名词的含义: 名词 含义 集成模式 所有的业务组件被“app壳工程”依赖,组成一个完整的APP; ...
  • 【Android 插件化】插件化简介 ( 组件化与插件化 )

    千次阅读 多人点赞 2021-05-29 21:27:36
    一、组件化与插件化、 二、插件化示例、 三、插件化标准引入、
  • 前端工程化、模块化、组件化

    千次阅读 多人点赞 2019-04-28 12:31:53
    而模块化和组件化是为工程化思想下相对较具体的开发方式,因此可以简单的认为模块化和组件化是工程化的表现形式。 前端工程化分为三个大阶段 技术选型 构建优化 模块化开发,组件化开发 模块化     ...
  • Android组件化和插件化的概念

    千次阅读 多人点赞 2020-11-10 15:43:17
    一、什么是组件化和插件化 组件化开发 就是将一个app分成多个模块,每个模块都是一个组件(Module),开发的过程中我们可以让这些组件相互依赖或者单独调试部分组件等,但是最终发布的时候是将这些组件合并统一...
  • Vue 组件化开发

    万次阅读 多人点赞 2018-12-20 15:50:21
    Vue 组件化开发 提示: 本次分享知识点基于 vue.js,需要对 vue.js 有一定的了解。 什么叫做组件化 vue.js 有两大法宝,一个是数据驱动,另一个就是组件化,那么问题来了,什么叫做组件化,为什么要组件化?接下来我...
  • 组件化开发系列文章 1. Android组件化开发之一:为什么要进行组件化开发 2. Android组件化开发之二:组件化架构 3. Android组件化开发之三:组件化开发手册 4. Android组件化开发之四:组件化填坑之旅 5. Android...
  • 组件化开发

    千次阅读 2018-09-11 17:36:47
    转自: ... Android组件化项目地址:Android组件化项目AndroidModulePattern Android组件化之终极方案地址:http://blog.csdn.net/guiying712/article/details/78057...
  • 组件化和插件化

    千次阅读 2018-03-08 14:30:55
    组件化开发:将一个app分成多个模块,可以对每个模块进行独立调试,打包时最终发布将所有的组件统一合并成一个apk。 插件化开发:将整个app拆分成多个模块,每个模块都是一个独立的apk(组件化每个模块是个libraily...
  • 组件化在业界已经炒的水深火热,关于组件化的好处和组件化的方案网上已经有大篇的文章了。笔者通过拆分一个现有的demo来简单聊一下项目实施组件化的过程(将分为上、中、下三篇)。demo可以从github下载(下载之后...
  • Android组件化之组件通信

    千次阅读 2018-11-20 23:37:28
    本文是续上一篇Android组件化方案实践与思考文章一些思考,主要是针对组件间通信,比如: 每个组件如何初始化各自的数据 Activity间如何跳转、Fragment实例获取、如何调用普通类的函数 如何在一个组件中通知...
  • Android 组件化总结

    千次阅读 2018-06-12 14:44:57
    本文我们先来说一下一下组件化组件化很早就有了,网上也有很多例子。讲的都非常好,我这里也只是把自己在实际使用的情况做一下记录。 新项目中用到了组件化开发的思想。为什么要用到组件化呢? 因为本来公司项目...
  • iOS组件化(一)-为何做组件化

    千次阅读 2018-07-26 14:22:15
    我们在做组件化之前,必须要弄清楚,我们为什么要组件化,如果没有明显的优点,或者解决我们的所需,我们没有必要组件化。在app迭代如此快速的情况下,耗费时间精力去做这么一件事情到底值不值得? 一、组件化所...
  • iOS应用组件化/模块化探究

    千次阅读 2019-01-09 14:48:35
    组件化是近几年流行起的概念,它是当代码扩张到一定程度时,所采取的一种代码组织架构策略。淘宝、蘑菇街等大厂也在近几年陆续完成了其代码组件化的过程。 提到组件化,给人的感觉似乎很高大上,很神秘的感觉。但是...
  • iOS组件化(二)-组件化前期工作

    千次阅读 2018-07-26 16:28:21
    上一次我们讲了为什么组件化,这次我们就开始组件化的前期工作。 一、Git库准备工作 独立工程git库:存放管理我们独立工程代码 spec git库:专门提交我们的私有库spec的git库 二、新建工程 既然我们要组件化...
  • AngularJs 组件化

    千次阅读 2017-04-27 20:36:12
    Angular 组件化 常规的网页开发中,页面 header、footer和nav通常都是固定不变的,如果一遍一遍的写就会增加工作量,现在angular的组件化可以很方便的解决这一问题,接下来我就为大家演示一下angular组件化的应用吧~...
  • angular 组件化

    万次阅读 2018-11-27 09:45:09
    在做sass产品页面的时候,往往每个页面的header和footer都是一样的...angular框架就支持这种组件化管理,不过也有优缺点,我先来说实现方法哈! index.html:没有用到路由,所以js都是src生引进来的   &lt;...
  • Android模块化、组件化、插件化区别

    千次阅读 2019-04-23 10:55:39
    组件化(lib)主要解决问题是功能拆分,强调单独编译 插件化(application)是所有组件都为apk的特殊组件化,特点可热更新 通讯方式不同点: 模块化相互引入,需要引入需要的module 组件化通讯方式分为隐式和...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 102,173
精华内容 40,869
关键字:

组件化