整理:ARouter集成、使用和踩坑

介绍:

一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
一、功能介绍
支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
支持多模块工程使用
支持添加多个拦截器,自定义拦截顺序
支持依赖注入,可单独作为依赖注入框架使用
支持InstantRun
支持MultiDex(Google方案)
映射关系按组分类、多级管理,按需初始化
支持用户指定全局降级与局部降级策略
页面、拦截器、服务等组件均自动注册到框架
支持多种方式配置转场动画
支持获取Fragment
完全支持Kotlin以及混编(配置见文末 其他#5)
支持第三方 App 加固(使用 arouter-register 实现自动注册)
支持生成路由文档
提供 IDE 插件便捷的关联路径和目标类
支持增量编译(开启文档生成后无法增量编译)
二、典型应用
从外部URL映射到内部页面,以及参数传递与解析
跨模块页面跳转,模块间解耦
拦截跳转过程,处理登陆、埋点等逻辑
跨模块API调用,通过控制反转来做组件解耦

集成:

1.添加依赖和配置

纯java的:

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

dependencies {
    // 替换成最新版本, 需要注意的是api
    // 要与compiler匹配使用,均使用最新版可以保证兼容
    compile 'com.alibaba:arouter-api:1.5.1'
    annotationProcessor 'com.alibaba:arouter-compiler:1.5.1'
    ...
}

Kotlin或者Kotlin&java混编的:
(手动引用一)

plugins {
    id 'com.android.library'
    id 'kotlin-android'
    id 'kotlin-android-extensions'
    id 'kotlin-kapt'
}
    defaultConfig {
        ...
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [AROUTER_MODULE_NAME: project.getName()]
            }
        }
    }
}

dependencies {
     //...
    api 'com.alibaba:arouter-api:1.5.1'
    kapt 'com.alibaba:arouter-compiler:1.5.1'
    ...
}

小插曲:
implementation 和 api 关键字

implementation 和 api 关键字,在Android studio3.0版本中,曾经的 compile 关键字被弃用,而 api 则是 compile 的替代品, api 与 compile 没有区别。但最新官方推荐使用 implementation 来代替 compile 关键字,据说 implementation 会使Android studio的编译速度更快呦。而 implementation 和 api 关键字的区别则在于用 implementation来声明的依赖包只限于当前module内部使用,对于依赖其module的模块是无法使用到该依赖包的。而用 api来声明依赖包时,依赖于该module的模块可以正常使用其模块内的依赖包。除此之外 testCompile 要用testImplementation 或 testApi 替换,androidTestCompile 要用androidTestImplementation 或 androidTestApi 替换。
在本次集成中,将直接继承到一个公共的基础module,来让app module进行依赖,因此使用 api关键字。
若没有对项目进行组件化,则可以使用 implementation 关键字进行依赖。

2.添加混淆规则(如果使用了Proguard)

-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

基础操作:

1.初始化SDK

  if (isApkInDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
            ARouter.openLog();     // 打印日志
            ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
        }
        ARouter.init(this); // 尽可能早,推荐在Application中初始化
 //判断当前应用是否是debug状态
    public static boolean isApkInDebug() {
        try {
            ApplicationInfo info = sAppLike.getApplicationInfo();
            return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
        } catch (Exception e) {
            return false;
        }
    }

2.添加注解

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

3.发起路由操作

// 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();

基础操作的细节:

  1. 注意看项目是不是有kotlin,如果有的那,那每一个与它相依赖的包都需要“引用一”的方式进行依赖;
  2. 包与包的调用,需要建立在依赖的前提下。(之后再写一种不需要直接依赖的方式);
  3. 路由路径建议写成常量,创建路由表进行统一管理。
  4. 路径需要注意的是至少需要有两级,不然会编译不通过或者报错;
  5. 并且不同包的一级路径不应该相同,否则会找不到对应路径进行跳转。原因,看以下剖析源码的过程:

出现以上细节错误的时候,都是显示如下截图的错误提示:
在这里插入图片描述

通常来说这种情况是没有找到目标页面,目标不存在如果这个页面是存在的,
那么您可以按照下面的步骤进行排查:
检查目标页面的注解是否配置正确,正确的注解形式应该是 (@Route(path="/test/test"), 如没有特殊需求,请勿指定group字段,废弃功能);
检查目标页面所在的模块的gradle脚本中是否依赖了 arouter-compiler sdk (需要注意的是,要使用apt依赖,而不是compile关键字依赖);
检查编译打包日志,是否出现了形如 ARouter::�Compiler >>> xxxxx 的日志,日志中会打印出发现的路由目标;
启动App的时候,开启debug、log(openDebug/openLog), 查看映射表是否已经被扫描出来,形如 D/ARouter::: LogisticsCenter has already been loaded, GroupIndex[4],GroupIndex > 0。

直接阅读源码,看具体问题所在:
点击进入这个navigation()的源码,一层一层进入:

 ARouter.getInstance().build(A_ROUTER_TEST_ACTIVITY).navigation()
  /**
     * Navigation to the route with path in postcard.
     *
     * @param context Activity and so on.
     */
    public Object navigation(Context context, NavigationCallback callback) {
        return ARouter.getInstance().navigation(context, this, -1, callback);
    }
 protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
            // Pretreatment failed, navigation canceled.
            return null;
        }

        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            //...

继续进入这个方法

LogisticsCenter.completion(postcard);
    public synchronized static void completion(Postcard postcard) {
    if (null == postcard) {
        throw new NoRouteFoundException(TAG + "No postcard!");
    }

    //查找RouteMeta对象,如果存在说明路由成功,如果失败说明还没有被加载或者这个path就是错误的
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
      // 通过groupsIndex去找IRouteGroup的实现类
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
        if (null == groupMeta) {
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {
            // 通过反射获取IRouteGroup的实现类,然后加载到内存
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);
            // 加载到内存后Warehouse.groupsIndex去掉这个group
            Warehouse.groupsIndex.remove(postcard.getGroup());
        }
    } else {
        //查找到路由地址
      	//...
    }
}

查一下IRouterGroup的实现类,这里特别注意,如果没找到对应的包的实现类(多个包做了路径映射的话,就会有多个实现类),那就证明没有映射成功,就证明出现细节上的bug啦

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$app implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/app/ble", RouteMeta.build(RouteType.ACTIVITY, BleBluetoothTestActivity.class, "/app/ble", "app", null, -1, -2147483648));
  }
}

这里的path都是”/app/xxxx/”,Aouter 要求path必须有至少两级的路径,是因为Arouter在寻找route的时候,是通过第一级路径,也就是这里的”app”来寻找的。Aouter通过”app”找到了route,并且在groupIndex中删除了这个路径,代表已经加载到了内存。

因此,不同的module使用了相同的一级路径,在Arouter第一次寻找到route的时候便删除了这个一级路径的group,因为一级路径的重复,再调用另一个module的一级路径是”app”的路由时,由于之前Warehouse.groupsIndex已经删除,便导致了there’s no route matched的错误。

Ref:

  1. 官方地址
  2. AROUTER THERE’S NO ROUTE MATCHED解决方法
Android ARouter 是一个用于实现组件化、模块化开发的路由框架,它提供了一种简单的方式来实现不同组件之间的跳转和通信。 使用 ARouter 的步骤如下: 1. 在项目的 build.gradle 文件中添加依赖: ```groovy implementation 'com.alibaba:arouter-api:x.x.x' annotationProcessor 'com.alibaba:arouter-compiler:x.x.x' ``` 请将 `x.x.x` 替换为最新的版本号。 2. 在需要使用 ARouter 的模块中,创建一个类似于 Application 的类,并在其 `onCreate()` 方法中进行 ARouter 的初始化: ```java public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); if (BuildConfig.DEBUG) { ARouter.openLog(); ARouter.openDebug(); } ARouter.init(this); } } ``` 3. 在需要跳转到的目标页面中,使用 `@Route` 注解进行标记,以便 ARouter 进行识别: ```java @Route(path = "/path/to/target") public class TargetActivity extends AppCompatActivity { // ... } ``` 4. 在需要跳转的地方,通过调用 `ARouter.getInstance().build("/path/to/target").navigation()` 来实现页面跳转: ```java ARouter.getInstance().build("/path/to/target").navigation(); ``` 5. 如果需要传递参数,可以使用 `withXxx` 方法来添加参数: ```java ARouter.getInstance() .build("/path/to/target") .withString("key", "value") .navigation(); ``` 通过以上步骤,你就可以在 Android 项目中使用 ARouter 进行页面跳转和参数传递了。当然,ARouter 还提供了其他功能,比如拦截器、URI 跳转等,你可以根据具体需求进行使用。希望对你有所帮助!如果还有其他问题,请继续提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值