为了将CRM组件独立编译,我们都做了什么

2018-12-10  本文已影响0人  最喜欢小菠萝

使用方法

I 准备

1.1 组件化

简单来说,就是将一个APP的业务功能进行拆分,每一个功能都是一个单独的工程,每个工程都能独立运行,且只包含自己的业务,我们姑且叫这个独立的功能为一个组件服务,最后整个APP由多个拆分出的组件集成而成。

1.2 组件化解决了哪些痛点

  • 提高工程编译速度
    进行组件化拆分后,每个业务或者功能都是一个单独的工程,这个单独的工程可以独立编译运行.。
  • 业务模块解耦
    业务组件之间不能相互引用,每个组件都把对应的业务功能收敛在一个工程里,彼此互不打扰。
  • 提供单独的组件服务

1.2 框架CC

II 独立编译CRM,都做了哪些工作?

2.1 应用初始化

2.2 登录信息

2.2.1 Android:sharedUserId = " "属性

在需要共享资源的项目的每个AndroidMainfest.xml中添加shareuserId的标签。
Android给每个APK进程分配一个单独的空间,manifest中的userid就是对应一个分配的Linux用户ID,并且为它创建一个沙箱,以防止影响其他应用程序(或者其他应用程序影响它)。用户ID 在应用程序安装到设备中时被分配,并且在这个设备中保持它的永久性。

通常,不同的APK会具有不同的userId,因此运行时属于不同的进程中,而不同进程中的资源是不共享的,保障了程序运行的稳定。
通过SharedUserId,拥有相同UserId的多个Apk可以配置成运行在同一个进程中.所以默认就是可以互相访问任意数据.

但是
因为sharedUserId属性的添加引出的问题:sharedUserId属性不同的应用不能覆盖安装
所以采用gradle修改清单文件的方式在App整体编译时(local文件中没有设置moduleName=true时)移除sharedUserId属性
清单文件合并后的地址:
fs-android\host\host_main\build\intermediates\manifests\full\online\debug\AndroidManifest.xml

2.3 如何独立运行crm

注意 : 框架在接收到组件调用请求时,优先查看当前App内是否有本次CC调用指定的组件(参照3.5.1)

  • 有: 执行App内部组件调用流程
  • 没有:检查当前app是否开启跨app调用功能:
    • 未开启: 返回-5的状态码,代表未找到指定的组件
    • 已开启: 执行跨App组件调用流程

III 如何使用ComponentCaller建立一个新的组件

简而言之:我们通过CC将业务请求转发,在被调用的组件中处理请求

eg:在CRM组件中调用BI的页面 如:数据看板

  • 步骤1:添加引用
  • 步骤2:在CRM组件中原来调用startActivity(或发送请求的地方 对应类为BIGoPageImpl)进行调用
  • 步骤3:在plugs/bi组件中实现一个继承自IComponent接口的类用来处理调用请求 (实际的context.startActivity(intent)也就是跳转工作在onCall中进行)
  • 步骤4:在主app module中添加对所有组件module的依赖

基本用法

下面介绍在Android Studio中进行集成的详细步骤

3.1.2 在每个组件module(包括主app)的build.gradle中添加对CC框架的依赖并应用CC自动注册插件

 // apply plugin: 'com.android.library' 注释掉

添加 : apply from: rootProject.file(cc-settings-2.gradle)
//注意:最好放在build.gradle中代码的第一行

3.1.3 修改组件module的build.gradle,将applicationId去除或者按以下方式修改,否则在集成打包时会报错

android {
   defaultConfig {
       //仅在以application方式编译时才添加applicationId属性
       if (project.ext.runAsApp) { 
           applicationId 'com.facishare.fs'
       }
       //...
   }
   //...
}

3.2. 在组件中创建实现IComponent接口的组件操作类(以CRM组件跳转BI页面为例)

public class BIHomeActivityComponent implements IComponent {

    /**
     * BI报表详情页参数
     */
    public static final String IS_FROM_DATA_CONTAINER = "getFromDataContainer";
    public static final String VIEW_URL_KEY_IN_DATA_CONTAINER = "RptDetailWebAct_viewUrl";

    @Override
    public String getName() {
        //此处return值为自己定义的标识符 确保与调用处CC.obtainBuilder("Component.BIActivityComponent")中的值保持一致
        return "Component.BIActivityComponent";
    }

    @Override
    public boolean onCall(CC cc) {
        //actionName:构造CC对象时调用的setActionName 用于区分执行操作,非必需
        String actionName = cc.getActionName();
        switch (actionName) {
            case "BIHomeActivity":
                showBIHomeActivity(cc);
                break;
            case "DataBoardHomeAct":
                showDataBoardHomeAct(cc);
                break;
            case "RptDetailWebAct":
                showRptDetailWebAct(cc);
                break;
            //要确保任何分支case都有sendCCresult的调用!!
            //下方示例:
            default:
                CC.sendCCResult(cc.getCallId(), CCResult.error("unsupported action name:" + actionName));
                break;
        }
        //返回值说明
        // false: 组件同步实现(onCall方法执行完之前会将执行结果CCResult发送给CC)
        // true: 组件异步实现(onCall方法执行完之后再将CCResult发送给CC,CC会持续等待组件调用CC.sendCCResult发送的结果,直至超时)
        return false;
    }

    private void showBIHomeActivity(CC cc) {
    
        Context context = cc.getContext();
        Intent intent = new Intent(context, BIHomeActivity.class);
        if (!(context instanceof Activity)) {
            //调用方没有设置context或app间组件跳转,context为application
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        //实际的Activity跳转操作
        context.startActivity(intent);
        //发送组件调用的结果(返回结果)
        CC.sendCCResult(cc.getCallId(), CCResult.success());
    }

3.3. 调用组件

public class BIGoPageImpl implements IBIGoPage {

    /**
     * BI报表详情页参数
     */
    public static final String IS_FROM_DATA_CONTAINER = "getFromDataContainer";
    public static final String VIEW_URL_KEY_IN_DATA_CONTAINER = "RptDetailWebAct_viewUrl";

    @Override
    public void go2BIHome(Activity act) {
    //在构建CC时可以通过addParam(String key, Object value)或addParams(Map<String, Object> params)携带参数
        CCResult result1 = 
        CC.obtainBuilder("Component.BIActivityComponent")//根据步骤2中getName方法的返回值确定Icomponent
             .setActionName("BIHomeActivity")
             .build()
             .call();
    }

    @Override
    public void go2BIDataBoard(Activity act) {
        CCResult result2 = CC.obtainBuilder("Component.BIActivityComponent")
                .setActionName("DataBoardHomeAct")
                .build()
                .call();
    }

    @Override
    public void go2BIRptDetailWebAct(Activity act, String categoryID, String loadUrl) {
        //url可能很大,超过intent data size 限制
        CCResult result3 = CC.obtainBuilder("Component.BIActivityComponent")
                .setActionName("RptDetailWebAct")
                .addParam("loadUrl", loadUrl)
                .build()
                .call();
    }
}

//所有的请求api
//同步调用,直接返回结果
CCResult result = CC.obtainBuilder("ComponentA").build().call();
//或 异步调用,不需要回调结果
String callId = CC.obtainBuilder("ComponentA").build().callAsync();
//或 异步调用,在子线程执行回调
String callId = CC.obtainBuilder("ComponentA").build().callAsync(new IComponentCallback(){...});
//或 异步调用,在主线程执行回调
String callId = CC.obtainBuilder("ComponentA").build().callAsyncCallbackOnMainThread(new IComponentCallback(){...});

3.4. 在主app module中按如下方式添加对所有组件module的依赖

注意:组件之间不要互相依赖

ext.mainApp = true
apply from: rootProject.file(cc-settings-2.gradle)

//...

dependencies {
    addComponent 'demo_component_a' //会默认添加依赖:project(':demo_component_a')
    addComponent 'demo_component_kt', project(':demo_component_kt') //module方式
    addComponent 'demo_component_b', 'com.billy.demo:demo_b:1.1.0'  //maven方式
}

3.5 注意

3.5.1. CC会优先调用app内部的组件,只有在内部找不到对应组件且设置了CC.enableRemoteCC(true)时才会尝试进行跨app组件调用。
所以,单组件以app运行调试时,如果主app要主动与此组件进行通信,要确保主app中没有包含此组件,做法为:
在工程根目录的local.properties中添加如下配置,并重新打包运行主app

module_name=true #module_name为具体每个module的名称,设置为true代表以application方式编译调试,从主app中排除
示例:crm=true

3.5.2. 组件module可以直接点击android studio上的绿色Run按钮将组件作为app安装到手机上运行进行调试,可通过跨app调用组件的方式调用到主app内的所有组件

排除错误

你定义了AndroidManifest.xml了吗:CC的流程没跑通。常见错误:CC的接口不正确&&相应的组件模块没有applyCC-settings 尝试依照CC的日志进行错误排查]

2

空指针:将写在HostInterfaceManager的方法补充在HostFunction中 并尝试将其替换成HostFunction

CC.enableDebug(true);  //普通调试日志,会提示一些错误信息
CC.enableVerboseLog(true);  //组件调用的详细过程日志,用于跟踪整个调用过程

状态码清单

状态码 说明
0 CC调用成功
1 CC调用成功,但业务逻辑判定为失败
-1 保留状态码:默认的请求错误code
-2 没有指定组件名称 //考虑步骤4 留意编译过程中的log cc-register是否检测到实现IComponent接口的类
-3 result不该为null。例如:组件回调时使用 CC.sendCCResult(callId, null) 或 interceptor返回null
-4 调用过程中出现exception //请查看logcat
-5 指定的ComponentName没有找到 //请检查obtainBuilder中传递的name与待调用组件中的GetName返回值是否相同
-6 context为null,获取application失败,出现这种情况可以用CC.init(application)来初始化
-8 已取消
-9 已超时
-10 component.onCall(cc) return false, 未调用CC.sendCCResult(callId, ccResult)方法
-11 跨app组件调用时对象传输出错,可能是自定义类型没有共用,//不能同时调用多个addParam

常用操作

上一篇 下一篇

猜你喜欢

热点阅读