轻量组件化方案

清量组件化方案一代目

参考得到的方案,做了部分修改
项目地址

一. 组件化我们要实现什么:

1.各模块可以单独运行


image.png

2.任意模块联合调试:


联合调试

3.代码隔离,看上图,不管单独运行还是联合调试,每个模块只引用一个一个项目:ycbaselib,,依赖通过配置文件来管理,在整个开发周期中都不用compile其他module,所以各个模块之间看不到其他模块的代码,各个模块交互通过路由协议ycbaselib中的module服务管理

二.怎么用

问:怎么单独运行模块

答:采用此框架后,如下图所示每个模块可以单独运行


image.png
问:如何挂载其他模块运行

答:挂载其他module只需要在当前module根目录的gradle.properties里加下配置debugComponent=ycpublishlib,yccarlib,而不需要在build.gradle中手动添加,在编译时会根据配置帮你添加依赖,这样就做到了各个模块的代码隔离。

问:每个模块需要在Application里做初始化等一些操作,如何做到?

答:在ycbaselib里定义了一个接口IApplicationLike,定义了一些类似Application的方法,该接口定义如下:

public interface IApplicationLike {
   void onCreate(Application application);  //初始化
   void exitApp();          //退出app
   void onTrimMemory(int level);    //内存等级
}

每个模块可以写一个实现了该接口的类,在对应的方法下做相应的操作。在编译期会使用AOP的方式,在真正的Application类里插入这些代码的调用。

问:依赖其他module有几种方式

答:有两种依赖方式,compile project或者compile aar,根据配置,假如为:debugComponent=ycpublishlib则会以compile project的方式添加ycpublishlib依赖,如果是debugComponent=aar:ycpublishlib则会以compile aar的方式添加ycpublishlib依赖,当然前提是你依赖的模块要发布过aar文件。

三.ycbaselib放什么

  1. 公共的第三方库,注意:是所有模块都要用到的,比如:网络框架、数据库、图片加载、Rxjava,所有业务相关的第三方库都放各自模块
  2. 公共res资源:图片、颜色等
  3. 服务管理,各个模块需要对外提供的服务会放此处:
    image.png

此处只定义各个服务接口,具体的服务实现放在各个业务的模块下。

ServiceHost为服务管理商,有两个方法:获取服务和注册服务。

//获取服务
public static<T>  T getService(Class<T> clazz);
//注册服务
public static<T>  void addService(Class<T> clazz, T instance);  

比如我想获取用户模块的某些东西我先从ServiceHost获取用户模块的服务类:

IUserService service = ServiceHost.getService(IUserService.class);
service.getUserName();

拿到用户服务类,就可以使用用户模块提供的的功能了,注意:假如当前模块没有挂载用户模块, 此处获取到的用户服务为空,要做好判空处理

备注:ycbaselib应该轻量,不含具体业务,只放所有模块都要用到的东西。

四.原理

假设:运行A模块,A模块挂载了B模块和C模块

  1. 根据编译命令,如ycpublishlib:assembleRelease找到当前运行模块也就是A模块,将A模块设为apply:application 其他挂载模块B、C设为apply:library
  2. 设置SourceSet,每个模块单独运行时和作为library时可能代码和res资源略有不同,此处根据运行模块(A)和挂载模块(B、C)对SourceSet做不同配置
  3. 根据A模块里的配置文件(放在gradle.properties里),在编译时添加B、C模块依赖 ,类似于动态在buildgradle里添加compile project(':Bproject')。。。
  4. 编译结束后,遍历所有的class文件,开始字节码插入功能,根据A模块配置文件里的Application类名全称,找到A模块的Application类,然后根据根项目配置里的applikename类名的全称,找到所有实现了IApplicationLike接口的类,然后在Application里挨个调用IApplicationLike对象的相关方法实现其他挂载模块的初始化工做等。

五.怎么做

配置:

1.在项目根目录的gradle.properties里添加如下配置:

mainmodulename=app    //主项目是哪个module
applikename = com.yiche.ycbaselib.component.IApplicationLike    //IApplicationLike接口全名

2.在每个模块下添加gradle.properties文件,然后添加如下配置:

isRunAlone=true
applicationName = 'com.yiche.circles.CirclesApplication'  //该模块单独运行时配置的Application全名
debugComponent=ycpublishlib    //debug依赖
compileComponent=ycpublishlib  //release依赖

3.在项目根目录build.gradle里:

buildscript {
    repositories {
        //...略...
         jcenter()
    }
    dependencies {
        clclasspath 'com.yiche.litecomponent:ycbuild-gradle:1.0.3'
         //...略...
    }
}

allprojects {
    repositories {
        flatDir {
            dirs '../release_aars' //本地依赖aar时指定目录
        }
    }
    //...略...
}

4.在每个模块的build.gradle里:

apply plugin: 'com.yiche.litecomponent'//注意这里,不再是android.application或者library之类
//...略...

5.代码部分,在除主模块以外其他每个模块main目录下新建runalone目录
这个是单独运行时使用的,可以只放置一个AndroidManifest.xml,用来配置application信息以及启动的Activity等信息,也可以放java目录和res目录用于存放代码和资源文件,运行时会将runalone里的代码和res和main目录下的合并。目录结构如下图所示。

添加runalone目录

[参考]以下是插件里的部分源码,展示合并SourceSet,PS:AndroidManifest不能合并,单独运行和作为library时各自用各自的


参考参考

f每个模块写一个类,实现ycbaselib模块下的IApplicationLike接口,用于模仿Application该接口定义如下:

public interface IApplicationLike {
    void onCreate(Application application);
    void exitApp();
    void onTrimMemory(int level);
}

类似于Application的功能,在这里根据需求添加模块的初始化、或者退出app时的操作等。这里的代码会在编译时动态插入。
(完毕)

配置说明:

1.根目录的gradle.properties

mainmodulename:标记哪个模块是主项目,一般都是app,设置这个是为了当直接输入assembleRelease等构建命令的时候知道哪个是主项目入口。

ps:编译某个moudle的命令是gradlew modulename:assembleRelease这样。咱们一般敲assembleRelease相当于app:assembleRelease

applikename :IApplicationLike是一个接口,模仿Application的功能,一般放在base包下(比如本项目的ycbaselib包下)。为什么要放这个全名呢,因为在编译时要动态添加代码,会根据这个全名找到所有实现了该接口的类,然后在Application里动态插入代码。

问:如何实现模拟Application功能?
答:当编译完成后,本框架会通过字节码插入的方式,遍历所有的class文件找到所有实现了IApplicationLike接口的对象,然后在真正的Application下挨个插入调用代码。

2.模块目录的gradle.properties

isRunAlone:一般为true,只有当本模块需要发布library包也就是打aar包的的时候改为false。

applicationName :本模块单独运行时指定的Application类全名。每个模块都必须定义一个Application,因为每个模块都可能挂载其他模块联合运行,其他模块有可能需要在Application里做一些操作。所以就算本模块不需要使用Application也需要定义一个Application类。

问:为什么需要在配置里加applicationName呢?
答:在编译期间需要动态添加代码,根据applicationName找到真正的Application类,然后再找到所有实现了IApplicationLike接口的类,然后在Application类的对应方法里添加所有IApplicationLike实现类的调用代码。

PS:applicationName是强制需要指定的。你定义的Application可以不写任何代码如下图,这样也不影响注入代码的:

//记得在runalone目录下的AndroidManifest里注册
public class CarApplication extends Application{
  //我是空的
}

debugComponent、compileComponent:本模块debug依赖以及realease依赖。用于配置联合其他模块调试时的依赖,会在编译期间根据这个配置帮你引用其他项目。

依赖有两种方式,比如debugComponent:ycuserlib,aar:yccarlib表示依赖ycuserlib和yccarlib两个模块,前者是直接直接compile ycuserlib的projrct,后者是compile yccarlib发布的aar文件。当某个模块配置isRunAlone为false会打一个aar包并拷贝到根目录里的release_aars文件夹,当使用aar:ycxxlib依赖时,会到这个目录下找对应的aar文件并添加依赖。

其他:

ycuserblib提供了一个无侵入初始化的方案,通过ContentProvider来实现,原理参考使用ContentProvider初始化你的Library,可以不用在Application的onCreate里加代码来时现模块的初始化。

主module的不同之处:

  1. 主module的isRunalone永远为true,主module不会作为library使用
  2. 其他模块不能依赖主module。
  3. 主module不需要设置runalone目录,它不会作为library使用,一直是application模式
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356