Arouter源码解析

790 阅读7分钟

1.Postcard解析

  • Postcard(明信片),继承与RouteMeta,在路由流程中起到定位提供类的作用,项目之中的所有跳转信息类都是基于此类,并提供了一些跳转能力

RouteMeta

    private RouteType type;         		   // 路由类型,支持如下:
                                                //    ACTIVITY(0, "android.app.Activity"),
                                                //    SERVICE(1, "android.app.Service"),
                                                //    PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
                                                //    CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
                                                //    BOARDCAST(-1, ""),
                                                //    METHOD(-1, ""),
                                                //    FRAGMENT(-1, "android.app.Fragment"),
                                                //    UNKNOWN(-1, "Unknown route type");

    private Element rawType;        		   // 路由之中的原始类型
    private Class<?> destination;   		   // 跳转目标类
    private String path;            		   // 路由地址
    private String group;           		   // 路由分组
    private int priority = -1;      		   // 优先级,数字越小,优先级越高
    private int extra;              		   // 额外数据
    private Map<String, Integer> paramsType;  	// 参数
    private String name;					  // 名称
    private Map<String, Autowired> injectConfig;// 缓存注入配置

Postcard

    // Base
    private Uri uri;									//Uri
    private Object tag;             				   	  // 标签为某些错误做准备。是内部参数,请勿使用
    private Bundle mBundle;         					  // 数据转换Bundele
    private int flags = 0;        						  // 路由标志
    private int timeout = 300;      					  // 超时时间
    private IProvider provider;     					  // 如果PostCard是Provider,则用于传递数据
    private boolean greenChannel;						  // 绿色通道,true则拦截器失效
    private SerializationService serializationService;	    // 内部使用的序列化服务
    private Context context;       	 					  // 上下文
    private String action;								 // 动作

    // 动画处理
    private Bundle optionsCompat;    
    private int enterAnim = -1;
    private int exitAnim = -1;

Postcard.navigation()

  • PostCard还提供了一系列跳转导航方法,其实内部都调用到了**ARouter.getInstance().navigation(mContext, this, requestCode, callback)**方法,最终实现至_ARouter.getInstance().navigation(mContext, postcard, requestCode, callback)方法之中

    _ARouter其实才是真正实现了相关功能的类

  • _ARouter.getInstance().navigation()代码实现如下:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
            return null;
        }
        postcard.setContext(null == context ? mContext : context);
        try {
            //物流中心预处理postcard,内部对postcard实现了完整的组装
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            //Debug专用处理
            if (debuggable()) {
                //如果postcard预处理失败则提示用户路由不匹配
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(mContext, "There's no route matched!\n" +
                                " Path = [" + postcard.getPath() + "]\n" +
                                " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                    }
                });
            }
            // 如果有callBack实现,则调用onLost回调
            if (null != callback) {
                callback.onLost(postcard);
            } else {
                // 如果没有具体的实现则启用全局降级服务进行处理
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                // 如果降级服务也没有预声明则调用onLost回调
                if (null != degradeService) { degradeService.onLost(context, postcard); }
            }
            return null;
        }
    	// 找到对应目的地则执行onFound回调
        if (null != callback) { callback.onFound(postcard); }
    	// 如果不是绿色通道,则执行拦截器处理
        if (!postcard.isGreenChannel()) {   
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(postcard, requestCode, callback);
                }
                @Override
                public void onInterrupt(Throwable exception) {
                    if (null != callback) { callback.onInterrupt(postcard);}
                    logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
        } else {
            // 否则直接执行_navigation()处理
            return _navigation(postcard, requestCode, callback);
        }
        return null;
    }

如果目的地存在,无论是否绿色通道,最终实现都在**_navigation(postcard, requestCode, callback)**方法之中

_navigation(postcard, requestCode, callback)

  • 方法内部会根据类型进行分类,最终调用Android系统实现,例如如果类型是Activity,则最终调用startActivity方法。
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = postcard.getContext();
        switch (postcard.getType()) {
            // Activity 类型处理
            case ACTIVITY:
                // 组装 Intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());
                int flags = postcard.getFlags();
                if (0 != flags) {
                    intent.setFlags(flags);
                }
                if (!(currentContext instanceof Activity)) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }
                // 最终在主线程调用 startActivity
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

                break;
            // Provider 类型处理
            case PROVIDER:
                // 直接返回
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                // 获取 fragmentMeta
                Class<?> fragmentMeta = postcard.getDestination();
                try {
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    // 普通 Fragment 处理
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    // V4 Fragment 处理
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }
                    // 返回 Fragment
                    return instance;
                } catch (Exception ex) {
                    // 无法找到对应实例
                    logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }
        return null;
    }

总结

  1. 首先Arouter根据最终目的地以及对应跳转参数创建PostCard,PostCard内部包含了全部属性
  2. 最终通过PostCard的navigation()方法进行跳转
    1. navigation()方法内部最终会调用_ARouter.getInstance().navigation()的实现
    2. _ARouter.getInstance().navigation()内部首先会通过LogisticsCenter实现物流预处理PostCard,如果是合法路径则继续
      1. 如果是绿色通道则不执行拦截器处理
      2. 如果不是绿色通道则执行拦截器处理
    3. 最终调用_navigation()方法
  3. _navigation()内部会根据类型进行区分
    1. 如果是Activity,则根据PostCard装载Intent,最终调用StartActivity()
    2. 如果是Provider直接返回PostCard中的Provider
    3. 如果是Fragment则区分是V4还是普通Fragment,最终返回实例,如果找不到实例则提示异常

2.Annotation注解处理器

Arouter强大的一点就是通过注解实现了许多重复的代码自动填充,提供的注解如下:

  • Route:标记页面可以通过路由器进行路由
  • Interceptor:标记一个拦截器进行路线拦截
  • Autowired:自动实现参数装配

每一个注解肯定对应这一个注解处理器,使得所有标记对应注解的类可以自动实现其能力

注解处理器都是通过javapoet和auto-service实现了面向切面编程的能力

Route

我们直接看process之中的parseRoutes方法,代码中只保留了关键部分

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
    ......
    if (CollectionUtils.isNotEmpty(routeElements)) {
        ......
        // 根据传入的所有标记Route的元素进行分析,最终返回RouteMeta添加至Map之中
        for (Element element : routeElements) {
            // Activity or Fragment
            if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
             	.....
                if (types.isSubtype(tm, type_Activity)) {
                    //创建对应 Activity 类型的 RouteMeta 
                    routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
                } else {
                    //创建对应 Fragment 类型的 RouteMeta 
                    routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
                }
                routeMeta.setInjectConfig(injectConfig);
            } else if (types.isSubtype(tm, iProvider)) {
                //创建对应 Provider 类型的 RouteMeta 
                routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
            } else if (types.isSubtype(tm, type_Service)) {
                //创建对应 Service 类型的 RouteMeta 
                routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
            } else {
                throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
            }
            //最终将 RouteMeta 添加到 groupMap
            categories(routeMeta);
        }
        MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(providerParamSpec);

        Map<String, List<RouteDoc>> docSource = new HashMap<>();

        // 根据 groupMap 列表开始生成对应JAVA代码
        for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
         	......
            Set<RouteMeta> groupData = entry.getValue();
            for (RouteMeta routeMeta : groupData) {
                .....
                switch (routeMeta.getType()) {
                    case PROVIDER: 
                        .....
                        //如果是Provider类型则要对其进行缓存
                        break;
                    default:
                        break;
                }
                StringBuilder mapBodyBuilder = new StringBuilder();
                Map<String, Integer> paramsType = routeMeta.getParamsType();
                Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();
                if (MapUtils.isNotEmpty(paramsType)) {
                    List<RouteDoc.Param> paramList = new ArrayList<>();
                    for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
                        // 开始真正的写入JAVA代码
                        mapBodyBuilder.append("put(\"").append(types.getKey()).append("\", ").append(types.getValue()).append("); ");
                        RouteDoc.Param param = new RouteDoc.Param();
                        Autowired injectConfig = injectConfigs.get(types.getKey());
                        param.setKey(types.getKey());
                        param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
                        param.setDescription(injectConfig.desc());
                        param.setRequired(injectConfig.required());
                        paramList.add(param);
                    }
                    routeDoc.setParams(paramList);
                }
                String mapBody = mapBodyBuilder.toString();

            }
            .................
    }
}

@Route总结

  1. 获取到所有的被注解Route的类,生成对应的RouteMeta,分组放到groupMap中,key为group name, value为支持排序放入的的Set中

  2. 遍历groupMap中所有的Set的所有RouteMeta (所以是个双层for循环)生成对应代码。

    生成的对应代码如下:

    public class ARouter$$Group$$test implements IRouteGroup {
      @Override
      public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1));
      }
    }
    

Autowired

  • Autowired其实也是通过注解处理器实现的,代码比较简单,其实生成的就是Intent的自动组装。只不过比较巧妙的是,我们在使用Arouter时都要调用ARouter.getInstance().inject(this)代码,其实生成的代码就是inject的重写intent的自动组装。

生成的代码如下:

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;
  	// Intent 自动组装
    substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
    substitute.height = substitute.getIntent().getIntExtra("height", substitute.height);
    substitute.girl = substitute.getIntent().getBooleanExtra("boy", substitute.girl);
    // 中间还有一部分安全检查例如要使用序列化则要实现SerializationService
    substitute.url = substitute.getIntent().getExtras() == null ? substitute.url : substitute.getIntent().getExtras().getString("url", substitute.url);
    substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
  }
}

@Autowired总结

  • 在Activity的变量上标注,注解处理器会在编译阶段扫描有被Autowired注解标注的变量,根据这个类和变量的情况生成一份Java代码,其中最主要会有一个inject方法,完成对相关变量的解析赋值,然后在Activity的onCreate靠前位置调用对应inject方法即可。这个注解主要目的在于省去了手动编写解析Intent参数的代码。

Interceptor

  • 拦截器的实现其实主要是在InterceptorServiceImpl类之中的两个方法

doInterceptions

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
        checkInterceptorsInitStatus();
        if (!interceptorHasInit) {
            //如果在处理拦截还没有初始化完成,则抛出初始化时间太长异常
            callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
            return;
        }
        //在异步线程逐个调用_execute()实现拦截
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                try {
                    _execute(0, interceptorCounter, postcard);
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    //判断是否超时
                    if (interceptorCounter.getCount() > 0) {   
                        callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                    // 异常处理
                    } else if (null != postcard.getTag()) { 
                        callback.onInterrupt((Throwable) postcard.getTag());
                    // 继续处理
                    } else {
                        callback.onContinue(postcard);
                    }
                } catch (Exception e) {
                    callback.onInterrupt(e);
                }
            }
        });
    } else {
        callback.onContinue(postcard);
    }
}

_execute

private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
        if (index < Warehouse.interceptors.size()) {
            IInterceptor iInterceptor = Warehouse.interceptors.get(index);
            iInterceptor.process(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    counter.countDown();
                    // 内部递归调用,并调整index数值
                    _execute(index + 1, counter, postcard);  
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception);   
                    counter.cancel();

                }
            });
        }
    }

@Interceptor总结

  1. 在doInterceptions()方法之中,通过异步线程实现调用_execute方法
    1. 内部还有异常以及超时处理
  2. _execute()之中通过递归调用,并且跳转index实现拦截器逐个处理