Route 注解的处理
我们回过来看 process 方法连对 Route 注解的处理。
// 扫描 Route 自己注解 Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class); List<TargetInfo> targetInfos = new ArrayList<>(); for (Element element : elements) { System.out.println("elements =" + elements); // 检查类型 if (!Utils.checkTypeValid(element)) continue; TypeElement typeElement = (TypeElement) element; Route route = typeElement.getAnnotation(Route.class); targetInfos.add(new TargetInfo(typeElement, route.path())); } // 根据 module 名字生成相应的 java 文件 if (!targetInfos.isEmpty()) { generateCode(targetInfos, moduleName); }
首先会扫描所有的 Route 注解,并添加到 targetInfos list 当中,接着调用 generateCode 方法生成相应的文件。
private void generateCode(List<TargetInfo> targetInfos, String moduleName) { MethodSpec.Builder methodSpecBuilder = MethodSpec.methodBuilder("map") // .addAnnotation(Override.class) .addModifiers(Modifier.STATIC) .addModifiers(Modifier.PUBLIC); // .addParameter(parameterSpec); for (TargetInfo info : targetInfos) { methodSpecBuilder.addStatement("com.xj.router.api.Router.getInstance().add($S, $T.class)", info.getRoute(), info.getTypeElement()); } TypeSpec typeSpec = TypeSpec.classBuilder(moduleName) // .addSuperinterface(ClassName.get(interfaceType)) .addModifiers(Modifier.PUBLIC) .addMethod(methodSpecBuilder.build()) .addJavadoc("Generated by Router. Do not edit it!\n") .build(); try { JavaFile.builder(Constants.ROUTE_CLASS_PACKAGE, typeSpec) .build() .writeTo(UtilManager.getMgr().getFiler()); System.out.println("generateCode: =" + Constants.ROUTE_CLASS_PACKAGE + "." + Constants.ROUTE_CLASS_NAME); } catch (Exception e) { e.printStackTrace(); System.out.println("generateCode:e =" + e); } }
这个方法主要是使用 javapoet 生成 java 文件,关于 javaposet 的使用可以见官网文档,生成的 java 文件是这样的。
package com.xj.router.impl; import com.xj.arounterdemo.MainActivity; import com.xj.arounterdemo.OneActivity; import com.xj.arounterdemo.TwoActivity; /** * Generated by Router. Do not edit it! */ public class RouterMapping_app { public static void map() { com.xj.router.api.Router.getInstance().add("activity/main", MainActivity.class); com.xj.router.api.Router.getInstance().add("activity/one", OneActivity.class); com.xj.router.api.Router.getInstance().add("activity/two", TwoActivity.class); } }
可以看到我们定义的注解信息,最终都会调用 Router.getInstance().add() 方法存放起来。
router-api
这个 module 主要是多外暴露的 api,最主要的一个文件是 Router。
public class Router { private static final String TAG = "ARouter"; private static final Router instance = new Router(); private Map<String, Class<? extends Activity>> routeMap = new HashMap<>(); private boolean loaded; private Router() { } public static Router getInstance() { return instance; } public void init() { if (loaded) { return; } RouterInit.init(); loaded = true; } }
当我们想要初始化 Router 的时候,代用 init 方法即可。 init 方法会先判断是否初始化过,没有初始化过,会调用 RouterInit#init 方法区初始化。
而在 RouterInit#init 中,会调用 RouterMap_{@moduleName}#map 方法初始化,改方法又调用 Router.getInstance().add() 方法,从而完成初始化
router 跳转回调
public interface RouterCallback { /** * 在跳转 router 之前 * @param context * @param uri * @return */ boolean beforeOpen(Context context, Uri uri); /** * 在跳转 router 之后 * @param context * @param uri */ void afterOpen(Context context, Uri uri); /** * 没有找到改 router * @param context * @param uri */ void notFind(Context context, Uri uri); /** * 跳转 router 错误 * @param context * @param uri * @param e */ void error(Context context, Uri uri, Throwable e); }
public void navigation(Activity context, int requestCode, Callback callback) { beforeOpen(context); boolean isFind = false; try { Activity activity = (Activity) context; Intent intent = new Intent(); intent.setComponent(new ComponentName(context.getPackageName(), mActivityName)); intent.putExtras(mBundle); getFragment(activity) .setCallback(callback) .startActivityForResult(intent, requestCode); isFind = true; } catch (Exception e) { errorOpen(context, e); tryToCallNotFind(e, context); } if (isFind) { afterOpen(context); } } private void tryToCallNotFind(Exception e, Context context) { if (e instanceof ClassNotFoundException && mRouterCallback != null) { mRouterCallback.notFind(context, mUri); } }
主要看 navigation 方法,在跳转 activity 的时候,首先会会调用
beforeOpen 方法回调 RouterCallback#beforeOpen。接着 catch exception 的时候,如果发生错误,会调用 errorOpen 方法回调 RouterCallback#errorOpen 方法。同时调用 tryToCallNotFind 方法判断是否是 ClassNotFoundException,是的话回调 RouterCallback#notFind。
如果没有发生 eception,会回调 RouterCallback#afterOpen。
Activity 的 startActivityForResult 回调
可以看到我们的 Router 也是支持 startActivityForResult 的
Router.getInstance().build("activity/two").navigation(this, new Callback() { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { Log.i(TAG, "onActivityResult: requestCode=" + requestCode + ";resultCode=" + resultCode + ";data=" + data); } });
它的实现原理其实很简单,是借助一个空白 fragment 实现的,原理的可以看我之前的这一篇文章。
Android Fragment 的妙用 - 优雅地申请权限和处理 onActivityResult
小结
如果觉得效果不错的话,请到 github 上面 star, 谢谢。 Router
我们的 Router 框架,流程大概是这样的。
题外话
看了上面的文章,文章一开头提到的三个问题,你懂了吗,欢迎在评论区留言评论。
- 注解的处理
- 怎样解决多个 module 之间的依赖问题,以及如何支持多 module 使用
- router 跳转及 activty startActivityForResult 的处理
其实,现在很多 router 框架都借助 gradle 插件来实现。这样有一个好处,就是在多 moudle 使用的时候,我们只需要 apply plugin 就 ok,对外屏蔽了一些细节。但其实,他的原理跟我们上面的原理都是差不多的。