ARouter是阿里在github上面的一个开源项目,地址是:ARouter 本文不是重点分享ARouter的使用,而是分享使用ARouter如何去组件化。关于它的详细使用,你们能够看文档以及加群向询问。关于如何编写一个路由实现组件化,推荐看我同事的一篇文章手把手教你写Router框架入门篇java
组件化的优势android
解耦,使得各自业务模块专一于本身的业务实现,而能够不关系别的模块业务。git
方便开发,在多人开发的时候,能够各自开发本身的特定模块,除了底层模块以外github
可配置,复用性强,针对不一样的App,能够有不一样的模块而没必要作出大的改变。网络
每一个模块能够独立运行,方便开发调试。数据结构
你们补充
组件化开发的实现app
![图片上传中...]框架
项目结构是: maven
配置ide
你们按照ARouter的文档去配置一些东西,须要注意的是每个模块都必须引入compile sdk,处理API SDk能够在Base sdk里面引入就行。
Annotation sdk不用咱们引入,他会本身自动依赖引入
在开发的时候,每个module 的分组都必须不一样,或者将会有一个 类重复的错误。分组就是path的第一个"/"与第二个"/"之间。
思路
咱们是须要拆分组件,不一样的模块负责本身的业务实现,不和其余模块有依赖关系的存在。假如这个模块须要启动别的Activity或者是调用别的模块方法,咱们就经过ARouter提供的方法去实现。
模块图以下:
App模块 App模块就是咱们的Apk模块,他是咱们的最终产物。他能够按照须要配置本身须要的模块,进行一些初始化的工做。
module模块 Module模块就是咱们的业务实现模块,他是只负责本module的业务而不负责其余的业务,他是经过使用ARouter提供的方法和接口,实现调用外部数据和提供方法给外部调用,也就是图里面的Provider。
base模块 关于base模块,你们也能够继续拆分一些细的模块,好比把router独立处理,可是不建议拆分太多,由于base模块是会变更较大,就是在版本的迭代过程当中,不断变化的,被拆分较多的话开发起来不是很方便。由于有依赖的传递的关系,好比base 依赖于本project的较多模块,当设计到底层较多模块修改,那么就须要一层一层的在传递上去。 base模块应该是与业务无关的模块,在本例子中,base模块负责管理Router,同时提供一些基础的东西,好比BaseActivit,Util资源等。假如咱们有自家的sdk,网络库依赖等,建议是在base中引入。
实践
管理Router
咱们使用一个ModuleManager,提供一个Map,Map是使用对应module的Provider的path做为key和value。以及相关的数据结构,用来对ARouter的进行二次封装和管理模块。 大概实现以下:
public class ModuleManager { private ModuleOptions options; private ModuleManager() { } private static class ModuleManagerHolder { private static final ModuleManager instance = new ModuleManager(); } public static ModuleManager getInstance() { return ModuleManagerHolder.instance; } public void init(ModuleOptions options) { if (this.options == null && options != null) { this.options = options; } } public ModuleOptions getOptions() { return options; } public boolean hasModule(String key) { return options.hasModule(key); } }
ModuleOptions就是具体管理那些包含那些模块的配置。该类是在App或者是测试module独立运行(后面提到)的时候进行初始化。例如:
public class CustomApplication extends BaseApplication { @Override public void onCreate() { super.onCreate(); initARouter(); } private void initARouter() { if (LG.isDebug) { ARouter.openLog(); ARouter.openDebug(); ARouter.printStackTrace(); } ARouter.init(this); ModuleOptions.ModuleBuilder builder = new ModuleOptions.ModuleBuilder(this) .addModule(IHomeProvider.HOME_MAIN_SERVICE, IHomeProvider.HOME_MAIN_SERVICE) .addModule(IModule1Provider.MODULE1_MAIN_SERVICE, IModule1Provider.MODULE1_MAIN_SERVICE) .addModule(IModule2Provider.MODULE2_MAIN_SERVICE, IModule2Provider.MODULE2_MAIN_SERVICE) .addModule(IModule4Provider.MODULE4_MAIN_SERVICE, IModule4Provider.MODULE4_MAIN_SERVICE) .addModule(IModule5Provider.MODULE5_MAIN_SERVICE, IModule5Provider.MODULE5_MAIN_SERVICE); ModuleManager.getInstance().init(builder.build()); } }
这样子就完成了对改App或者是module的管理。
管理服务
咱们使用一个ServiceManager,用来获取不一样模块的服务,即Provider。安装ARouter的文档,咱们经过继承IProvider,编写一个对应模块的接口,提供接口方法,在对应模块实现该Provider。而后该Provider咱们就是在ServiceManager里面进行管理和获取。好比: home模块实现一个IHomeProvider,实现类是HomeProvider。
//接口 public interface IHomeProvider extends IBaseProvider { //Service String HOME_MAIN_SERVICE = "/home/main/service"; //开屏 String HOME_ACT_SPLASH = "/home/act/splash"; //home主页 String HOME_ACT_HOME = "/home/act/home"; String HOME_TABTYPE = "home_tab_type"; void toast(String msg); void selectedTab(Activity activity,int position); } //实现类 @Route(path = IHomeProvider.HOME_MAIN_SERVICE) public class HomeProvider implements IHomeProvider { private Context context; @Override public void init(Context context) { this.context = context; } @Override public void toast(String msg) { Toast.makeText(context, msg, Toast.LENGTH_SHORT).show(); } @Override public void selectedTab(Activity activity,int position) { if (activity instanceof HomeActivity) { ((HomeActivity) activity).selectedTab(position); } } }
而后在ServiceManager中,
//也可使用自动注入,这里是手动发现而且调用 public IModule1Provider getModule1Provider() { return module1Provider != null ? module1Provider : (module1Provider = ((IModule1Provider) MyRouter.newInstance(IModule1Provider.MODULE1_MAIN_SERVICE).navigation())); }
咱们对本Project的全部服务进行管理。而后,在base当中,提供不一样的Service,对Provider进行调用,同时提供Intent方法,去启动不一样模块的Activity,好比:
//管理调用Provider的某一个特定模块的Service public class HomeService { private static boolean hasModule() { return ModuleManager.getInstance().hasModule(IHomeProvider.HOME_MAIN_SERVICE); } public static void selectedTab(Activity activity, int position) { if (!hasModule()) return; ServiceManager.getInstance().getHomeProvider().selectedTab(activity, position); } } //管理该module的Activity跳转 public class HomeIntent { private static boolean hasModule() { return ModuleManager.getInstance().hasModule(IHomeProvider.HOME_MAIN_SERVICE); } public static void launchHome(int tabType) { //HomeActivity MyBundle bundle = new MyBundle(); bundle.put(IHomeProvider.HOME_TABTYPE, tabType); MyRouter.newInstance(IHomeProvider.HOME_ACT_HOME) .withBundle(bundle) .navigation(); } }
Module可独立运行配置
通过这两个,咱们就已经基本完成了项目组件化。可是对于组件化,咱们还有一个特色,就是每个module都是能够独立运行的,方便开发和调试。那么,咱们应该怎么弄?
咱们提供两种环境,一个是debug,一种是release,debug的时候,咱们是可运行的独立模块,release的时候,咱们是library。
debug的时候,咱们须要提供一些测试代码和一套初始化。 例如:咱们须要module1是能够独立运行的,在本demo中,他是一个Fragment做为主入口被别的模块添加使用,因此咱们的debug中须要添加一个Activity,一套清单,一些资源。 咱们使用config.gradle去管理咱们的一些外部依赖arr以及咱们的一些编译版本号,sdk版本号等,以下:
ext {
//...版本号以及arr管理 //home是不是做为模块,true的时候是,false的时候能够独立运行,ps名字有点不对,不想改了ORZ isMouleDebugHome = true; //module1是不是做为模块,true的时候是,false的时候能够独立运行 isModule1Debug = true;
}
而后,在跟build.gradle第一行中apply 进去。
apply from: "config.gradle"
而后,使用sourceSets对代码进行管理,配置debug和release的代码,module1的结构以下
![图片上传中...]
他的gradle配置以下:
if (rootProject.ext.isModule1Debug) { apply plugin: 'com.android.library' } else { apply plugin: 'com.android.application' } android { compileSdkVersion rootProject.ext.android.compileSdkVersion buildToolsVersion rootProject.ext.android.buildToolsVersion defaultConfig { minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion versionCode 101 versionName "1.0.1" if (!rootProject.ext.isModule1Debug) { applicationId "com.github.io.liweijie.lib1" } javaCompileOptions { annotationProcessorOptions { arguments = [moduleName: project.getName()] } } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_7 targetCompatibility JavaVersion.VERSION_1_7 } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } sourceSets { main { if (!rootProject.ext.isModule1Debug) { manifest.srcFile 'src/debug/AndroidManifest.xml' java.srcDir 'src/debug/java/' res.srcDirs=['src/debug/res'] } else { manifest.srcFile 'src/release/AndroidManifest.xml' java.srcDir 'src/release/java/' } } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile project(':base') testCompile 'junit:junit:4.12' annotationProcessor rootProject.ext.dependencies["aroutercompiler"] }
这里须要注意的问题是,debug和relase的清单都须要声明须要的activity以及其余组件。debug中还应该配置Application,进行ARouter的初始化。 通过这样子,咱们的每个module都是能够独立运行的模块了。通常而言,release实际上是没有什么东西的,由于release须要的就是咱们module自己须要的业务逻辑实现代码,他是做为library去使用的,看本身项目是否须要配置relase。 在本项目中,修改对应的config.gradle中的值就可使得module1和home独立运行或者是做为lib添加。 最终结果如图:
App独立运行:
![图片上传中...]
App2独立运行:
home独立运行:
module1独立运行:
组件化开发的建议
在咱们每个模块文档以后,咱们应该使用arr的形式引入依赖,上传咱们的maven库,再去compile下来,同时注释掉setting.gradle的配置,这样子有助于编译加快。
删除各个module的test代码,也就是src目录下的test,由于那些都是包含一些task的,在同步或者是编译的时候会被执行,减慢了编译速度。
四大组件应该在各自module里面声明。
在Base中提供一个BaseApplication。
旧项目组件化过程实践
最近在作公司的App,和同事一块儿把负责的App组件化了,在这个过程当中有一些坑跟你们分享一下,避免你们再踩。
不要一步完全的组件化,步子不要迈得太大,建议是先将比较容易的组件进行拆分出来,先作成模块,而后一边进行业务的迭代,两边都不耽误,除非你有足够时间进行组件化工做才去完全组件化。
关于资源的拆分,一些style,一些常见的string,一些共用的图片,drawable等资源,建议存放在base module当中,能够共用。对于属于不一样模块的资源,建议不要存放在base,可能一开始你直接把全部的资源都放到base里面会比较好作,好比不用担忧编译错误,资源没法寻找到,须要一个一个的复制的问题,可是,你存放到了base,而base模块,通常开发过程当中,是常常变更的,那么,就是他会常常被编译,就会带来一个编译的速度问题,因此应该各个模块存放本身的资源。
关于Base的依赖库问题,好比咱们可能有自家的sdk,第三方的依赖等等,这些,与业务逻辑无关的依赖,建议是使用一个单独的project 进行二次封装,封装完成以后,打包arr 依赖,上传自家maven库,经过compile 进行base,或者是特定模块,不建议直接在app中新建module。好比,咱们要引入volley,咱们应该使用一个新的project,对volley进行二次封装,稳定以后,经过compile arr 引入base 或者是其余模块中。这样子这些与业务逻辑无关的也方便公司其余项目使用。
针对旧项目拆分的module,能够暂时不编写debug,也就是能够不用使得旧项目能够独立运行,由于他涉及的初始化逻辑可能较多,时间不够,直接被app依赖运行测试。针对新的module,能够编写debug来使得新module独立运行,方便调试。
组件化带来的问题
因为项目的组价化,可能须要每一个业务拆分的比较开,那么将会致使module也比较的多。到了最后,多是一个模块带一个Activity以及一个Fragment做为一个组件,那么将会比较难的开发,因此,模块的拆分,咱们须要按照咱们实际项目进行划分模块,不要划分太细致。同时,在旧项目组件化过程当中,因为各个模块还没有成熟,尚未稳定,那么就会有比较多的模块进行编译,速度也是比较慢的,因此旧项目的拆分也是须要按照必定的步骤慢慢拆分。同时,建议使用Activity+Fragment的方式进行组件化,方便被别的组件使用,Activity不涉及通常逻辑。有时候修改base库,须要修改依赖方式会比较的麻烦。有时候会有资源冲突问题,这个咱们在gradle里面为模块资源添加一个前缀解决