首页 存档 技术 查看内容

android组件化方案,让团队开发更有效率

2018-3-30 13:00 |来自: 互联网 344 0

摘要: 刚接到Leader组件化任务的时候,内心是有疑惑的。目前项目中,各种业务交杂在一起,互相跳转、互相请求数据。分模块的过程必然是痛苦的,需要增加模块之间通的信协议。对于一个5,6人的团队来说,全都放在一个大模块 ...

刚接到Leader组件化任务的时候,内心是有疑惑的。目前项目中,各种业务交杂在一起,互相跳转、互相请求数据。分模块的过程必然是痛苦的,需要增加模块之间通的信协议。对于一个5,6人的团队来说,全都放在一个大模块中似乎也没啥不好,可以随心所欲地调用,节约思考框架合理性的时间。

但最终让我解除疑惑是团队扩张的长远考虑,未来团队扩张到8~10人时,在同一个模块中开发不同的业务就会很乱。代码量2倍,掌握的难度就会超过4倍,按业务分模块一定是未来的方向。

顾名思义,组件化能够让开发者只需专注自己开发的组件,独立运行自己的模块,节省编译时间,减少因别人的问题导致工作被打断的可能。

组件化的实现围绕下面几个点

1、子模块单独编译
2、sdk和第三方库的版本一致性
3、资源重复定义
4、模块之间页面跳转
5、模块之间数据传递
6、模块初始化处理

1、子模块如何单独编译

我们希望在开发模式下,能够单独调试自己的模块,编译成独立的apk。而在主程序发布时,成为一个library嵌入主工程。

首先在子模块build.gradle中定义常量,来标示模块目前是否处于开发模式

def isDebug = true

在子模块的build.gradle中进行模式配置。debug模式下编译成独立app,release模式下编译成library。

if (isDebug.toBoolean()) {
 apply plugin: 'com.android.application'} else {
 apply plugin: 'com.android.library'}

两种模式下模块AndroidManifest.xml文件是有差别的。作为独立运行的app,有自己的Application,要加Launcher的入口intent,作为library不需要。这个问题很好解决,写两个不同的AndroidManifest.xml即可,并在gradle中进行配置。

 sourceSets {
  main {
   if (isDebug.toBoolean()) {
    manifest.srcFile 'src/main/debug/AndroidManifest.xml'
   } else {
    manifest.srcFile 'src/main/AndroidManifest.xml'
   }
  }
 }

2、sdk和第三方库的版本一致性

不同module依赖sdk版本不一致,会因兼容性问题导致编译问题。
不同module引用了同一个第三方库的不同版本,并且这个库没有做到向前兼容,就有可能出现方法找不到、参数不对应等问题。
所以有必要统一整个project的依赖版本。

在最外层build.gradle中定义的常量能被整个projectbuild.gradle文件引用,统一的版本定义可以放在这里。

ext {
  android_compileSdkVersion = 25
  android_buildToolsVersion = '25.0.2'
  android_minSdkVersion = 21
  android_targetSdkVersion = 25

  lib_appcompat = 'com.android.support:appcompat-v7:25.1.1'
  lib_picasso = 'com.squareup.picasso:picasso:2.5.2'
  lib_gson = 'com.google.code.gson:gson:2.6.1'}

3、资源的重复定义

说到资源的重复定义,笔者趟过坑,如果主工程和子模块中重复定义了同名的资源。

主工程中

爸爸

子工程中

干爹

虽然编译不会出错,但是最后子模块中用到daddy的地方都会显示爸爸
编译时子模块的资源会和主工程合并到同一个类中,所以资源重名会有问题。

但是资源也要模块化呀,总不能在底层找个统一的地方都扔在里面,gradle提供了一个解决方案来避免重复定义的问题。

resourcePrefix "a_"

强制模块中的资源名称带有a_前缀,否则编译不过。

聊到这里,我们知道了如何使用gradle独立编译子模块,以及如何处理分模块导致的一些问题。但是除了主工程统一调度外,模块与模块之间也需要互相调起和访问,所以需要协议去统一,这个协议是模块间共同定义与使用的,所以写在底层。


4、模块之间页面跳转

首先想到的就是配置uri去匹配模块AndroidManifest.xml中的intentFilter来启动相应Activity,这种方式是解耦的,但有缺点,要跳转其它模块,得先去看别的模块的AndroidManifest.xml进行入口适配,还得研究具体Activity中的传参设置,虽然代码依赖上解耦了,但是实现逻辑上没有解耦,忍不了。需要在底层创建一个路由协议,让使用者通过协议方便地调用。

用注解把需要的参数写在路由协议的接口中。下面是moduleA提供给其它模块跳转moduleA中页面的接口:

public interface RouterA {

 @RouterUri("test://host_a")
 public Intent getIntentActivityA(@RounterParam("name") String name, @RounterParam("age") int age,
   @RounterParam("phones") Phones phones);//Phones是一个自定义类}

其中@RouterUri表示跳转改页面需要匹配的uri,这个uri最终会拿去和moduleA中的AndroidManifest.xml中对应activityintentFilter去匹配。

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface RouterUri {
 String value() default "";}

@RounterParam用来表示目标activity需要的参数,最终会在目标activity中进行解析。

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.PARAMETER)public @interface RounterParam {
 String value() default "";}

为什么用注解的方式写接口而不是直接定义跳转方法呢?
用注解的方式,可以把参数更直观地展现在最醒目的方法声明中。而写成实现的方法,参数会被写在方法内部,定义起来不方便,而且要带上少量逻辑,不够简洁。参考retrofit框架,也是用注解方式去实现,简洁、方便。

为什么接口返回的是Intent,而不是直接进行页面跳转呢?
因为我们的项目中,实现这个跳转可能是activity,可能是fragment,也可能startActivityForResult需要带入一个自定义的requestCode。所以为了灵活性,直接返回Intent

写好了接口,还需要将接口中的参数组装成一个可进行跳转的Intent。使用Proxy生成类动态代理这个接口。

public class RounterBus {
 //静态map存储代理接口的实例
 private static HashMap sRounterMap = new HashMap();

 /**
  * 得到动态代理路由接口的实例
  *
  * @param c 接口类
  * @param 
  * @return
  */
 public static T getRounter(Class c) {
  T rounter = (T) sRounterMap.get(c);
  if (rounter == null) {
   rounter = (T) Proxy.newProxyInstance(c.getClassLoader(), new Class[] { c }, new InvocationHandler() {
    @Override public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
     //从方法注解的获取uri
     RouterUri routerUri = method.getAnnotation(RouterUri.class);
     if (routerUri == null || TextUtils.isEmpty(routerUri.value())) {
      throw new IllegalArgumentException(
        "invoke a rounter method, bug not assign a rounterUri");
     }
     Uri.Builder uriBuilder = Uri.parse(routerUri.value()).buildUpon();

     //从参数值和参数注解,获取信息,拼入uri的query
     Anno
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部