使用ARouter进行Android模块化开发

Android模块化

关于为何要进行Android模块化开发,网上也已经讲烂了,不过归结起来,大致是能够总结为:在App开发的初期,代码量不大,业务量比较少,一个App做为一个单独的模块进行开发,每每问题不大。但随着业务的增多,代码变的愈来愈复杂,每一个模块之间的代码耦合变得愈来愈严重,结构愈来愈臃肿,修改一处代码要编译整个工程,致使很是耗时,这时候最好的办法就是进行模块化拆分。java

模块化的好处

总结如今的模块化,大致有如下一些好处:android

  • 公用功能,不用重复开发、修改,代码复用性更强;
  • 独立编译运行,提升编译速度,也就提升了开发效率;
  • 更利于团队开发,不一样的人能够独立负责不一样的模块;
  • 独立模块能够采用不一样的技术架构,尝试新的技术方案,好比采用新的网络框架,甚至换成Kotlin来开发App。

须要解决的问题

要将项目模块化拆分,须要解决如下几个问题:git

  • 模块间页面跳转;
  • 模块间事件通讯;
  • 模块间服务调用;
  • 模块的独立运行;
  • 模块间页面跳转路由拦截(如登陆)

对于上面的问题,均可以使用ARouter进行解决。github

ARouter框架

官方地址:ARouter开源地址
官方对ARouter框架的定义是:一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通讯、解耦。api

  • 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
  • 支持多模块工程使用
  • 支持添加多个拦截器,自定义拦截顺序
  • 支持依赖注入,可单独做为依赖注入框架使用
  • 支持InstantRun
  • 支持MultiDex(Google方案)
  • 映射关系按组分类、多级管理,按需初始化
  • 支持用户指定全局降级与局部降级策略
  • 页面、拦截器、服务等组件均自动注册到框架
  • 支持多种方式配置转场动画
  • 支持获取Fragment
  • 彻底支持Kotlin以及混编(配置见文末 其余#5)
  • 支持第三方 App 加固(使用 arouter-register 实现自动注册)
  • 支持生成路由文档
  • 提供 IDE 插件便捷的关联路径和目标类

基本用法

添加依赖和配置

使用ARouter以前,须要先添加相应的依赖,依赖的脚步以下:安全

android {
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
    // 替换成最新版本, 须要注意的是api
    // 要与compiler匹配使用,均使用最新版能够保证兼容
    compile 'com.alibaba:arouter-api:x.x.x'
    annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
    ...
}
// 旧版本gradle插件(< 2.2),可使用apt插件,配置方法见文末'其余#4'
// Kotlin配置参考文末'其余#5'

添加注解

要想ARouter识别路由,须要使用@Route注解标示具体的路径。例如:网络

// 在支持路由的页面上添加注解(必选)
// 这里的路径须要注意的是至少须要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
    ...
}

初始化SDK

if (isDebug()) {           // 这两行必须写在init以前,不然这些配置在init过程当中将无效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 开启调试模式(若是在InstantRun模式下运行,必须开启调试模式!线上版本须要关闭,不然有安全风险)
}
ARouter.init(mApplication); // 尽量早,推荐在Application中初始化

发起路由操做

所谓发起路由操做,就是指触发路由的操做,例如:架构

// 1. 应用内简单的跳转(经过URL跳转在'进阶用法'中)
ARouter.getInstance().build("/test/activity").navigation();

// 2. 跳转并携带参数
ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .withObject("key4", new Test("Jack", "Rose"))
            .navigation();

若是要接受传递处理的数据,可使用下面的方式:app

@Route(path = MainRoutePath.MAIN_ACTIVITY)
public class MainActivity extends BaseActivity {

    /**
     * 接收参数
     */
    @Autowired(name = "name")
    public String name;
    @Autowired(name = "age")
    public int age;
    ...
}

拦截器

// A more classic application is to handle login events during a jump so that there is no need to repeat the login check on the target page.
// Interceptors will be executed between jumps, multiple interceptors will be executed in order of priority
@Interceptor(priority = 8, name = "test interceptor")
public class TestInterceptor implements IInterceptor {
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        ...
        // No problem! hand over control to the framework
        callback.onContinue(postcard);  
        
        // Interrupt routing process
        // callback.onInterrupt(new RuntimeException("Something exception"));      

        // The above two types need to call at least one of them, otherwise it will not continue routing
    }

    @Override
    public void init(Context context) {
        // Interceptor initialization, this method will be called when sdk is initialized, it will only be called once
    }
}

降级策略

ARouter提供的降级策略主要有两种方式,一种是经过回调的方式;一种是提供服务接口的方式。
1、回调方式
这种方式在跳转失败的时候会回调NavCallback接口的onLost方法。ARouter提供的回调方式的函数以下:框架

ARouter.getInstance().build(MainRoutePath.MAIN_ACTIVITY).navigation(this, new NavCallback() {
     @Override
     public void onFound(Postcard postcard) {
          Log.d("ARouter", "找到了");
     }

     @Override
     public void onLost(Postcard postcard) {
          Log.d("ARouter", "找不到了");
     }

     @Override
     public void onArrival(Postcard postcard) {
          Log.d("ARouter", "跳转完了");
     }

     @Override
      public void onInterrupt(Postcard postcard) {
           Log.d("ARouter", "被拦截了");
      }
});

2、服务接口
全局降级-服务接口的方式主要处理逻辑在内部,暴露的接口很友好。

ARouter.getInstance().build("/test/test").navigation();

此种降级策略须要实现服务接口DegradeService,返回的就一个方法就是onLost。例如:

@Route(path = RoutePath.DEGRADE)
public class DegradeServiceImpl implements DegradeService {
    @Override
    public void onLost(Context context, Postcard postcard) {
        ARouter.getInstance().build(RoutePath.DEGRADE_TIP).greenChannel().navigation();
    }

    @Override
    public void init(Context context) {

    }
}

混淆

为了不打包时出现错误,须要将下面的内容使用keep标示。

-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

# 若是使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 若是使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider

ARouter Helper,

在 Android Studio 插件市场中搜索 ARouter Helper, 或者直接下载文档上方 最新版本 中列出的 arouter-idea-plugin zip 安装包手动安装,安装后 插件无任何设置,能够在跳转代码的行首找到一个图标 (navigation) 点击该图标,便可跳转到标识了代码中路径的目标类。
在这里插入图片描述

ARouter模块化示例

接下来,将会用一个demo介绍如何用ARouter进行模块化开发,demo模块化的总体架构以下图所示。
在这里插入图片描述
每一个模块的做用以下:

    • app:项目的宿主模块,仅仅是一个空壳,依赖于其余模块,成为项目架构的入口;
    • baselibrary:项目的基类库,每一个子模块都依赖共享公用的类和资源,防止公用的功能在不一样的模块中有多个实现方式;
    • module_route:集中管理全部模块的route的库;
    • module_main:闪屏页,登陆页,主页等;
    • module_home:首页模块;
    • module_video:视频模块;
    • module_mine:个人模块;

    依赖模式与独立模式的切换

    使用模块化开发的一个好处是,各个独立的模块能够同时开发,独立运行而没必要依赖于宿主app,也就是每一个module是一个独立的App,项目发布的时候依赖到宿主App中便可。各业务模块之间不容许存在相互依赖关系,可是须要依赖基类库。
    而且,单一模块生成的apk体积也小,编译时间也快,开发效率会高不少,同时也能够独立测试,要实现这样的效果须要对项目作一些配置。

    gradle.properties配置

    在主项目的gradle.properties中须要设置一个开关,用来控制module的编译模式,例如:

    isModule=false

    当isModule为false时做为依赖库,只能以宿主app启动项目,选择运行模块时其余module前都是红色的X,表示没法运行。
    在这里插入图片描述

    当isModule为true时,做为单独的模块进行运行,选择其中一个具体的module就能够直接运行。
    在这里插入图片描述

    module清单文件

    为了完成依赖模式与独立模式的切换,module清单文件须要配置两个,一个做为独立项目的清单文件,一个做为库的清单文件,以module_main模块为例。
    在这里插入图片描述
    buildApp做为依赖库的清单文件,和独立项目的清单文件buildModule区别是依赖库的清单文件Application中没有配置入口的Activity,其余都同样。

    gradle配置

    为了完成切换,还须要对module的build.gradle文件进行配置,以下图:
    在这里插入图片描述

    宿主app配置

    接下来,在宿主app的build.gradle中添加模块依赖,以下所示:

    dependencies {
        if (!isModule.toBoolean()) {
            implementation project(':module_home')
            implementation project(':module_video')
            implementation project(':module_main')
            implementation project(':module_mine')
            implementation project(':module_router')
        }
    }

    ARouter Helper

    常见问题

    问题1

    错误:ARouter::Compiler >>> No module name, for more information, look at gradle log.
    检查项目依赖的所有module,包括module依赖的module,为了可以进行单独的编译,因此须要为每个module添加名称,即在每一个module的 build.gradle中加上下面的代码:

    defaultConfig {
        
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [ AROUTER_MODULE_NAME : project.getName() ]
                }
            }
    }

    问题2

    ARouter.getInstance().inject(this);
    问题描述:有一个singletask启动模式的activity,在onNewIntent方法中调用ARouter.getInstance().inject(this);得不到参数,查看ARouter在build过程当中生成的代码能够知道它是调用了activity的getIntent来获取参数的,可是onNewIntent中的intent和在onCreate方法中的intent并不相同,因此须要在onNewIntent方法中调用setIntent方法,而后就能获得参数了。

    问题3

    ARouter there's no route matched

    不一样module的一级路径必须不一样,不然会致使一个moudle中的一级路径失效。

    附:示例下载地址

    相关文章
    相关标签/搜索