Android ActivityRouter 源码解析

Header

在如今的 Android 组件化开发中,一款好的路由框架是不可或缺的。比如目前阿里的 ARouter 、美团的 WMRouter 等。路由框架可以降低 Activity 之间的耦合,从而在不需要关心目标 Activity 的具体实现类, 利用协议完成跳转。

ActivityRouter使用方法

在AndroidManifest.xml配置

<activity
    android:name="com.github.mzule.activityrouter.router.RouterActivity"
    android:theme="@android:style/Theme.NoDisplay">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="mzule" /><!--改成自己的scheme-->
    </intent-filter>
</activity>

在需要配置的Activity上添加注解

@Router("main")
public class MainActivity extends Activity {
    ...
}

想要跳转到 MainActivity ,只要调用以下代码即可

Routers.open(context, "mzule://main")

如果想用 @Router 来调用方法

@Router("logout")
public static void logout(Context context, Bundle bundle) {
    Toast.makeText(context, "logout", Toast.LENGTH_SHORT).show();
}

源码解析

ActivityRouter 工程的结构如下

ActivityRouter

  • activityrouter: 路由跳转的具体实现代码
  • annotaition: 路由注解
  • app: 路由 demo
  • app_module: 路由 demo module
  • compiler: 注解处理
  • stub: 壳 module

annotation

先来看看 Router 的注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.CLASS)
public @interface Router {

String[] value();

String[] stringParams() default "";

String[] intParams() default "";

String[] longParams() default "";

String[] booleanParams() default "";

String[] shortParams() default "";

String[] floatParams() default "";

String[] doubleParams() default "";

String[] byteParams() default "";

String[] charParams() default "";

String[] transfer() default "";
}

@Router 定义了该 Activity 路由的名字以及一些参数,这里可以注意到 @Retention 是 CLASS ,所以后面肯定在编译期间利用 Processor 来解析 @Router 生成路由表的。

另外,看到 @Target 是 ElementType.TYPE 和 ElementType.METHOD ,其实 @Router 除了跳转 Activity 之外,还有一个功能就是可以执行方法,只要在方法加上 @Router 即可。

路由表的生成源码我们到后面再讲,先来看看有了协议之后,Routers 是如何实现跳转 Activity 的。

activityrouter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Routers {

...

public static boolean open(Context context, String url) {
return open(context, Uri.parse(url));
}

public static boolean open(Context context, String url, RouterCallback callback) {
return open(context, Uri.parse(url), callback);
}

public static boolean open(Context context, Uri uri) {
return open(context, uri, getGlobalCallback(context));
}

public static boolean open(Context context, Uri uri, RouterCallback callback) {
return open(context, uri, -1, callback);
}

public static boolean openForResult(Activity activity, String url, int requestCode) {
return openForResult(activity, Uri.parse(url), requestCode);
}

public static boolean openForResult(Activity activity, String url, int requestCode, RouterCallback callback) {
return openForResult(activity, Uri.parse(url), requestCode, callback);
}

public static boolean openForResult(Activity activity, Uri uri, int requestCode) {
return openForResult(activity, uri, requestCode, getGlobalCallback(activity));
}

public static boolean openForResult(Activity activity, Uri uri, int requestCode, RouterCallback callback) {
return open(activity, uri, requestCode, callback);
}

...

}

可以看到不同的 open openForResult 方法重载,最后都是调用了 open(Context context, Uri uri, int requestCode, RouterCallback callback) 。那么接着跟踪:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private static boolean open(Context context, Uri uri, int requestCode, RouterCallback callback) {
boolean success = false;
// 如果有 callback 在跳转前回调
if (callback != null) {
if (callback.beforeOpen(context, uri)) {
return false;
}
}
// 执行路由跳转
try {
success = doOpen(context, uri, requestCode);
} catch (Throwable e) {
e.printStackTrace();
if (callback != null) {
// 错误回调
callback.error(context, uri, e);
}
}
// 成功或失败回调
if (callback != null) {
if (success) {
callback.afterOpen(context, uri);
} else {
callback.notFound(context, uri);
}
}
return success;
}

open 方法中有很多都是不同状态下 callback 的回调,真正跳转的逻辑放在了 doOpen 方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private static boolean doOpen(Context context, Uri uri, int requestCode) {
// 如果没有初始化的话,调用 Router.init 进行初始化路由表
initIfNeed();
// 解析 uri 得到对应的 path
Path path = Path.create(uri);
// 根据 path 去查找与之对应匹配的 mapping ,然后实现跳转
for (Mapping mapping : mappings) {
if (mapping.match(path)) {
// 如果 activity 是空的,就说明是执行方法的
if (mapping.getActivity() == null) {
mapping.getMethod().invoke(context, mapping.parseExtras(uri));
return true;
}
// 否则就是利用 intent 来跳转 activity
Intent intent = new Intent(context, mapping.getActivity());
intent.putExtras(mapping.parseExtras(uri));
intent.putExtra(KEY_RAW_URL, uri.toString());
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (requestCode >= 0) {
if (context instanceof Activity) {
((Activity) context).startActivityForResult(intent, requestCode);
} else {
throw new RuntimeException("can not startActivityForResult context " + context);
}
} else {
context.startActivity(intent);
}
return true;
}
}
return false;
}

我们一步步来分析 doOpen 中的具体步骤。先从 Path path = Path.create(uri); 开始看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static Path create(Uri uri) {
Path path = new Path(uri.getScheme().concat("://"));
String urlPath = uri.getPath();
if (urlPath == null) {
urlPath = "";
}
if (urlPath.endsWith("/")) {
urlPath = urlPath.substring(0, urlPath.length() - 1);
}
parse(path, uri.getHost() + urlPath);
return path;
}

private static void parse(Path scheme, String s) {
String[] components = s.split("/");
Path curPath = scheme;
for (String component : components) {
Path temp = new Path(component);
curPath.next = temp;
curPath = temp;
}
}

上面的代码看完可能会感觉很绕,简单地解释下。上面这段代码主要做的事情就是把传入的 uri 解析,生成了一个 Path 对象。该 Path 对象主要包含了 uri 中的 scheme 、host 、path 这三部分,利用单链表的特点把这三部分串连起来。这个 Path 也就是后面用来匹配路由表用的。

关于 uri 的 scheme 、 host 等,在这里就简单地普及下。

比如现在有一个 uri

mzule://main/home/login?username=tom

这个 uri 就可以分解为

scheme :mzule ,就是 “://” 前面的字符串
host :main ,“://” 后面的字符串
path :home 和 login 都属于 path,就是 “/” 与 “/” 之间的字符串
query :参数,可以理解成键值对,多个之间用 & 连接。获取 username 这个参数,对应的值就是 tom

生成好了 Path 之后,就是遍历路由表进行匹配了。

所谓的路由表其实就是一个 List

private static List<Mapping> mappings = new ArrayList<>();

在调用 RouterInit.init 时候会把路由数据添加到 List 中。准确的说, RouterInit.init 中调用了 Router.map 方法来实现添加的。

1
2
3
static void map(String format, Class<? extends Activity> activity, MethodInvoker method, ExtraTypes extraTypes) {
mappings.add(new Mapping(format, activity, method, extraTypes));
}

那么,我们来看下 Mapping 的结构

1
2
3
4
5
6
7
8
9
10
public class Mapping {
private final String format;
private final Class<? extends Activity> activity;
private final MethodInvoker method;
private final ExtraTypes extraTypes;
private Path formatPath;

...

}
  • format 就是我们传入的 uri
  • activity 就是路由对应的 activity
  • method 表示是否是执行方法
  • extraTypes 是所携带的参数类型
  • formatPath 就是 uri 对应的 Path

具体的 Mapping 初始化是在 Processor 生成的代码中完成的,我们到后面再讲。

在回过头来看 doOpen 方法,在 mapping.match(path) 方法中用来判断该 path 有没有匹配路由表中的路由

1
2
3
4
5
6
7
8
9
10
11
12
13
public boolean match(Path fullLink) {
if (formatPath.isHttp()) {
return Path.match(formatPath, fullLink);
} else {
// fullLink without host
boolean match = Path.match(formatPath.next(), fullLink.next());
if (!match && fullLink.next() != null) {
// fullLink with host
match = Path.match(formatPath.next(), fullLink.next().next());
}
return match;
}
}

Mapping 的 match 方法就是把自身的 formatPath 和 fullLink 进行比较,最终调用的还是 Path.match 方法,本质就是把 Path 链表中的每一项进行比较,来判断两个 Path 是否相等。

再后面的就是判断 activity ,如果是空的,就认为是执行方法,否则就构造 Intent 来实现跳转,再利用 requestCode 来判断是 startActivity 还是 startActivityForResult 。其中执行方法主要调用了 MethodInvoker.invoke 方法

1
2
3
public interface MethodInvoker {
void invoke(Context context, Bundle bundle);
}

再重点关注下 mapping.parseExtras(uri) 这句代码。这里主要做的事情就是构造 Bundle 传入 uri 的参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public Bundle parseExtras(Uri uri) {
Bundle bundle = new Bundle();
// path segments // ignore scheme
Path p = formatPath.next();
Path y = Path.create(uri).next();
while (p != null) {
// 是否是 path 中传递参数
if (p.isArgument()) {
put(bundle, p.argument(), y.value());
}
p = p.next();
y = y.next();
}
// 解析 uri 中的参数,放入 bundle 中
Set<String> names = UriCompact.getQueryParameterNames(uri);
for (String name : names) {
String value = uri.getQueryParameter(name);
put(bundle, name, value);
}
return bundle;
}

// 本方法主要做的事情就是根据参数名来判断参数类型
private void put(Bundle bundle, String name, String value) {
int type = extraTypes.getType(name);
name = extraTypes.transfer(name);
if (type == ExtraTypes.STRING) {
type = extraTypes.getType(name);
}
switch (type) {
case ExtraTypes.INT:
bundle.putInt(name, Integer.parseInt(value));
break;
case ExtraTypes.LONG:
bundle.putLong(name, Long.parseLong(value));
break;
case ExtraTypes.BOOL:
bundle.putBoolean(name, Boolean.parseBoolean(value));
break;
case ExtraTypes.SHORT:
bundle.putShort(name, Short.parseShort(value));
break;
case ExtraTypes.FLOAT:
bundle.putFloat(name, Float.parseFloat(value));
break;
case ExtraTypes.DOUBLE:
bundle.putDouble(name, Double.parseDouble(value));
break;
case ExtraTypes.BYTE:
bundle.putByte(name, Byte.parseByte(value));
break;
case ExtraTypes.CHAR:
bundle.putChar(name, value.charAt(0));
break;
default:
bundle.putString(name, value);
break;
}
}

这代码很简单,基本上都加了注释,相信大家都看得懂,就不讲咯。

到这里,整个 ActivityRouter 的流程就讲完啦。

剩下的,就是 Processor 解析注解生成代码了。

compiler

先告诉处理器支持的注解

1
2
3
4
5
6
7
8
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> ret = new HashSet<>();
ret.add(Modules.class.getCanonicalName());
ret.add(Module.class.getCanonicalName());
ret.add(Router.class.getCanonicalName());
return ret;
}

剩下主要看 RouterProcessor 的 process 方法。

方法的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
debug("process apt with " + annotations.toString());
if (annotations.isEmpty()) {
return false;
}
boolean hasModule = false;
boolean hasModules = false;
// module
String moduleName = "RouterMapping";
Set<? extends Element> moduleList = roundEnv.getElementsAnnotatedWith(Module.class);
if (moduleList != null && moduleList.size() > 0) {
Module annotation = moduleList.iterator().next().getAnnotation(Module.class);
moduleName = moduleName + "_" + annotation.value();
hasModule = true;
}
// modules
String[] moduleNames = null;
Set<? extends Element> modulesList = roundEnv.getElementsAnnotatedWith(Modules.class);
if (modulesList != null && modulesList.size() > 0) {
Element modules = modulesList.iterator().next();
moduleNames = modules.getAnnotation(Modules.class).value();
hasModules = true;
}
// RouterInit
if (hasModules) {
debug("generate modules RouterInit");
generateModulesRouterInit(moduleNames);
} else if (!hasModule) {
debug("generate default RouterInit");
generateDefaultRouterInit();
}
// RouterMapping
return handleRouter(moduleName, roundEnv);
}

process 方法中的逻辑可以分为三部分:

  • 判断是否有 @module 和 @modules ,即是否是组件化开发的
  • 生成 RouterInit
  • 生成 RouterMapping

那我们慢慢分析,先来看第一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// module
String moduleName = "RouterMapping";
Set<? extends Element> moduleList = roundEnv.getElementsAnnotatedWith(Module.class);
if (moduleList != null && moduleList.size() > 0) {
Module annotation = moduleList.iterator().next().getAnnotation(Module.class);
// 如果是多 module 组件化开发的话,每个 module 需要标注 @module ,这样每个module都会生成一个属于自己的 RouterMapping ,防止重复
// 比如 @Module("abc") moduleName 就是 RouterMapping_abc
moduleName = moduleName + "_" + annotation.value();
hasModule = true;
}
// @Modules 的作用就是把上面生成的各个 RouterMapping 给汇总起来,统一到 RouterInit 里面,这样只要调用 RouterInit.init 方法就完成了各模块的路由初始化
String[] moduleNames = null;
Set<? extends Element> modulesList = roundEnv.getElementsAnnotatedWith(Modules.class);
if (modulesList != null && modulesList.size() > 0) {
Element modules = modulesList.iterator().next();
// 比如@Modules("abc","def") , moduleNames 就是 [“abc”, "def"]
moduleNames = modules.getAnnotation(Modules.class).value();
hasModules = true;
}

接下来就是生成 RouterInit 类

1
2
3
4
5
6
7
if (hasModules) {
debug("generate modules RouterInit");
generateModulesRouterInit(moduleNames);
} else if (!hasModule) {
debug("generate default RouterInit");
generateDefaultRouterInit();
}

如果是多 module 组件化开发,最终会调用 generateModulesRouterInit ,否则调用的就是默认的 generateDefaultRouterInit 。

这里我们就看 generateModulesRouterInit 的代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void generateModulesRouterInit(String[] moduleNames) {
MethodSpec.Builder initMethod = MethodSpec.methodBuilder("init")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC);
for (String module : moduleNames) {
initMethod.addStatement("RouterMapping_" + module + ".map()");
}
TypeSpec routerInit = TypeSpec.classBuilder("RouterInit")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(initMethod.build())
.build();
try {
JavaFile.builder("com.github.mzule.activityrouter.router", routerInit)
.build()
.writeTo(filer);
} catch (Exception e) {
e.printStackTrace();
}
}

可以看到,利用了 javapoet 来生成 java 代码,这代码很简单,就不用多讲啦,直接来看下最后生成 RouterInit 类的代码吧

1
2
3
4
5
6
7
8
package com.github.mzule.activityrouter.router;

public final class RouterInit {
public static final void init() {
RouterMapping_app.map();
RouterMapping_sdk.map();
}
}

RouterInit 生成好之后,最后的工作就是生成对应的 RouterMapping_app 和 RouterMapping_sdk 这两个类了。

生成的入口就是 handleRouter(moduleName, roundEnv) 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
private boolean handleRouter(String genClassName, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Router.class);

// 定义方法 public static final void map()
MethodSpec.Builder mapMethod = MethodSpec.methodBuilder("map")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL, Modifier.STATIC)
.addStatement("java.util.Map<String,String> transfer = null")
.addStatement("com.github.mzule.activityrouter.router.ExtraTypes extraTypes")
.addCode("\n");

// 遍历 @Router 修饰的 element
for (Element element : elements) {
Router router = element.getAnnotation(Router.class);
// 判断 @Router 中有没有 transfer
String[] transfer = router.transfer();
if (transfer.length > 0 && !"".equals(transfer[0])) {
mapMethod.addStatement("transfer = new java.util.HashMap<String, String>()");
for (String s : transfer) {
String[] components = s.split("=>");
if (components.length != 2) {
error("transfer `" + s + "` not match a=>b format");
break;
}
mapMethod.addStatement("transfer.put($S, $S)", components[0], components[1]);
}
} else {
mapMethod.addStatement("transfer = null");
}

// 解析路由参数类型
mapMethod.addStatement("extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes()");
mapMethod.addStatement("extraTypes.setTransfer(transfer)");

addStatement(mapMethod, int.class, router.intParams());
addStatement(mapMethod, long.class, router.longParams());
addStatement(mapMethod, boolean.class, router.booleanParams());
addStatement(mapMethod, short.class, router.shortParams());
addStatement(mapMethod, float.class, router.floatParams());
addStatement(mapMethod, double.class, router.doubleParams());
addStatement(mapMethod, byte.class, router.byteParams());
addStatement(mapMethod, char.class, router.charParams());

// 遍历 @Router 生成所有路由的解析代码
for (String format : router.value()) {
ClassName className;
Name methodName = null;
if (element.getKind() == ElementKind.CLASS) {
className = ClassName.get((TypeElement) element);
} else if (element.getKind() == ElementKind.METHOD) {
className = ClassName.get((TypeElement) element.getEnclosingElement());
methodName = element.getSimpleName();
} else {
throw new IllegalArgumentException("unknow type");
}
if (format.startsWith("/")) {
error("Router#value can not start with '/'. at [" + className + "]@Router(\"" + format + "\")");
return false;
}
if (format.endsWith("/")) {
error("Router#value can not end with '/'. at [" + className + "]@Router(\"" + format + "\")");
return false;
}
// 如果 @Router 是修饰类的 就是路由跳转的
if (element.getKind() == ElementKind.CLASS) {
mapMethod.addStatement("com.github.mzule.activityrouter.router.Routers.map($S, $T.class, null, extraTypes)", format, className);
} else {
// 否则就是路由调用方法的,第三个参数传入 MethodInvoker 对象
mapMethod.addStatement("com.github.mzule.activityrouter.router.Routers.map($S, null, " +
"new MethodInvoker() {\n" +
" public void invoke(android.content.Context context, android.os.Bundle bundle) {\n" +
" $T.$N(context, bundle);\n" +
" }\n" +
"}, " +
"extraTypes)", format, className, methodName);
}
}
mapMethod.addCode("\n");
}
TypeSpec routerMapping = TypeSpec.classBuilder(genClassName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(mapMethod.build())
.build();
// 生成 RouterMapping_xxx 类
try {
JavaFile.builder("com.github.mzule.activityrouter.router", routerMapping)
.build()
.writeTo(filer);
} catch (Throwable e) {
e.printStackTrace();
}
return true;
}

// 生成 extraTypes 参数类型设置代码
// 比如
// extraTypes.setLongExtra("id,updateTime".split(","));
// extraTypes.setBooleanExtra("web".split(","));
private void addStatement(MethodSpec.Builder mapMethod, Class typeClz, String[] args) {
String extras = join(args);
if (extras.length() > 0) {
String typeName = typeClz.getSimpleName();
String s = typeName.substring(0, 1).toUpperCase() + typeName.replaceFirst("\\w", "");

mapMethod.addStatement("extraTypes.set" + s + "Extra($S.split(\",\"))", extras);
}
}

来看一下最后生成的 RouterMapping_xxx 的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public final class RouterMapping_app {
public static final void map() {
java.util.Map<String,String> transfer = null;
com.github.mzule.activityrouter.router.ExtraTypes extraTypes;

transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("user/:userId", UserActivity.class, null, extraTypes);
com.github.mzule.activityrouter.router.Routers.map("user/:nickname/city/:city/gender/:gender/age/:age", UserActivity.class, null, extraTypes);

transfer = new java.util.HashMap<String, String>();
transfer.put("web", "fromWeb");
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
extraTypes.setLongExtra("id,updateTime".split(","));
extraTypes.setBooleanExtra("web".split(","));
com.github.mzule.activityrouter.router.Routers.map("http://mzule.com/main", MainActivity.class, null, extraTypes);
com.github.mzule.activityrouter.router.Routers.map("main", MainActivity.class, null, extraTypes);
com.github.mzule.activityrouter.router.Routers.map("home", MainActivity.class, null, extraTypes);

transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("with_host", HostActivity.class, null, extraTypes);

transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("home/:homeName", HomeActivity.class, null, extraTypes);

transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("logout", null, new MethodInvoker() {
public void invoke(android.content.Context context, android.os.Bundle bundle) {
NonUIActions.logout(context, bundle);
}
}, extraTypes);

transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("upload", null, new MethodInvoker() {
public void invoke(android.content.Context context, android.os.Bundle bundle) {
NonUIActions.uploadLog(context, bundle);
}
}, extraTypes);

transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("user/collection", UserCollectionActivity.class, null, extraTypes);

}
}

至此,ActivityRouter 所有的流程都已经讲完啦!!!

RouterActivity

对啦,还有一点,ActivityRouter 支持从外部唤起 Activity 。

在 AndroidManifest.xml 中声明 RouterActivity ,填写对应 scheme 和 host 。

<activity
    android:name="com.github.mzule.activityrouter.router.RouterActivity"
    android:theme="@android:style/Theme.NoDisplay">
    ...
    <intent-filter>
    	<action android:name="android.intent.action.VIEW" />
    	<category android:name="android.intent.category.DEFAULT" />
    	<category android:name="android.intent.category.BROWSABLE" />
    	<data android:scheme="http" android:host="mzule.com" />
    </intent-filter>
</activity>

其实先唤起的是 RouterActivity ,然后在 RouterActivity 中根据 uri 再跳转到对应的 Activity ,这点可以从 RouterActivity 的代码中印证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class RouterActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

RouterCallback callback = getRouterCallback();

Uri uri = getIntent().getData();
if (uri != null) {
Routers.open(this, uri, callback);
}
finish();
}

private RouterCallback getRouterCallback() {
if (getApplication() instanceof RouterCallbackProvider) {
return ((RouterCallbackProvider) getApplication()).provideRouterCallback();
}
return null;
}
}