ARouter 是阿里巴巴出品的一款优秀的路由以及依赖注入解决方案。其可用于模块化的改造,解除模块之间的强依赖。通过辅以简单的依赖构造脚本,就可以实现完全隔离各个模块之间的依赖。
通过 ARouter 来管理路由,可以通过设置路径以及 URI 的方式来路由到目标 Activity。也就免去了我们通过 startActivity 的方式来启动一个 Activity,这有利于模块之间的隔离。
而在路由方面,除了可以携带参数之外,其还有一个更重要的功能便是拦截器。通过拦截器,我们可以很容易也很优雅的实现界面的授权跳转。如用户未登录则可以让其先跳登录,而不用通过写的满天飞的 if/else 的方式判断是否登录,是否可跳转。
依赖注入特性,是通过实现其接口 IProvider 来实现一个我们的服务类,当然,本质上就是一个普通的 Java 类。然后我们可以通过 ByType,也就是目标类,如 xx.class 来获取该类的实例。也可以通过 ByName ,也就是目标路径,如 “/service/HelloService” 来获取类的实例。
依赖注入的这个特性,天然就是为了模块间的解耦而设计的。
@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); } }
ARouter.getInstance() .build("/test/activity1") .navigation();
获取到 ARouter 的实例,然后通过目标路径 build 出一个 PostCard,再然后调用 navigation() 方法就可以路由到目标 Activity 了。当然,如果目标 Activity 有在 AndroidManifest.xml 通过 注册有 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();
这个路由看起来好像有点神秘的样子。那我们需要去看 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 文件。如下图所示。
在这些生成的 Java 文件中,大概可以分为这么 3 类。
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 为维度对路由表进行分类。
2) 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。对于服务类,也是一样的。下面讲服务类时也会提到。
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 的技术储备,那即使不看源码,也应该对路由的本质有所了解了吧。
@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 要素:
public class ARouter$$Interceptors$$app implements IInterceptorGroup { @Override public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) { interceptors.put(7, Test1Interceptor.class); } }
拦截器会以优先级做为维度,也就是 key ,建立一个拦截器的表。
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; } }
((HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation()).sayHello("mike");
和路由到一个目标 Activity 的使用方法一致。
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 框架,其本身还涉及到其他的技术,这些将在后面的源码分析文章中来分享。