• ARouter详解


    ARouter简介

    ARouter 是阿里巴巴开发的一款,页面路由工具库,旨在解决组件化或者模块之间界面跳转的问题。

    一、ARouter页面路由产生的背景

    1. Intent intent = new Intent(mContext, XxxActivity.class);
    2. intent.putExtra("key","value");
    3. startActivity(intent);
    4.         
    5. Intent intent = new Intent(mContext, XxxActivity.class);
    6. intent.putExtra("key","value");
    7. startActivityForResult(intent, 666);
    先看下上面的代码,在未使用ARouter路由框架之前是这样的。
    在Android开发中,最常见的常用功能就是页面的跳转了,我们经常遇到从浏览器或者其他App 跳转到自己App中某个页面的需求。不过就算是App内部简单的页面跳转,随着时间的推移,也会遇到一些问题:
    1. 集中式的URL管理:
    谈到集中式的管理,总是比较蛋疼,多人协同开发的时候,大家都去AndroidManifest.xml文件中,定义各种,然后使用隐式Intent实现跳转。最终发现AndroidManifest.xml中充斥着各种Schame、各种Path,需要经常解决Path冲突的问题。
    2. 可培植性较差:
    AndroidManifest.xml限制于xml格式,书写比较麻烦,配置较为复杂,可以进行自定义的东西也很少。
    3. 跳转过程中无法干预:
    直接通过Intent的方式跳转,跳转过程,开发者是无法进行干预的,一些面向切面的事情也难以实施。比如:登录、埋点的逻辑,在每个子页面中判断是很不合理的。
    4. 跨模块无法显示依赖:
    当App程序越来越大的时候我们会对App做水平拆分,按照业务拆分成多个子模块,模块与模块之间完全解藕,通过打包流程控制App功能,这样更有利于多人协作开发,逻辑相互不干扰。
    这种情况下,就只能依赖隐式Intent实现页面间的跳转了 ,书写麻烦,成功与否也难以控制。
    为了解决上述问题,我们需要一款能够解藕、简单、功能全面、定制性强、支持拦截跳转过程的路由组件,为此ARouter便诞生了。

    二、原生路由方案的缺点

    1,显式:直接的类依赖,耦合严重
    2,隐式:会在AndroidManifest.xml中进行集中式管理,写作困难。
    3,AndroidManifest.xml扩展性差。
    4,跳转过程无法控制。
    5,失败无法降级。

    三、ARouter的优势

    1,使用注解,实现了映射关系自动注册 与 分布式路由管理
    2,编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
    3,映射关系按组分类,多级管理,按需初始化。
    4,灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity() 一旦失败会抛出异常。
    5,自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登陆判断,和埋点处理。
    6,支持依赖注入,可单独作为依赖注入框架使用,从而实现 跨模块API调用。
    7,支持直接解析标准Url进行跳转,并自动注入参数到目标页面中。
    8,支持多模块使用,支持组件化开发。

    四、ARouter功能介绍

    1,支持直接解析URL进行跳转、参数将按照类型解析到Bundle,支持Java基本类型。
    2,支持应用内的标准页面跳转,API接近Android原生接口。
    3,支持模块工程,允许分别打包,包结构符合Android包规范即可。
    4,支持组件化开发。
    5,支持拦截跳转过程,在跳转过程中自定义拦截逻辑,自定义拦截顺序。
    6,支持服务托管,可以通过ByName、ByType这两种方式获取服务实例,方便面向接口开发 与 跨模块调用解藕。
    7,支持 映射关系按组分类、多级管理,按需初始化,减少内存占用,提高查询效率。
    8,支持用户指定全局降级策略。
    9,支持获取单次跳转结果。
    10,支持丰富的API和可定制化。
    11,被ARouter管理的页面、拦截器、服务等均无需主动注册到ARouter,是被动发现的。
    12,支持Android N推出的Jack编译链。
    13,支持获取Fragment。
    14,支持MultiDex (Google方案)。
    15,支持多种方式配置专场动画。
    16,支持Kotlin及混编。
    17,支持第三方App加固。
    18,支持生成路由文档。
    19,支持InstantRun热运行。

    五、ARouter的典型应用场景

    1,从APP外部URL映射到App内的某个页面,以及参数传递与解析。
    2,App内的跨模块页面之间的跳转,实现模块间解藕。
    3,拦截跳转过程,处理登陆、埋点等逻辑。
    4,跨模块API调用,模块之间解藕 (注册ARouter服务的形式,通过接口相互调用)。
    六、ARouter路由导航流程图

    七、ARouter基本配置

    1、添加依赖和配置
    在app.gradle 和 每个module.gradle 里面都需要进行配置
    1. android {
    2.     defaultConfig {
    3.         ...
    4.         javaCompileOptions {
    5.             annotationProcessorOptions {
    6.                 arguments = [AROUTER_MODULE_NAME: project.getName()]
    7.             }
    8.         }
    9.     }
    10. }
    11. dependencies {
    12.     // 替换成最新版本, 需要注意的是api
    13.     // 要与compiler匹配使用,均使用最新版可以保证兼容
    14.     compile 'com.alibaba:arouter-api:x.x.x'
    15.     annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
    16. }
    2、使用Gradle插件,实现路由列表的自动加载 (可选)
    1. apply plugin: 'com.alibaba.arouter'
    2. buildscript {
    3.     repositories {
    4.         jcenter()
    5.     }
    6.     dependencies {
    7.         classpath "com.alibaba:arouter-register:?"
    8.     }
    9. }
    3、添加混淆配置
    1. -keep public class com.alibaba.android.arouter.routes.**{*;}
    2. -keep public class com.alibaba.android.arouter.facade.**{*;}
    3. -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
    4. # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
    5. -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
    6. # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
    7. # -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
    4、生成路由文档
    1. // 更新 build.gradle, 添加参数 AROUTER_GENERATE_DOC = enable
    2. // 生成的文档路径 : build/generated/source/apt/
    3. // (debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
    4. android {
    5.     defaultConfig {
    6.         ...
    7.         javaCompileOptions {
    8.             annotationProcessorOptions {
    9.                 arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
    10.             }
    11.         }
    12.     }
    13. }

    八、ARouter基本功能的使用

    1、ARouter初始化
    1. public static void initRouter(Application application) {
    2.     if (BuildConfig.DEBUG) {   // 这两行必须写在init之前,否则这些配置在init过程中将无效
    3.         ARouter.openLog();     // 打印日志
    4.         // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
    5.         ARouter.openDebug();   
    6.     }
    7.     ARouter.init(application); // 尽可能早,推荐在Application中初始化
    8. }
    2、为目标Activity页面 添加注解
    1. // 在支持路由的页面上添加注解(必选)
    2. // 这里的路径需要注意的是至少需要有两级,/xx/xx
    3. @Route(path = "/test/activity")
    4. public class YourActivity extend Activity {
    5.     ...
    6. }
    3,发起路由跳转
    1. // 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
    2. ARouter.getInstance().build("/test/activity").navigation();
    3. // 2. 跳转并携带参数
    4. ARouter.getInstance().build("/test/1")
    5.             .withLong("key1", 666L)
    6.             .withString("key3", "888")
    7.             .withObject("key4", new Test("Jack", "Rose"))
    8.             .navigation();
    4,跳转到其他moudle
    ARouter跳转到其他moudle时,需要注意的是,moudle中的目标Activity或者service服务类上使用@Route(path = “xx/xxx”) 与 app moudle中使用的@Route里的path路径不能相同,否则会报错,找不到索引。
    步骤一:
    鼠标右键项目 -> New -> Module -> 直至创建完成;
    步骤二:
    注释掉module中build.gradle里面相应的配置,如:(applicationId 和apply plugin等)
    同时引入ARouter中的依赖库,同app module;
    步骤三:
    删除module中AndoridManifest.xml中不需要的配置;
    步骤四:
    为module中的目标页面配置路由
    步骤五:
    实现module之间的跳转。

    九、ARouter注解

    注解共有三种:@Route、@AutoWired、@Interceptor
    1、注解路由 @Route
    用于注解一个目标Activity或者 一个目标服务类。
    参数包括@Route (path = “/self/test_activity”, group = “default”, name = “这是一个测试页面”,)
    1. /**
    2.  * Path of route
    3.  */
    4.  String path();
    5. /**
    6.  * Used to merger routes, the group name must be use the common words !!!
    7.  */
    8.  String group() default "";
    9. /**
    10.  * Name of route, used to generate javadoc.
    11.  */
    12.  String name() default "";
    13. /**
    14.  * Extra data, 可以被用户设置
    15.  * Ps. U should use the integer num sign the switch, by bits. 10001010101010
    16.  */
    17.  int extras() default Integer.MIN_VALUE;
    18. /**
    19.  * The priority of route.
    20.  * 值越小,优先级越高
    21.  */
    22.  int priority() default -1;
    2、注解字段 @AutoWired
    接收参数时,不要忘记 进行注册 ARouter.getInstance().inject(this);   
    用户注解字段参数,需要配合ARouter.getInstance().inject(this)配置。
    2.1 传递参数
    1. @OnClick(R2.id.enter_activity_inject)
    2. public void onClick9() {
    3.     TestSerializable testSerializable = new TestSerializable("Titanic", 555);
    4.     TestParcelable testParcelable = new TestParcelable("jack", 666);
    5.     TestObj testObj = new TestObj("Rose", 777);
    6.     List objList = new ArrayList<>();
    7.     objList.add(testObj);
    8.     Map> map = new HashMap<>();
    9.     map.put("testMap", objList);
    10.     ARouter.getInstance().build("/self/activity_inject")
    11.             .withString("name", "老王")
    12.             .withInt("age", 18)
    13.             .withBoolean("boy", true)
    14.             .withLong("high", 180)
    15.             .withString("url", "https://a.b.c")
    16.             .withSerializable("ser", testSerializable)
    17.             .withParcelable("pac", testParcelable)
    18.             .withObject("obj", testObj)
    19.             .withObject("objList", objList)
    20.             .withObject("map", map)
    21.             .navigation();
    22. }
    2.2 接收参数值
    1. @Autowired(desc = "姓名")
    2. String name = "jack";
    3. @Autowired
    4. int age = 10;
    5. @Autowired
    6. int height = 175;
    7. //通过name来映射URL中的不同参数,及girl是传递参数时的字段名,在当前,可以改变字段名称,使用boy替换girl。
    8. @Autowired(name = "boy", required = true)
    9. boolean girl;
    10. @Autowired
    11. char ch = 'A';
    12. @Autowired
    13. float fl = 12.00f;
    14. @Autowired
    15. double dou = 12.01d;
    16. @Autowired
    17. TestSerializable ser;
    18. @Autowired
    19. TestParcelable pac;
    20. // URL中不能传递Parcelable类型数据,通过ARouter api可以传递Parcelable对象
    21. // 支持解析自定义对象,URL中使用json传递
    22. @Autowired
    23. TestObj obj;
    24. //使用withObject传递实现Serializable接口ArrayList和HashMap的时候,
    25. //接收对象的地方不能标注为具体实现的类型,否则会影响序列化中类型的判断,
    26. //应该标注为:List 和 Map.   
    27. @Autowired
    28. List objList;
    29. @Autowired
    30. Map> map;
    31. @Autowired
    32. String url;
    33. //获取服务实例的第三种方式 -- 通过注解获取
    34. @Autowired
    35. HelloService helloService;
    自定义类型参数--用于网页传递json数据
    网页传递json参数到App页面,然后ARouter帮助我们自动转换为我们的自定义数据类
    1. // 如果需要传递自定义对象,新建一个类(并非自定义对象类),
    2. // 然后实现 SerializationService, 并使用@Route注解标注(方便用户自行选择序列化方式),例如
    3. @Route(path = "/self_service/json")
    4. public class JsonServiceImpl implements SerializationService {
    5.     @Override
    6.     public void init(Context context) {
    7.     }
    8.     
    9.     //json字符串转换为对象
    10.     @Override
    11.     public T json2Object(String text, Class clazz) {
    12.         return JSON.parseObject(text, clazz);
    13.     }
    14.     //自定义对象转换为json字符串
    15.     @Override
    16.     public String object2Json(Object instance) {
    17.         return JSON.toJSONString(instance);
    18.     }
    19.     @Override
    20.     public T parseObject(String input, Type clazz) {
    21.         return JSON.parseObject(input, clazz);
    22.     }
    23. }
    网页链接为:
    1. <p>
    2. <a href="arouter://m.aliyun.com/test/activity1?name=tpnet&age=21&sex=true&obj=%7B%22name%22:%22jack%22,%22id%22:666%7D">
    3. 带json自定义对象
    4. a>
    5. p>
    自定义对象为:
    1. public class TestObj() {
    2.     //这里变量名称对应Url的json的key
    3.     public String name;     
    4.     public int id;
    5. }
    然后在目标Activity中接收对象:
    1. @AutoWired
    2. String name;
    3. @AutoWired(name = “id”)
    4. int ID;
    3、拦截器 @Interceptor (拦截跳转过程,面向切面编程)
    拦截器注解,在跳转的过程中进行拦截,方便添加某些逻辑处理。
    1. /**
    2. * 自定义拦截器
    3. * priority: 为拦截器的优先级,其值越小,优先级越高;多个拦截器时候有用;
    4. */
    5. @Interceptor(priority = 7, name = "测试用拦截器")
    6. public class Test1Interceptor implements IInterceptor {
    7.     Context mContext;
    8.     /**
    9.      * process:拦截器操作
    10.      * @param postcard 数据
    11.      * @param callback 回调
    12.      */
    13.     @Override
    14.     public void process(final Postcard postcard, final InterceptorCallback callback) {
    15.         if ("/self/activity_page".equals(postcard.getPath())) {
    16.             // 这里的弹窗仅做举例,代码写法不具有可参考价值
    17.             final AlertDialog.Builder ab = new AlertDialog.Builder(ARouterHomeActivity.getThis());
    18.             ab.setCancelable(false);
    19.             ab.setTitle("温馨提醒");
    20.             ab.setMessage(
    21.                 "想要跳转到ARouterPageActivity么?(触发了\"/inter/test\"拦截器,拦截了本次跳转)”
    22.                );
    23.             ab.setNegativeButton("继续", new DialogInterface.OnClickListener() {
    24.                 @Override
    25.                 public void onClick(DialogInterface dialog, int which) {
    26.                     //继续执行跳转,不做额外的拦截操作
    27.                     callback.onContinue(postcard);
    28.                 }
    29.             });
    30.             ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
    31.                 @Override
    32.                 public void onClick(DialogInterface dialog, int which) {
    33.                     //直接终止跳转
    34.                     callback.onInterrupt(null);
    35.                     callback.onInterrupt(new RuntimeException("出现跳转终止的异常"));
    36.                 }
    37.             });
    38.             ab.setPositiveButton("加参数", new DialogInterface.OnClickListener() {
    39.                 @Override
    40.                 public void onClick(DialogInterface dialog, int which) {
    41.                     postcard.withString("extra", "这拦截器中附加的参数");
    42.                     callback.onContinue(postcard);
    43.                 }
    44.             });
    45.             MainLooper.runOnUiThread(new Runnable() {
    46.                 @Override
    47.                 public void run() {
    48.                     ab.create().show();
    49.                 }
    50.             });
    51.         } else {
    52.             callback.onContinue(postcard);
    53.         }
    54.     }
    55.     /**
    56.      * 拦截器的初始化,会在sdk初始化的时候调用该方法,且仅会调用一次;
    57.      */
    58.     @Override
    59.     public void init(Context context) {
    60.         mContext = context;
    61.         Log.e("testService", Test1Interceptor.class.getName() + " has init.");
    62.     }
    63. }

    十、服务

    并不是说的四大组件中的service服务,而是一个普通对象。
    ARouter中的服务,可以通过注入的方式解藕,实现moudle模块之间的API调用。
    1、服务管理——暴露服务
    1. // 声明接口,其他组件通过接口来调用服务
    2. public interface HelloService implements IProvider {
    3.     String sayHello(String name);
    4. }
    5. // 实现接口
    6. @Route(path = "/yourservicegroupname/hello", name = "测试服务")
    7. public class HelloServiceImpl implements HelloService {
    8.     @Override
    9.     public String sayHello(String name) {
    10.     return "hello, " + name;
    11.     }
    12.     @Override
    13.     public void init(Context context) {
    14.     }
    15. }
    2、服务管理——发现服务
    1. public class Test {
    2.     //注解的方式获取服务
    3.     @Autowired
    4.     HelloService helloService;
    5.     
    6.     //注解的方式获取服务—重命名服务
    7.     @Autowired(name = "/yourservicegroupname/hello")
    8.     HelloService helloService2;
    9.     //其他方式获取服务
    10.     HelloService helloService3;
    11.     HelloService helloService4;
    12.     public Test() {
    13.        //这里是重点********** 出过错,忘记在使用 Aroter的组件(Activity)中注册了
    14.       ARouter.getInstance().inject(this);
    15.     }
    16.     public void testService() {
    17.     // 1. (推荐)使用依赖注入的方式发现服务,通过注解标注字段,即可使用,无需主动获取
    18.     // Autowired注解中标注name之后,将会使用byName的方式注入对应的字段,
    19.     // 不设置name属性,会默认使用byType的方式发现服务
    20.     // (当同一接口有多个实现的时候,必须使用byName的方式发现服务)
    21.     helloService.sayHello("Vergil");
    22.     helloService2.sayHello("Vergil");
    23.     // 2. 使用依赖查找的方式发现服务,主动去发现服务并使用,下面两种方式分别是byName和byType
    24.     helloService3 = ARouter.getInstance().navigation(HelloService.class);
    25.     helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
    26.     helloService3.sayHello("Vergil");
    27.     helloService4.sayHello("Vergil");
    28.     }
    29. }

    3、预处理服务

    1. // 实现 PretreatmentService 接口,并加上一个Path内容任意的注解即可
    2. @Route(path = "/xxx/xxx")
    3. public class PretreatmentServiceImpl implements PretreatmentService {
    4.     @Override
    5.     public boolean onPretreatment(Context context, Postcard postcard) {
    6.         // 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可
    7.     }
    8.     @Override
    9.     public void init(Context context) {
    10.     }
    11. }

    4、重写跳转Url的服务

    即会拦截跳转时的Path 和 Url (app内部跳转,以及页面跳转到app页面) ,拦截之后可以重新定义跳转路径,改变跳转的目标页面。
    1. @Route(path = "/self/repace_pathorurl_service")
    2. public class ReplacePathOrUrlServiceImpl implements PathReplaceService {
    3.     Context mContext;
    4.     /**
    5.      * void 重写跳转路径path
    6.      * @param path 原始的跳转路径
    7.      * @return 按照一定的规则处理之后返回处理后的结果
    8.      */
    9.     @Override
    10.     public String forString(String path) {
    11.         return path;
    12.     }
    13.     /**
    14.      * void 重写跳转Uri
    15.      * @param uri 原始的跳转url
    16.      * @return 按照一定的规则处理之后返回处理后的结果
    17.      */
    18.     @Override
    19.     public Uri forUri(Uri uri) {
    20.         return uri;
    21.     }
    22.     @Override
    23.     public void init(Context context) {
    24.         this.mContext = context;
    25.     }

    十一、为目标页面,声明更多参数

    // 我们经常需要在目标页面中配置一些属性,比方说"是否需要登陆”之类的,
    // 可以通过 Route 注解中的 extras 属性进行扩展,这个属性是一个 int值,
    // 换句话说,单个int有4字节,也就是32位,可以配置32个开关
    // 剩下的可以自行发挥,通过字节操作可以标识32个开关,通过开关标记目标页面的一些属性,
    // 在拦截器中可以拿到这个标记进行业务逻辑判断
    @Route(path = "/test/activity", extras = Consts.XXXX)

    十二,降级策略

    通俗的讲,降级就是,在使用ARouter实现跳转的时候,如果发生错误,而进行相应的逻辑处理,
    不至于导致崩溃现象。

    十三、API总结

    1. //构建标准的路由请求
    2. ARouter.getInstance().build("/home/main").navigation();
    3. //构建标准的路由请求,并指定分组
    4. ARouter.getInstance().build("/home/main", "ap").navigation();
    5. //构建标准的路由请求,通过Uri直接解析
    6. Uri uri;
    7. ARouter.getInstance().build(uri).navigation();
    8. //构建标准的路由请求,startActivityForResult
    9. //navigation的第一个参数必须是Activity,第二个参数则是RequestCode
    10. ARouter.getInstance().build("/home/main", "ap").navigation(this, 5);
    11. //直接传递Bundle
    12. Bundle params = new Bundle();
    13. ARouter.getInstance()
    14.     .build("/home/main")
    15.     .with(params)
    16.     .navigation();
    17. //指定Flag
    18. ARouter.getInstance()
    19.     .build("/home/main")
    20.     .withFlags();
    21.     .navigation();
    22. //获取Fragment
    23. Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
    24. //对象传递
    25. ARouter.getInstance()
    26.     .withObject("key", new TestObj("Jack", "Rose"))
    27.     .navigation();
    28. //觉得接口不够多,可以直接拿出Bundle赋值
    29. ARouter.getInstance()
    30.         .build("/home/main")
    31.         .getExtra();
    32. //转场动画(常规方式)
    33. ARouter.getInstance()
    34.     .build("/test/activity2")
    35.     .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom)
    36.     .navigation(this);
    37. //转场动画(API16+)
    38. ActivityOptionsCompat compat = ActivityOptionsCompat.
    39.     makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);
    40.     //ps. makeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前Activity
    41. ARouter.getInstance()
    42.     .build("/test/activity2")
    43.     .withOptionsCompat(compat)
    44.     .navigation();
    45. //使用绿色通道(跳过所有的拦截器)
    46. ARouter.getInstance().build("/home/main").greenChannel().navigation();
    47. //使用自己的日志工具打印日志
    48. ARouter.setLogger();
    49. //获取原始的URI
    50. String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);
    51. //关闭ARouter
    52. ARouter.getInstance().destroy();

    十四、使用IDE插件,导航到目标类

    在 Android Studio 插件市场中搜索 ARouter Helper, 或者直接下载文档上方 最新版本 中列出的 arouter-idea-plugin zip 安装包手动安装,安装后 插件无任何设置,可以在跳转代码的行首找到一个图标 () 点击该图标,即可跳转到标识了代码中路径的目标类
    手动安装过程:
    1,Preferences —> Plugins -> Install plugin from disk -> 重启IDE

    ARouter原理

  • 相关阅读:
    springboot docker
    使用C++的CCF-CSP满分解决方案 202012-2 期末预测之最佳阈值
    【HMS Core】【SDK集成】Android Studio中Gradle Version7.1+以上版本如何集成agcp插件?
    LCT 的基本操作
    python Matplotlib Tkinter-->tab切换3
    华为机试 HJ36 字符串加密【Java实现】
    LCR 175.计算二叉树的深度
    递归查找树形状结(利用steam流的方式)leval值标明
    [漏洞复现]Text4shell(CVE-2022-42889)
    js 数组移除指定元素【函数封装】(含对象数组移除指定元素)
  • 原文地址:https://blog.csdn.net/m0_49508485/article/details/127786294