remove bintray repo
一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
English
添加依赖和配置
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'
添加注解
// 在支持路由的页面上添加注解(必选) // 这里的路径需要注意的是至少需要有两级,/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();
添加混淆规则(如果使用了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
使用 Gradle 插件实现路由表的自动加载 (可选)
apply plugin: 'com.alibaba.arouter' buildscript { repositories { mavenCentral() } dependencies { classpath "com.alibaba:arouter-register:?" } }
可选使用,通过 ARouter 提供的注册插件进行路由表的自动加载(power by AutoRegister), 默认通过扫描 dex 的方式 进行加载通过 gradle 插件进行自动注册可以缩短初始化时间解决应用加固导致无法直接访问 dex 文件,初始化失败的问题,需要注意的是,该插件必须搭配 api 1.3.0 以上版本使用!
使用 IDE 插件导航到目标类 (可选)
在 Android Studio 插件市场中搜索 ARouter Helper, 或者直接下载文档上方 最新版本 中列出的 arouter-idea-plugin zip 安装包手动安装,安装后 插件无任何设置,可以在跳转代码的行首找到一个图标 () 点击该图标,即可跳转到标识了代码中路径的目标类
ARouter Helper
最新版本
arouter-idea-plugin
通过URL跳转
// 新建一个Activity用于监听Scheme事件,之后直接把url传递给ARouter即可 public class SchemeFilterActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Uri uri = getIntent().getData(); ARouter.getInstance().build(uri).navigation(); finish(); } }
AndroidManifest.xml
<activity android:name=".activity.SchemeFilterActivity"> <!-- Scheme --> <intent-filter> <data android:host="m.aliyun.com" android:scheme="arouter"/> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.BROWSABLE"/> </intent-filter> </activity>
解析参数 ``` java // 为每一个参数声明一个字段,并使用 @Autowired 标注 // URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象 @Route(path = “/test/activity”) public class Test1Activity extends Activity {
@Autowired public String name; @Autowired int age; // 通过name来映射URL中的不同参数 @Autowired(name = "girl") boolean boy; // 支持解析自定义对象,URL中使用json传递 @Autowired TestObj obj; // 使用 withObject 传递 List 和 Map 的实现了 // Serializable 接口的实现类(ArrayList/HashMap) // 的时候,接收该对象的地方不能标注具体的实现类类型 // 应仅标注为 List 或 Map,否则会影响序列化中类型 // 的判断, 其他类似情况需要同样处理 @Autowired List<TestObj> list; @Autowired Map<String, List<TestObj>> map; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ARouter.getInstance().inject(this); // ARouter会自动对字段进行赋值,无需主动获取 Log.d("param", name + age + boy); }
}
// 如果需要传递自定义对象,新建一个类(并非自定义对象类),然后实现 SerializationService,并使用@Route注解标注(方便用户自行选择序列化方式),例如: @Route(path = "/yourservicegroupname/json") public class JsonServiceImpl implements SerializationService { @Override public void init(Context context) { } @Override public <T> T json2Object(String text, Class<T> clazz) { return JSON.parseObject(text, clazz); } @Override public String object2Json(Object instance) { return JSON.toJSONString(instance); } } ```
声明拦截器(拦截跳转过程,面向切面编程)
// 比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查 // 拦截器会在跳转之间执行,多个拦截器会按优先级顺序依次执行 @Interceptor(priority = 8, name = "测试用拦截器") public class TestInterceptor implements IInterceptor { @Override public void process(Postcard postcard, InterceptorCallback callback) { ... callback.onContinue(postcard); // 处理完成,交还控制权 // callback.onInterrupt(new RuntimeException("我觉得有点异常")); // 觉得有问题,中断路由流程 // 以上两种至少需要调用其中一种,否则不会继续路由 } @Override public void init(Context context) { // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次 } }
处理跳转结果
// 使用两个参数的navigation方法,可以获取单次跳转的结果 ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() { @Override public void onFound(Postcard postcard) { ... } @Override public void onLost(Postcard postcard) { ... } });
自定义全局降级策略
// 实现DegradeService接口,并加上一个Path内容任意的注解即可 @Route(path = "/xxx/xxx") public class DegradeServiceImpl implements DegradeService { @Override public void onLost(Context context, Postcard postcard) { // do something. } @Override public void init(Context context) { } }
为目标页面声明更多信息
// 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆"之类的 // 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,换句话说,单个int有4字节,也就是32位,可以配置32个开关 // 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,在拦截器中可以拿到这个标记进行业务逻辑判断 @Route(path = "/test/activity", extras = Consts.XXXX)
通过依赖注入解耦:服务管理(一) 暴露服务
// 声明接口,其他组件通过接口来调用服务 public interface HelloService extends IProvider { String sayHello(String name); } // 实现接口 @Route(path = "/yourservicegroupname/hello", name = "测试服务") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "hello, " + name; } @Override public void init(Context context) { } }
通过依赖注入解耦:服务管理(二) 发现服务
public class Test { @Autowired HelloService helloService; @Autowired(name = "/yourservicegroupname/hello") HelloService helloService2; HelloService helloService3; HelloService helloService4; public Test() { ARouter.getInstance().inject(this); } public void testService() { // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取 // Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,不设置name属性,会默认使用byType的方式发现服务(当同一接口有多个实现的时候,必须使用byName的方式发现服务) helloService.sayHello("Vergil"); helloService2.sayHello("Vergil"); // 2. 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType helloService3 = ARouter.getInstance().navigation(HelloService.class); helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation(); helloService3.sayHello("Vergil"); helloService4.sayHello("Vergil"); } }
预处理服务
// 实现 PretreatmentService 接口,并加上一个Path内容任意的注解即可 @Route(path = "/xxx/xxx") public class PretreatmentServiceImpl implements PretreatmentService { @Override public boolean onPretreatment(Context context, Postcard postcard) { // 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可 } @Override public void init(Context context) { } }
动态注册路由信息 适用于部分插件化架构的App以及需要动态注册路由信息的场景,可以通过 ARouter 提供的接口实现动态注册 路由信息,目标页面和服务可以不标注 @Route 注解,注意:同一批次仅允许相同 group 的路由信息注册
ARouter.getInstance().addRouteGroup(new IRouteGroup() { @Override public void loadInto(Map<String, RouteMeta> atlas) { atlas.put("/dynamic/activity", // path RouteMeta.build( RouteType.ACTIVITY, // 路由信息 TestDynamicActivity.class, // 目标的 Class "/dynamic/activity", // Path "dynamic", // Group, 尽量保持和 path 的第一段相同 0, // 优先级,暂未使用 0 // Extra,用于给页面打标 ) ); } });
初始化中的其他设置
ARouter.openLog(); // 开启日志 ARouter.openDebug(); // 使用InstantRun的时候,需要打开该开关,上线之后关闭,否则有安全风险 ARouter.printStackTrace(); // 打印日志的时候打印线程堆栈
详细的API说明
// 构建标准的路由请求 ARouter.getInstance().build("/home/main").navigation(); // 构建标准的路由请求,并指定分组 ARouter.getInstance().build("/home/main", "ap").navigation(); // 构建标准的路由请求,通过Uri直接解析 Uri uri; ARouter.getInstance().build(uri).navigation(); // 构建标准的路由请求,startActivityForResult // navigation的第一个参数必须是Activity,第二个参数则是RequestCode ARouter.getInstance().build("/home/main", "ap").navigation(this, 5); // 直接传递Bundle Bundle params = new Bundle(); ARouter.getInstance() .build("/home/main") .with(params) .navigation(); // 指定Flag ARouter.getInstance() .build("/home/main") .withFlags(); .navigation(); // 获取Fragment Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation(); // 对象传递 ARouter.getInstance() .withObject("key", new TestObj("Jack", "Rose")) .navigation(); // 觉得接口不够多,可以直接拿出Bundle赋值 ARouter.getInstance() .build("/home/main") .getExtra(); // 转场动画(常规方式) ARouter.getInstance() .build("/test/activity2") .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom) .navigation(this); // 转场动画(API16+) ActivityOptionsCompat compat = ActivityOptionsCompat. makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0); // ps. makeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前Activity ARouter.getInstance() .build("/test/activity2") .withOptionsCompat(compat) .navigation(); // 使用绿色通道(跳过所有的拦截器) ARouter.getInstance().build("/home/main").greenChannel().navigation(); // 使用自己的日志工具打印日志 ARouter.setLogger(); // 使用自己提供的线程池 ARouter.setExecutor();
获取原始的URI
String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);
重写跳转URL
// 实现PathReplaceService接口,并加上一个Path内容任意的注解即可 @Route(path = "/xxx/xxx") // 必须标明注解 public class PathReplaceServiceImpl implements PathReplaceService { /** * For normal path. * * @param path raw path */ String forString(String path) { return path; // 按照一定的规则处理之后返回处理后的结果 } /** * For uri type. * * @param uri raw uri */ Uri forUri(Uri uri) { return url; // 按照一定的规则处理之后返回处理后的结果 } }
生成路由文档
// 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable // 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"] } } } }
路由中的分组概念
@Route(path = "/test/1", group = "app")
拦截器和服务的异同
旧版本gradle插件的配置方式
apply plugin: 'com.neenbedankt.android-apt' buildscript { repositories { mavenCentral() } dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } } apt { arguments { AROUTER_MODULE_NAME project.getName(); } } dependencies { compile 'com.alibaba:arouter-api:x.x.x' apt 'com.alibaba:arouter-compiler:x.x.x' ... }
Kotlin项目中的配置方式
// 可以参考 module-kotlin 模块中的写法 apply plugin: 'kotlin-kapt' kapt { arguments { arg("AROUTER_MODULE_NAME", project.getName()) } } dependencies { compile 'com.alibaba:arouter-api:x.x.x' kapt 'com.alibaba:arouter-compiler:x.x.x' ... }
“W/ARouter::: ARouter::No postcard![ ]”
这个Log正常的情况下也会打印出来,如果您的代码中没有实现DegradeService和PathReplaceService的话,因为ARouter本身的一些功能也依赖 自己提供的Service管理功能,ARouter在跳转的时候会尝试寻找用户实现的PathReplaceService,用于对路径进行重写(可选功能),所以如果您没有 实现这个服务的话,也会抛出这个日志
推荐在app中实现DegradeService、PathReplaceService
“W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group [xxx][ ]”
开启InstantRun之后无法跳转(高版本Gradle插件下无法跳转)?
因为开启InstantRun之后,很多类文件不会放在原本的dex中,需要单独去加载,ARouter默认不会去加载这些文件,因为安全原因,只有在开启了openDebug之后 ARouter才回去加载InstantRun产生的文件,所以在以上的情况下,需要在init之前调用openDebug
TransformException:java.util.zip.ZipException: duplicate entry ….
ARouter有按组加载的机制,关于分组可以参考 6-1 部分,ARouter允许一个module中存在多个分组,但是不允许多个module中存在相同的分组,会导致映射文件冲突
Kotlin类中的字段无法注入如何解决?
首先,Kotlin中的字段是可以自动注入的,但是注入代码为了减少反射,使用的字段赋值的方式来注入的,Kotlin默认会生成set/get方法,并把属性设置为private 所以只要保证Kotlin中字段可见性不是private即可,简单解决可以在字段上添加 @JvmField
通过URL跳转之后,在intent中拿不到参数如何解决?
需要注意的是,如果不使用自动注入,那么可以不写 ARouter.getInstance().inject(this),但是需要取值的字段仍然需要标上 @Autowired 注解,因为 只有标上注解之后,ARouter才能知道以哪一种数据类型提取URL中的参数并放入Intent中,这样您才能在intent中获取到对应的参数
ARouter.getInstance().inject(this)
@Autowired
新增页面之后,无法跳转?
ARouter加载Dex中的映射文件会有一定耗时,所以ARouter会缓存映射文件,直到新版本升级(版本号或者versionCode变化),而如果是开发版本(ARouter.openDebug()), ARouter 每次启动都会重新加载映射文件,开发阶段一定要打开 Debug 功能
沟通和交流
钉钉交流群1
QQ 交流群1
QQ 交流群2
版权所有:中国计算机学会技术支持:开源发展技术委员会 京ICP备13000930号-9 京公网安备 11010802032778号
English
最新版本
Demo展示
Demo apk下载、Demo Gif
一、功能介绍
二、典型应用
三、基础功能
添加依赖和配置
添加注解
初始化SDK
发起路由操作
添加混淆规则(如果使用了Proguard)
使用 Gradle 插件实现路由表的自动加载 (可选)
可选使用,通过 ARouter 提供的注册插件进行路由表的自动加载(power by AutoRegister), 默认通过扫描 dex 的方式 进行加载通过 gradle 插件进行自动注册可以缩短初始化时间解决应用加固导致无法直接访问 dex 文件,初始化失败的问题,需要注意的是,该插件必须搭配 api 1.3.0 以上版本使用!
使用 IDE 插件导航到目标类 (可选)
在 Android Studio 插件市场中搜索
)
点击该图标,即可跳转到标识了代码中路径的目标类
ARouter Helper, 或者直接下载文档上方最新版本中列出的arouter-idea-pluginzip 安装包手动安装,安装后 插件无任何设置,可以在跳转代码的行首找到一个图标 (四、进阶用法
通过URL跳转
AndroidManifest.xml
解析参数 ``` java // 为每一个参数声明一个字段,并使用 @Autowired 标注 // URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象 @Route(path = “/test/activity”) public class Test1Activity extends Activity {
}
声明拦截器(拦截跳转过程,面向切面编程)
处理跳转结果
自定义全局降级策略
为目标页面声明更多信息
通过依赖注入解耦:服务管理(一) 暴露服务
通过依赖注入解耦:服务管理(二) 发现服务
预处理服务
动态注册路由信息 适用于部分插件化架构的App以及需要动态注册路由信息的场景,可以通过 ARouter 提供的接口实现动态注册 路由信息,目标页面和服务可以不标注 @Route 注解,注意:同一批次仅允许相同 group 的路由信息注册
五、更多功能
初始化中的其他设置
详细的API说明
获取原始的URI
重写跳转URL
生成路由文档
六、其他
路由中的分组概念
拦截器和服务的异同
旧版本gradle插件的配置方式
Kotlin项目中的配置方式
七、Q&A
“W/ARouter::: ARouter::No postcard![ ]”
这个Log正常的情况下也会打印出来,如果您的代码中没有实现DegradeService和PathReplaceService的话,因为ARouter本身的一些功能也依赖 自己提供的Service管理功能,ARouter在跳转的时候会尝试寻找用户实现的PathReplaceService,用于对路径进行重写(可选功能),所以如果您没有 实现这个服务的话,也会抛出这个日志
推荐在app中实现DegradeService、PathReplaceService
“W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group [xxx][ ]”
开启InstantRun之后无法跳转(高版本Gradle插件下无法跳转)?
因为开启InstantRun之后,很多类文件不会放在原本的dex中,需要单独去加载,ARouter默认不会去加载这些文件,因为安全原因,只有在开启了openDebug之后 ARouter才回去加载InstantRun产生的文件,所以在以上的情况下,需要在init之前调用openDebug
TransformException:java.util.zip.ZipException: duplicate entry ….
ARouter有按组加载的机制,关于分组可以参考 6-1 部分,ARouter允许一个module中存在多个分组,但是不允许多个module中存在相同的分组,会导致映射文件冲突
Kotlin类中的字段无法注入如何解决?
首先,Kotlin中的字段是可以自动注入的,但是注入代码为了减少反射,使用的字段赋值的方式来注入的,Kotlin默认会生成set/get方法,并把属性设置为private 所以只要保证Kotlin中字段可见性不是private即可,简单解决可以在字段上添加 @JvmField
通过URL跳转之后,在intent中拿不到参数如何解决?
需要注意的是,如果不使用自动注入,那么可以不写
ARouter.getInstance().inject(this),但是需要取值的字段仍然需要标上@Autowired注解,因为 只有标上注解之后,ARouter才能知道以哪一种数据类型提取URL中的参数并放入Intent中,这样您才能在intent中获取到对应的参数新增页面之后,无法跳转?
ARouter加载Dex中的映射文件会有一定耗时,所以ARouter会缓存映射文件,直到新版本升级(版本号或者versionCode变化),而如果是开发版本(ARouter.openDebug()), ARouter 每次启动都会重新加载映射文件,开发阶段一定要打开 Debug 功能
八、其他
沟通和交流
钉钉交流群1
QQ 交流群1
QQ 交流群2