ARouter 不一样的简介

一、前言

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 框架,其本身还涉及到其他的技术,这些将在后面的源码分析文章中来分享。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352