说说cc组件化

73 阅读7分钟

这个的话,我就结合一下组件化的特性,说说cc时如何实现组件化的可以吧。

1,组件化的第一个特性是解偶 我们把业务组件或者基础组件放到单独的module,本身组件之间是不会耦合的,但是app模块需要依赖所有的模块,如果使用implementation的话,就直接依赖了对应组件,在app模块中可以直接调用对应模块的类或者方法。这时的组件化就没有完成隔离。

cc提供了addComponent dependencymodule 方法,只有当前task是在给当前module集成打包的时候才会使用implementation去依赖dependencymodule(dependencymodule没有在localProperties文件中声明排除),保证了组件化完全隔离。

这里有一个问题是如何判断当前task是在给当前module集成打包? (在gradle文件中)读取当前task,如果当前任务是ASSEMBLE|(BUILD)|(INSTALL)中的一种,并且前缀是当前module名,则是为当前module打包。

2,组件化的第二个特性是需要组件module在application和library之间的自动切换 因为,一方面我们希望每一个组件直接点运行按钮,就能独立打包运行,这个时候需要给它应用application插件, 另一方面的化,比如说有两个module,当moduleA依赖moduleB,给moduleA打包时, moduleB自动变成library被依赖。

如果是手动切换的话,比较繁琐。这个时候就需要自动切换

cc会给所有的module应用一个插件,在这个插件里面,可以判断当前执行的task:

如果当前task是对当前module集成打包或者当前task不是集成打包的task,则应用application插件,否则应用library插件 这样就保证了 1、 如果当前task是集成打包的task,并且不是为该module打包,那么该module是应用的library插件,可以被依赖; 2、如果当前task不是集成打包的task,module默认都是应用的application插件,可以直接运行 )

另外,它还会根据module应用的是application插件还是library插件,使用sourceSets来给module应用不同的menifest文件。如果是application的话,还会把debug目录的代码拷贝到正式目录去,这些debug代码就可以进行编译了。

3,组件化的第三个特性是组件之间的通信。 这里设计到三种情况 第一种是单进程中组件之间进行通信。 第二种是多进程中组件之间进行通信。 第三种是组件跨app中进行通信。

1,概念 先说一下cc中组件的概念,cc中有一个icomponent的接口,里面有一个oncall方法,实现这个接口的类就是一个组件,在oncall方法中可以根据功能名称去实现整个module的功能分发。

2,总体

cc中有一个组件管理类,在这个类中维护了一个map,记录的是组件名称和组件对象的映射。

3,具体(必须举例子) 第一种是单进程中组件之间进行通信,如果组件a调用组件b,首先要构建一个cc对象,参数包括被调用组件的名称,功能名称(功能名称是要调用组件的那一个功能),然后去组件管理类的map中根据组件名称查找到组件对象,然后调用这个组件对象的call方法,在call方法中根据功能名称去调用组件对应功能。

1,先说通信原理 然后是多进程组件之间的调用

cc跨进程调用是采用的contenprovider+aidl。

2,前提 首先一个前提是cc会给所有的进程都种入一个contenprovider,并且它对应的uri就是它的进程名。

3,具体(必须举例) 比如说进程a组件a要调用组件b时,首先查询到b组件要运行在哪个进程 (比如说它要运行在进程b),那么就可以通过b进程中的contenprovider获取到的一个binder对象,

这里要解释下,首先这个contenprovider的进程名称是b,然后它对应的uri也是b,所以通过这个uri可以开启b进程的contenprovider,获取到的binder对应也是b进程,通过这个binder调用b组件,b组件也就运行在b进程了。

获取到binder对象之后,就可以通过这个binder对象的call方法发起对组件b的调用,参数有两个,第一个是cc对象,还是包括了组件名和功能名,第二个参数是个binder类型的callback对象,然后在b进程中的binder对象的call方法中响应调用,根据传递过来的cc对象,构建出一个本地的cc调用流程对组件b进行调用,然后将对应的返回值通过callback传递给调用方。

4,问题1 有一个问题:

1,如何查询组件要运行在哪个进程。

其实组件要运行在哪个进程,是我们自己通过注解去定义的,然后在编译的时候,就可以通过asm扫描到组件名称和它对应的进程名称,然后把它记录到管理类的另外一个map中。

情况1:同一个app中,查询这个map,就可以获取组件对应的进程名称。

情况2:跨app调用时,比如说 a app 中组件a b app中的组件b,a app中组件名称对于进程名称的map中 是没有b组件的 消息的,a app都没有b组件的代码,asm肯定没法收集到b组件对应的进程名称的。

跨app调用时,获取组件对应进程名称的具体过程是:

1、app启动时,获取到手机中所有应用了cc框架的app,以及它对应的包名(这个包名即主进程名) 2、通过主进程中的ContentProvider获取这些app主进程中的binder对象。 3、通过主进程的binder对象去查询对应app中,组件名称对应进程名称的map,就可以获取组件对应的进程名。

5,问题2 如何知道哪些app应用了cc框架,并且获取其包名? 在cc框架中内置一个Activity,并给其指定一个intent-filter, 使用PackageManager去查询这个activity对应的activityinfo,继而通过这个ActivityInfo获取到对应packageName。

另外会通过广播监听app的安装,可直接获取app对应的包名,通过判断是否能启动内置一个Activity,如果是就是,应用了cc框架。


组件注册什么时候注册, 调用方进程,在第一次发起调用的时候,注册需要运行在本进程中所有组件, asm注册组件的代码被插入到组件管理类的static代码块中,所有调用统一入口在组件管理类中,第一次调用初始化组件管理类,注册所有组件

被调用方进程,也是第一次本地调用时,注册需要运行在本进程中的所有组件对象

记录组件对象的时候有进程判断,记录进程名称的时候没有进程判断。

怎么知道时本地调用还是跨进程调用, 1,查询组件对应的进程名称 2,判断组件对象的map中有没有这个组件对象

同步实现,直接调用方法,cc设置结果之后可以直接获取 异步实现,返回一个true,检测到true时,线程wait,等组件处理完成后,设置结果,然后唤醒对呀线程,对应线程取结果。