阅读 152

ARouter的源码梳理

ARouter存在的目的就是为了解耦,当两个module之间不存在依赖关系或者只存在单向依赖时,没有办法获取具体的Activity进行跳转,这时就可以借助ARouter来实现。

1. ARouter初始化

ARouter初始化时,会调用LogisticsCenter.init(mContext, executor)方法,该方法通过反射扫描包下指定的路径,获取编译时根据注解生成的类,然后通过反射将数据缓存到Warehouse类对应的集合中。

为了解决ARouter初始化时,反射扫描包下的路径导致的耗时操作,ARouter提供了字节码插桩的方式。通过提供的插件在loadRouterMap方法中处理数据,并将registerByPlugin置为true。

 public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
         loadRouterMap();
            if (registerByPlugin) {
                //字节码插桩处理
            } else {
                //反射处理
            }
    }
复制代码

2. ARouter跳转

ARouter.getInstance().build("/ar/ma2")
                .withInt("age",100)
                .withString("name","xiaoli")
                .navigation()
复制代码

这是一个最标准的ARouter的跳转方式,当执行到build方法时,会根据路由的路径拆分出对应的group,创建一个Postcard对象。Postcard中封装了对应基本数据类型和实现序列化的类的with方法,通过这些方法,可以方便的添加需要传递的参数,封装在Bundle中。当执行到navigation方法时,会按照下面的方法执行。

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
      
        LogisticsCenter.completion(postcard);

        if (!postcard.isGreenChannel()) {
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    if (null != callback) {
                        callback.onInterrupt(postcard);
                    }
                }
            });
        } else {
            return _navigation(context, postcard, requestCode, callback);
        }
        return null;
    }

复制代码
  1. LogisticsCenter的completion方法中会找到Postcard中路径对应的RouteMeta对象,该对象是在编译期根据我们添加的路由来动态生成的一个类,包含了路径和注解类相关的信息。通过对应的RouteMeta对象来补全Postcard中需要的目标类的相关信息。
  2. isGreenChannel= true表示跳过所有的拦截器,默认为false,PROVIDER和FRAGMENT类型会被默认设置greenChannel=true。这里的拦截服务interceptorService在ARouter中指的是InterceptorServiceImpl,执行对应的doInterceptions方法开启一个线程池来执行我们创建的拦截器。执行成功的话会回调到onContinue方法中,最终执行_navigation方法。
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = null == context ? mContext : context;
        switch (postcard.getType()) {
            case ACTIVITY:
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());
                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

                // Navigation in main looper.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

                break;
            case PROVIDER:
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                Class fragmentMeta = postcard.getDestination();
               Object instance = fragmentMeta.getConstructor().newInstance();
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }

                    return instance;
                } catch (Exception ex) {
        }

        return null;
    }
复制代码

可以看到根据postcard中对应的目标Type,有不同的处理方式。例如ACTIVITY代表调转,FRAGMENT则会创建一个对应的对象并返回。

3. ARouter拦截器

public interface IProvider {
    void init(Context context);
}

public interface IInterceptor extends IProvider {
    void process(Postcard postcard, InterceptorCallback callback);
}
复制代码

ARouter中自定义的拦截器需要继承IInterceptor,并重写initprocess方法。init方法在ARouter初始化时会被调用一次。具体的流程图为:

ARouter.init() -> _ARouter.afterInit()->InterceptorServiceImpl.init()->IInterceptor.init()
复制代码

ARouter中拦截器的操作从doInterceptions开始,通过默认的线程池开启一个线程来执行拦截器的拦截操作。每一层的拦截器执行完后,需要调用onContinue交还ARouter控制权,执行下一层拦截器,或者调用onInterrupt中断路由。

 public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
            LogisticsCenter.executor.execute(new Runnable() {
                @Override
                public void run() {
                   _execute(0, interceptorCounter, postcard);
                   if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings.
                            callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                        } else if (null != postcard.getTag()) {    // Maybe some exception in the tag.
                            callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                        } else {
                            callback.onContinue(postcard);
                        }
            }
    }
    
     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();
                    _execute(index + 1, counter, postcard); 
                }

                @Override
                public void onInterrupt(Throwable exception) {
                  
                }
            });
        }
    }
复制代码

拦截器执行完后会会调到InterceptorServiceImpl的doInterceptions方法中继续执行下一步,也就是我们上面讲到的ARouter的跳转的最后一步。

4. 服务管理

ARouter中通过实现IProvider接口可以创建一个服务,然后实现该接口。通过路由注解声明的服务会在编译期被添加到ARouter$$Providers$$app类中。

interface MiProvider : IProvider {
    fun getName():String
}

@Route(path = "/mi/pro",name = "这是测试")
class IMiProviderImpl : MiProvider {
    override fun getName() = "yu"
    override fun init(context: Context?) {}
}

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.mdy.retrofitdemo.arouter.MiProvider", RouteMeta.build(RouteType.PROVIDER, IMiProviderImpl.class, "/mi/pro", "mi", null, -1, -2147483648));
  }
}
复制代码

通过调用接口的方法,ARouter会帮助我们调用具体的实现类中对应的方法,具体的调用方式分为两种。

依赖查找

通过依赖查找的方式主动去发现服务,主要有以下两种方式,一种是根据服务的Name来获取对应的服务,另一种是根据路由来获取对应的服务。

 val miService = ARouter.getInstance().navigation(MiProvider::class.java)
 val miService1 = ARouter.getInstance().build("/mi/pro").navigation()
复制代码

1.根据Name查找

首先来看第一种根据Name来获取服务,执行到ARouter的navigation方法时,具体会执行到_ARouter的navigation方法:

    protected <T> T navigation(Class<? extends T> service) {
         Postcard postcard = LogisticsCenter.buildProvider(service.getName());
         LogisticsCenter.completion(postcard);
         return (T) postcard.getProvider();
    }
复制代码

LogisticsCenter的buildProvider方法会根据服务的Name,从Warehouse的providersIndexMap集合中查找具体的RouteMeta对象,根据RouteMeta对象中存储的路由信息创建一个Postcard对象。后续调用LogisticsCenter的completion方法时,会根据Postcard中存储的路由路径,从Warehouse的routes集合中,获取对应的RouteMeta对象,根据RouteMeta中存储的type,也就是PROVIDER来获取对应的服务实现类,并添加到postcard的provider中,最终返回服务实现类。

2.根据路由查找

根据路由查找的流程和前面ARouter的跳转流程基本一致,不同点在于生成的Postcard对象中服务的type类型为PROVIDER。当执行到最后的_navigation方法时,返回的是对应的服务实现类。

 private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
            case PROVIDER:
                return postcard.getProvider();
    }
复制代码

依赖注入

ARouter中推荐我们使用依赖注入的方法来实现服务的管理。具体的实现为:

public class MiTest2 {

    @Autowired(name = "/mi/pro")
    MiProvider miProvider;

    public MiTest2() {
        ARouter.getInstance().inject(this);
    }

    public String getName() {
        return miProvider.getName();
    }
}
复制代码

检测到Autowired注解声明的服务类时,在编译期会自动生成对应的注解类:

public class MiTest2$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    MiTest2 substitute = (MiTest2)target;
    substitute.miProvider = (MiProvider)ARouter.getInstance().build("/mi/pro").navigation();
  }
}
复制代码

MiTest2类中通过Autowired注解声明的服务MiProvider,在执行到MiTest2的构造函数后,会被直接赋值。详细的原理从inject,该方法会调用到_ARouter的inject方法:

    static void inject(Object thiz) {
        AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
        if (null != autowiredService) {
            autowiredService.autowire(thiz);
        }
    }
复制代码

这里AutowiredService的实现类为AutowiredServiceImpl,执行到autowire方法时,内部会调用到doInject方法:

    private void doInject(Object instance, Class<?> parent) {
        Class<?> clazz = null == parent ? instance.getClass() : parent;

        ISyringe syringe = getSyringe(clazz);
        if (null != syringe) {
            syringe.inject(instance);
        }
    }
复制代码

getSyringe方法会通过反射获取上面生成的注解类的对象,然后调用对应的inject方法,内部依然是调用

ARouter.getInstance().build("/mi/pro").navigation()
复制代码

方法获取服务实现类,并赋值给对应的注入类中的服务对象。

通过上面的介绍,可以发现依赖注入方式帮助我们自动查找对应的服务实现类,实现会更加优雅。通过介绍也可以发现拦截器和服务的实现非常类似,都会直接或者间接继承自IProvider接口,且都存在init方法。不同之处在于拦截器在每一次的路由发起时,都会调用,拦截器会在ARouter第一次初始化时,通过线程池异步初始化,在调用拦截器时,如果初始化未完成,路由会等待。而服务只有在第一次调用时才会初始化。

总结

ARouter是帮助App进行组件化构造的框架,目标是帮助App实现多模块之间的跳转,通信和解耦。

注意: module中如果存在编译期生成的注解类的话,打包成aar时会自动打包到aar中。具体的路径为

 com.alibaba.android.arouter.routes
复制代码
文章分类
Android
文章标签