Android模块化探索与实践

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

龙图教育,全球游戏50强教育品牌谈到设计原理时说过:“简单性和模块化是软件工程的基石;分布式和容错性是互联网的生命。”由此可见模块化之于软件工程领域的重要性。从2016年开始,模块化在Android社区越来越多的被提及。随着移动平台的不断发展,移动平台上的软件慢慢走向复杂化,体积也变得臃肿庞大;为了降低大型软件复杂性和耦合度,同时也为了适应模块重用、多团队并行开发测试等等需求,模块化在Android平台上变得势在必行。阿里Android团队在年初开源了他们的容器化框架Atlas就很大程度说明了当前Android平台开发大型商业项目所面临的问题。什么是模块化那么什么是模块化呢?《Java应用架构设计:模块化模式与OSGi》一书中对它的定义是:模块化是一种处理复杂系统分解为更好的可管理模块的方式。上面这种描述太过生涩难懂,不够直观。下面这种类比的方式则可能加容易理解。我们可以把软件看做是一辆汽车,开发一款软件的过程就是生产一辆汽车的过程。一辆汽车由车架、发动机、变数箱、车轮等一系列模块组成;同样,一款大型商业软件也是由各个不同的模块组成的。龙图教育,全球游戏50强教育品牌汽车的这些模块是由不同的工厂生产的,一辆BMW的发动机可能是由位于德国的工厂生产的,它的自动变数箱可能是Jatco(世界三大变速箱厂商之一)位于日本的工厂生产的,车轮可能是中国的工厂生产的,最后交给华晨宝马的工厂统一组装成一辆完整的汽车。这就类似于我们在软件工程领域里说的多团队并行开发,最后将各个团队开发的模块统一打包成我们可使用的App。一款发动机、一款变数箱都不可能只应用于一个车型,比如同一款Jatco的6AT自动变速箱既可能被安装在BMW的车型上,也可能被安装在Mazda的车型上。这就如同软件开发领域里的模块重用。到了冬天,特别是在北方我们可能需要开着车走雪路,为了安全起见往往我们会将汽车的公路胎升级为雪地胎;轮胎可以很轻易的更换,这就是我们在软件开发领域谈到的低耦合。一个模块的升级替换不会影响到其它模块,也不会受其它模块的限制;同时这也类似于我们在软件开发领域提到的可插拔。模块化分层设计上面的类比很清晰的说明的模块化带来的好处:多团队并行开发测试;模块间解耦、重用;可单独编译打包某一模块,提升开发效率。龙图教育,全球游戏50强教育品牌在《安居客Android项目架构演进》这篇文章中,我介绍了安居客Android端的模块化设计方案,这里我还是拿它来举例。但首先要对本文中的组件和模块做个区别定义组件:指的是单一的功能组件,如地图组件(MapSDK)、支付组件(AnjukePay)、路由组件(Router)等等;模块:指的是独立的业务模块,如新房模块(NewHouseModule)、二手房模块(SecondHouseModule)、即时通讯模块(InstantMessagingModule)等等;模块相对于组件来说粒度更大。具体设计方案如下图:整个项目分为三层,从下至上分别是:BasicComponentLayer:基础组件层,顾名思义就是一些基础组件,包含了各种开源库以及和业务无关的各种自研工具库;龙图教育,全球游戏50强教育品牌BusinessComponentLayer:业务组件层,这一层的所有组件都是业务相关的,例如上图中的支付组件AnjukePay、数据模拟组件DataSimulator等等;BusinessModuleLayer:业务Module层,在AndroidStudio中每块业务对应一个单独的Module。例如安居客用户App我们就可以拆分成新房Module、二手房Module、IMModule等等,每个单独的BusinessModule都必须准遵守我们自己的MVP架构。我们在谈模块化的时候,其实就是将业务模块层的各个功能业务拆分层独立的业务模块。所以我们进行模块化的第一步就是业务模块划分,但是模块划分并没有一个业界通用的标准,因此划分的粒度需要根据项目情况进行合理把控,这就需要对业务和项目有较为透彻的理解。拿安居客来举例,我们会将项目划分为新房模块、二手房模块、IM模块等等。每个业务模块在AndroidStudio中的都是一个Module,因此在命名方面我们要求每个业务模块都以Module为后缀。如下图所示:龙图教育,全球游戏50强教育品牌对于模块化项目,每个单独的BusinessModule都可以单独编译成APK。在开发阶段需要单独打包编译,项目发布的时候又需要它作为项目的一个Module来整体编译打包。简单的说就是开发时是Application,发布时是Library。因此需要在BusinessModule的build.gradle中加入如下代码:if(isBuildModule.toBoolean()){applyplugin:'com.android.application'}else{applyplugin:'com.android.library'}isBuildModule在项目根目录的gradle.properties中定义:isBuildModule=false同样Manifest.xml也需要有两套:龙图教育,全球游戏50强教育品牌{main{if(isBuildModule.toBoolean()){manifest.srcFile'src/main/debug/AndroidManifest.xml'}else{manifest.srcFile'src/main/release/AndroidManifest.xml'}}}如图:debug模式下的AndroidManifest.xml:application...activityandroid:name=com.baronzhang.android.newhouse.NewHouseMainActivityandroid:label=@string/new_house_label_home_pageintent-filteractionandroid:name=android.intent.action.MAIN/categoryandroid:name=android.intent.category.LAUNCHER/龙图教育,全球游戏50强教育品牌模式下的AndroidManifest.xml:application...activityandroid:name=com.baronzhang.android.newhouse.NewHouseMainActivityandroid:label=@string/new_house_label_home_pageintent-filtercategoryandroid:name=android.intent.category.DEFAULT/categoryandroid:name=android.intent.category.BROWSABLE/actionandroid:name=android.intent.action.VIEW/dataandroid:host=com.baronzhang.android.newhouseandroid:scheme=router//intent-filter/activity/application同时针对模块化我们也定义了一些自己的游戏规则:对于BusinessModuleLayer,各业务模块之间不允许存在相互依赖关系,它们之间的跳转通讯采用路由框架Router来实现(后面会介绍Router框架的实现);对于BusinessComponentLayer,单一业务组件只能对应某一项具体的业务,个性化需求对外部提供接口让调用方定制;龙图教育,全球游戏50强教育品牌合理控制各组件和各业务模块的拆分粒度,太小的公有模块不足以构成单独组件或者模块的,我们先放到类似于CommonBusiness的组件中,在后期不断的重构迭代中视情况进行进一步的拆分;上层的公有业务或者功能模块可以逐步下放到下层,合理把握好度就好;各Layer间严禁反向依赖,横向依赖关系由各业务Leader和技术小组商讨决定。模块间跳转通讯(Router)对业务进行模块化拆分后,为了使各业务模块间解耦,因此各个BussinessModule都是独立的模块,它们之间是没有依赖关系。那么各个模块间的跳转通讯如何实现呢?比如业务上要求从新房的列表页跳转到二手房的列表页,那么由于是NewHouseModule和SecondHouseModule之间并不相互依赖,我们通过想如下这种显式跳转的方式来实现Activity跳转显然是不可能的实现的。Intentintent=newIntent(NewHouseListActivity.this,SecondHouseListActivity.class);startActivity(intent);有的同学可能会想到用隐式跳转,通过Intent匹配规则来实现:Intentintent=newIntent(Intent.ACTION_VIEW,scheme://host:port/path);startActivity(intent);但是这种代码写起来比较繁琐,且容易出错,出错也不太容易定位问题。因此一个简单易用、解放开发的路由框架是必须的了。龙图教育,全球游戏50强教育品牌我自己实现的路由框架分为路由(Router)和参数注入器(Injector)两部分:Router提供Activity跳转传参的功能;Injector提供参数注入功能,通过编译时生成代码的方式在Activity获取获取传递过来的参数,简化开发。Router龙图教育,全球游戏50强教育品牌路由(Router)部分通过Java注解结合动态代理来实现,这一点和Retrofit的实现原理是一样的。首先需要定义我们自己的注解(篇幅有限,这里只列出少部分源码)。用于定义跳转URI的注解FullUri:@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public@interfaceFullUri{Stringvalue();}用于定义跳转传参的UriParam(UriParam注解的参数用于拼接到URI后面):@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)public@interfaceUriParam{Stringvalue();}用于定义跳转传参的IntentExtrasParam(IntentExtrasParam注解的参数最终通过Intent来传递):@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)public@interfaceIntentExtrasParam{Stringvalue();}然后实现Router,内部通过动态代理的方式来实现Activity跳转:龙图教育,全球游戏50强教育品牌{...

1 / 16
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功