美文网首页编程
ARouter 不一样的简介

ARouter 不一样的简介

作者: 仰简 | 来源:发表于2020-06-14 01:23 被阅读0次

    一、前言

    ARouter 是阿里巴巴出品的一款优秀的路由以及依赖注入解决方案。其可用于模块化的改造,解除模块之间的强依赖。通过辅以简单的依赖构造脚本,就可以实现完全隔离各个模块之间的依赖。

    1. 路由特性

    通过 ARouter 来管理路由,可以通过设置路径以及 URI 的方式来路由到目标 Activity。也就免去了我们通过 startActivity 的方式来启动一个 Activity,这有利于模块之间的隔离。

    而在路由方面,除了可以携带参数之外,其还有一个更重要的功能便是拦截器。通过拦截器,我们可以很容易也很优雅的实现界面的授权跳转。如用户未登录则可以让其先跳登录,而不用通过写的满天飞的 if/else 的方式判断是否登录,是否可跳转。

    2.依赖注入特性

    依赖注入特性,是通过实现其接口 IProvider 来实现一个我们的服务类,当然,本质上就是一个普通的 Java 类。然后我们可以通过 ByType,也就是目标类,如 xx.class 来获取该类的实例。也可以通过 ByName ,也就是目标路径,如 "/service/HelloService" 来获取类的实例。

    依赖注入的这个特性,天然就是为了模块间的解耦而设计的。

    二、简单的路由

    1.定义路由

    @Route(path = "/test/activity1", name = "测试用 Activity")
    public class Test1Activity extends AppCompatActivity {
        @Autowired(desc = "姓名")
        String name;
        @Autowired
        int age;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_test1);
    
            ARouter.getInstance().inject(this);
        }
    }
    
    1. 使用注解 @Route 定义路由的路径
    2. 使用 @Autowired 定义要注解的参数
    3. 使用 inject() 方法注解对象,其主要目的是为了给参数赋值

    2.路由

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

    获取到 ARouter 的实例,然后通过目标路径 build 出一个 PostCard,再然后调用 navigation() 方法就可以路由到目标 Activity 了。当然,如果目标 Activity 有在 AndroidManifest.xml 通过 <intent-filter></intent-filter> 注册有 scheme、host 的话,也可通过 URI 来进行路由。示例如下:
    注册

     <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>
    

    路由

    Uri testUriMix = Uri.parse("arouter://m.aliyun.com/test/activity1");
                    ARouter.getInstance().build(testUriMix)
                            .withString("key1", "value1")
                            .navigation();
    

    3.路由的秘密

    这个路由看起来好像有点神秘的样子。那我们需要去看 ARouter 的源码吗?当然,如果你有时间和能力也不是不可以,但我觉得在这之前,应该还有更简单的方式来窥探一二。
    如下,在 build.gradle 中如果配置了 ARouter 的 annotationProcessorOptions,

    javaCompileOptions {
                annotationProcessorOptions {
                    arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
                }
            }
    

    则会在 build/generated/source/apt/debug/com/alibaba/arouter/ 下面生成相应的 Java 文件。如下图所示。


    image.png

    在这些生成的 Java 文件中,大概可以分为这么 3 类。

    1. 根路由
    public class ARouter$$Root$$app implements IRouteRoot {
      @Override
      public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        routes.put("test", ARouter$$Group$$test.class);
        routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
      }
    }
    

    根据实现了 IRouteRoot 类,其以 group 为维度对路由表进行分类。

    1. group 路由表
    public class ARouter$$Group$$test implements IRouteGroup {
      @Override
      public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
        atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
        atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
        atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
        atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("obj", 11); put("name", 8); }}, -1, -2147483648));
        atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
      }
    }
    

    路由表以 path 为 key ,并以目标 Activity、目标路径、参数等元素构造 RouteMeta 为 Value。对于服务类,也是一样的。下面讲服务类时也会提到。

    1. 参数注入器 Syringe
    public class Test1Activity$$ARouter$$Autowired implements ISyringe {
      private SerializationService serializationService;
      @Override
      public void inject(Object target) {
        serializationService = ARouter.getInstance().navigation(SerializationService.class);
        Test1Activity substitute = (Test1Activity)target;
        substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
        substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
        ......
      }
    }
    

    其实参数流入就是从 Intent 里面拿参数咯,如果是 Fragment 那就是从 Bundle 里拿,这里就不展开了。

    看到这里,如果你的 APT 的技术储备,那即使不看源码,也应该对路由的本质有所了解了吧。

    三、拦截器

    1. 实现一个自己的拦截器

    @Interceptor(priority = 7)
    public class Test1Interceptor implements IInterceptor {
        Context mContext;
        @Override
        public void process(final Postcard postcard, final InterceptorCallback callback) {
            if (isLogin) {
                callback.onContinue(postcard);
            } else {
                callback.onInterrupt(null);
            }
        }
        @Override
        public void init(Context context) {
            mContext = context;
            Log.e("testService", Test1Interceptor.class.getName() + " has init.");
        }
    }
    

    如上,实现拦截器的 3 要素:

    1. 使用注解 @Interceptor,同时还可以定义优先级,优先级越高,就越被先执行。
    2. 实现 IInterceptor 接口
    3. 重载 process() 方法。process() 方法中,callback.onContinue() 和 callback.onInterrupt() 必须调用其中一个。init() 方法可选,其只在初始化执行一次。

    2.拦截器的秘密

    public class ARouter$$Interceptors$$app implements IInterceptorGroup {
      @Override
      public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
        interceptors.put(7, Test1Interceptor.class);
      }
    }
    

    拦截器会以优先级做为维度,也就是 key ,建立一个拦截器的表。

    四、服务类

    1. 实现自己的服务类

    public interface HelloService extends IProvider {
        void sayHello(String name);
    }
    @Route(path = "/yourservicegroupname/hello")
    public class HelloServiceImpl implements HelloService {
        Context mContext;
    
        @Override
        public void sayHello(String name) {
            Toast.makeText(mContext, "Hello " + name, Toast.LENGTH_SHORT).show();
        }
        @Override
        public void init(Context context) {
            mContext = context;
        }
    }
    
    1. 定义一个服务接口,继承自 IProvider。
    2. 实现服务接口,并且实现具体的方法。

    2. 路由服务类

    ((HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation()).sayHello("mike");
    

    和路由到一个目标 Activity 的使用方法一致。

    3. 服务类的秘密

    public class ARouter$$Group$$yourservicegroupname implements IRouteGroup {
      @Override
      public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/yourservicegroupname/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
        atlas.put("/yourservicegroupname/json", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
        atlas.put("/yourservicegroupname/single", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
      }
    }
    

    看上去和 Activity 的路由差不多,但是其中有一个很小的区别,那就是 RouteType.PROVIDER。而对于 Activity 则是 RouteType.ACTIVITY。想必其内部应该是通过这个 RouteType 来区分不同的目标对象的。

    五、总结

    文章大致介绍了 ARouter 的用途,对路由、拦截器以及服务类相对比较详细的介绍。为了避免深入源码的细节,这里从其产生的 Java 文件为视口,从一定程度上揭秘了 ARouter 的大致原理。当然,ARouter 框架,其本身还涉及到其他的技术,这些将在后面的源码分析文章中来分享。

    相关文章

      网友评论

        本文标题:ARouter 不一样的简介

        本文链接:https://www.haomeiwen.com/subject/ykqjahtx.html