Android组件化

我理解的组件化、模块化、传统模式
  • 传统模式
    所有的功能点都放在同一个工程下
    传统模式的缺点:现在做了多个点,每个点的功能模块都不一样,比如说A地区需要视频会议,B地区需要第三方加密,这样就造成了我的所有的功能模块都放在同一个工程下,就算我打包的时候去屏蔽他们,也需要屏蔽多个地方的代码,效率低下,而且还容易造成遗漏,我不屏蔽的话,编译的时候效率极其低下,难受!而且不适合团队合作,业务耦合严重,容易引起代码提交冲突。

  • 组件化
    组件化就是将项目中的功能拆分出来,每个功能都单独做成一个Library,并且组件可以单独调试运行,组件与组件之间也必须达到完全隔离,不管去除掉哪一个组件都不会影响到整体的编译运行。
    组件化的优点:可以单一调试,快速编译,复用性高,随意拆解,如果针对那种需要对不同地区用不同功能打包的项目及其适用。

  • 模块化
    模块化就是将项目中的业务模块拆分出来,业务模块与业务模块中可能存在关联。
    模块化的优点:业务耦合性小,复用性高。

 我曾一度分不清组件化与模块化,因为他们太像了,我也纠结了老久,脑海里一直在被这两个绕着,我一直在想模块不能提供给别人调用么?模块我不能单独调试么?

 下面是我划分的一个图,希望能够更好的将划分出来,组件给业务模块提供服务,比如说首页我可以调用语音组件、可以调用OCR组件,一个模块可以调起很多组件,而模块注重的是业务内容的开发,组件注重的是功能开发。

在这里插入图片描述

组件化需要解决的问题
  • 动态配置工程
  • 跳转问题-模块跳转到组件、组件跳转到组件
  • 组件之间数据交互、组件与模块之间的数据交互
  • 组件的Application与AndroidManifest应该如何灵活配置
  • 如何实现组件与组件之间的完全隔离
动态配置工程
  • 组件的引用
  1. 在gradle.properties中去设定配置项
    在这里插入图片描述
  2. 在组件的build.gradle中根据配置项来控制当前是做成依赖库还是工程
   if (isSigningModule.toBoolean()) {
           apply plugin: 'com.android.library'
       } else {
           apply plugin: 'com.android.application'
       }
  1. 在组件中根据配置项控制ApplicationId的添加与否
    if (!isSigningModule.toBoolean()) {
           applicationId "com.example.signing"
       }
  1. 在需要依赖的module中根据配置项来控制是否依赖
   if (isSigningModule.toBoolean()){
           runtimeOnly project(':signingmodule')
       }
  • 组件的AndroidManifest的灵活配置
  1. 在组件的src/main下新建一个文件夹manifest,然后将Android Manifest文件拷过去,添加启动页,在src/main下面的Android Manifest就不要启动页了。
    在这里插入图片描述
跳转问题

 我们如果要做到能组件化的特性,普通的显示跳转肯定是不行了,我们可以使用隐式跳转。

		<activity android:name="com.ideal.signing.SigningActivity"
            android:theme="@style/Theme.AppCompat.NoActionBar">
           <intent-filter>
               <action android:name="signing.com.Signing"/>
               <category android:name="android.intent.category.DEFAULT"/>
           </intent-filter>
        </activity>
Intent intent = new Intent("signing.com.Signing");
startActivity(intent);

还有另一种隐式跳转的方式

Intent intent = new Intent();
intent.setClassName("APP包名","Activity路径");
intent.setComponent(new ComponentName("APP包名", "Activity路径"));
if (intent.resolveActivity(getPackageManager()) != null) {//验证是否会接收Intent
    startActivity(intent);
}

 第一种方法由于配置了intent-filter ,exported的值会变为true,所以我们如果不希望别的APP通过隐式跳转调用到我们的APP,我们可以将exported的值设置为false,在activity节点下配置。

exported在Activity中表示当前Activity是否可以被另一个Application的组件启动,true允许,false不允许,当设置了intent-filter时exported得值默认为true,如果没有设置intent-filter时默认为false。

以上两种是通过隐式跳转的,当然我们也有其他更好更灵活的方式来进行页面跳转,比如阿里的ARouter,这里我下一篇文章会介绍。

ARouter地址: https://github.com/alibaba/ARouter

组件之间的数据交互

 组件之间由于要完全隔离,所以我们没办法去直接进行数据交互,但是我们可以通过BaseLibrary、EventBus,RxBus、本地持久化存储、数据库、广播等方式来进行数据交互。
在这里插入图片描述

 这几种方式我们都需要将我们需要进行交互的数据实体放到基础框架这一层,就是我们的BaseLibrary,但是我觉得真正的组件化是不应该去依靠BaseLibrary来实现通信的,我们的组件应该尽可能的减少对其他的模块的依赖,要达到这种效果,我们可以考虑使用一些组件通信框架,比如ModularizationArchitecture

如果要使用EventBus建议采用3.0,3.0之后的是先编译后运行,所以包会大一些,但是效率高一些,3.0之前的是运行时编译,效率慢一些。

Application与AndroidManifest的处理

 组件中的Application我们应该怎么在主Module中调用才不会让耦合性这么高呢?这里我们要先提及一下AndroidManifest的合并规则,当主Module引用组件时,会合并那些主Module中没有的,比如说主Module中没有读写权限,但是组件中有,那么打出来的包中,清单文件就会存在这个读写权限,同样的,主题,四大组件的注册,都会被合并进来。

 以上我们所说的都是主module中没有,但是组件中有的,都会被合并,那如果组件与主Module中都同时存在呢?那该咋办呢?比如同样的主题,同样的icon(同样的Activity),我们可以尝试编译一下,会发现报错了,会提示让我们使用tools:replace=""来声明Application是被可替代的。

使用方法:
tools:replace=“android:theme,android:label,android:name,android:icon”

 一般来说我们考虑主Module中会有自定义的Application,组件中也有自定义的Application,这样我们需要在主Module中添加上replace即可解决冲突。

 AndroidManifest的合并说完了,那么就是Application的解耦处理了,我们可以采用反射来调用组件中的Application,采用这种方法组件中就不需要再去注册Application了。

  1. BaseLibrary中
package com.ideal.baselibrary.base;

import android.app.Application;

public interface BaseInitApplication {
    void initBefore(Application application);
    void initRear(Application application);
}
package com.ideal.baselibrary.base;

public class BaseInitConstans {
    public static String[] initPackName={
      "com.ideal.signing"
    };
}
  1. 组件中
package com.ideal.signing;

import android.app.Application;
import com.ideal.baselibrary.base.BaseInitApplication;

public class SigningInitApplication implements BaseInitApplication {
    @Override
    public void initBefore(Application application) {
        //自己的操作,较前一个
    }

    @Override
    public void initRear(Application application) {
        //自己的操作,较后一点的
    }
}
  1. 主Module,通过反射获取组件中建立的类,进行初始化
package com.ideal.cmp;

import android.app.Application;
import com.ideal.baselibrary.base.BaseInitApplication;
import com.ideal.baselibrary.base.BaseInitConstans;

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化组件中的Application
        initModuleBefore();
        initModuleRear();
    }

    private void initModuleBefore() {
        for (String moduleApp : BaseInitConstans.initPackName) {
            try {
                Class<?> clazz = Class.forName(moduleApp);
                BaseInitApplication baseApp = (BaseInitApplication) clazz.newInstance();
                baseApp.initBefore(this);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }

    private void initModuleRear(){
        for (String moduleApp : BaseInitConstans.initPackName) {
            try {
                Class<?> clazz = Class.forName(moduleApp);
                BaseInitApplication baseApp = (BaseInitApplication) clazz.newInstance();
                baseApp.initRear(this);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
    }
}

 同样的道理我们也可以用在获取Fragment中,比如我们当前的主Module想要引用组件中的一个Fragment,但是又不想通过代码直接去调用,那也可以采用这种反射的方式。

如何尽量的将组件解耦
  • 资源隔离

 我们在设计组件架构的时候,尽量的在每个组件中的资源文件名、类名、主题名、颜色名称等等一些需要命名的地方都加上标示,比如:我当前是一个Signing组件,那么我的所有的资源文件都用signing_开头,signing_more_bg.xml;signing_main_layout.xml;然后每个组件都是用resourcePrefix来约束命名。

resourcePrefix只能用来约束res中的xml,其他的文件命名,我们需要自己定义好,定义清晰,就算在其他能被引用的地方被引用的时候也能清楚知道这是哪的,别乱引用。

  • 代码隔离

 代码隔离这里采用runtimeOnly来实现,这样我们在编译的时候就不会乱引用到组件中的类。

组件化权限应该如何去处理

 权限分为两类,一类是普通权限,一类是铭感权限,在6.0之后铭感权限都需要动态申请了,所以我们可以将一些普通权限都在放BaseLibrary,铭感权限也通过Base library建立权限工具类来去动态请求。

 如果我们使用了Arouter,还可以将权限的请求放在跳转拦截中进行。

收个尾

 Android组件化还是很有多需要学习的,这里我记录的肯定不全(记录的是已经掌握的),后续如果还有不同的见解,或者其他的点子也会重新编辑,持续更新,如果大家有更好的想法,更好的一些见解也欢迎指点指点。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值