ARouter

2019-01-03  本文已影响0人  要学的东西太多了

1.ARouter主要用于不同组件的路由跳转,使用时,目标对象需要使用@Route注解,规则是@Route(path = "/xxx/xxx"),这里的一级路径必须唯一,一般跟moudle名相同,二级一般使用目标对象的类名,完整的路径不可重复,不然会报错。path必须"/"开头,且至少两级,在build方法里面有如下判断:

if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        }

build(path)方法会调用extractGroup(path)获取group,有如下判断:

if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
            throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
        }

2.Arouter跳转,使用ARouter.getInstance().build("/xxx/xxx").navigation(),build里面的内容就是目标对象注解的路径,需要带参数的话可使用ARouter.getInstance().build("/xxx/xxx").with(new Bundle()).navigation(),也可以用其他类型的withXXX,支持基本类型、String、实现了Parcelable或Serializable接口的对象以及这些类型的集合和数组。此外,还支持withObject,要使用withObject,需要定义一个实现SerializationService接口的类,并加上@Route(path = "/service/json")注解,这个类需要实现解析的方法,实质上是对字符串的解析,例如:

@Route(path = "/service/json")
public class JsonServiceImpl implements SerializationService {
    private Gson mGson;
    @Override
    public <T> T json2Object(String input, Class<T> clazz) {
        check();
        return mGson.fromJson(input,clazz);
    }

    @Override
    public String object2Json(Object instance) {
        check();
        return mGson.toJson(instance);
    }

    @Override
    public <T> T parseObject(String input, Type clazz) {
        check();
        return mGson.fromJson(input,clazz);
    }

    @Override
    public void init(Context context) {
        check();
    }

    private void check(){
        if (mGson==null) {
            mGson = new Gson();
        }
    }
}

接收方可以用@Autowired注解标记字段,并调用ARouter.getInstance().inject(this)直接赋值,不用在intent里面去取值,@Autowired(name = "xxx")可以指定key。
这里还有几点需要注意:

(1).@Autowired注解标记的字段不能是private的。

(2).withObject传递的对象不能实现Parcelable接口,实现了Parcelable接口的传递用withParcelable方法。

(3).withObject有时候会出现接收方解析出的对象为空的情况,考虑以下解决思路:
实体类提供显示的构造器;
实体类的成员变量改成public类型;
接收方的@Autowired注解的字段改成和传入的key名称一样。

(4).withObject传递List或者map时,接收方@Autowired注解的字段不能是他们的实现类,如ArrayList。

3.Arouter在使用时,使用Arouter的注解,如@Route、@Autowired等,build.gradle必须加如下语句,否则注解无法解析:

    android {
        ...
        defaultConfig {
              ...
              javaCompileOptions {
                  annotationProcessorOptions {
                        arguments = [ AROUTER_MODULE_NAME : project.getName() ]
                  }
              }
        }
    }
    dependencies {
        ...
        //ARouter
        api 'com.alibaba:arouter-api:1.4.0'
        annotationProcessor 'com.alibaba:arouter-compiler:1.2.1'
    }

4.Arouter的build也可以传入Uri,Uri必须指定path,build方法返回的是Postcard对象,在PendingIntent中需要传入Intent,可以用如下方式获取class:

public static Class getRouterClass(String path){
        Postcard postcard = ARouter.getInstance().build(path);
        LogisticsCenter.completion(postcard);
        return postcard.getDestination();
    }

startActivityForResult的实现方式是navigation(Context context,int requestCode),Intent设置flag的方式用withFlags方法,withTransition方法指定转场动画。

5.Arouter可以自定义拦截器,例如:

@Interceptor(name = "init",priority = 1)//priority 指定拦截顺序,越小排越前面
public class InitInterceptorImpl implements IInterceptor {
    private static final String TAG = "InitInterceptorImpl";
    @Override
    public void process(Postcard postcard, InterceptorCallback callback) {
        String path = postcard.getPath();
        LogUtils.LogI(TAG,path);
        if(ARouterManager.PATH_AIDL_AIDL.equals(path)){//根据目标判断
            boolean v = KeyValueUtils.getInstance().get("hasInMeg");//判断条件
            if(v){
                //不拦截,可以做其他操作,比如加参数
                callback.onContinue(postcard);
            }else {
                //表示拦截
                callback.onInterrupt(new Throwable("请先打开Messenger页面"));
            }
        }else{
            callback.onContinue(postcard);
        }
    }

    @Override
    public void init(Context context) {
        LogUtils.LogI(TAG,"初始化拦截器");
    }
}

在跳转的时候调用navigation(Context context, NavigationCallback callback)方法。NavigationCallback的回调方法如下:

                    @Override
                    public void onFound(Postcard postcard) {
                        LogUtils.LogE(TAG,"onFound");//找到路由
                    }

                    @Override
                    public void onLost(Postcard postcard) {
                        LogUtils.LogE(TAG,"onLost");//未找到路由
                    }

                    @Override
                    public void onArrival(Postcard postcard) {
                        LogUtils.LogE(TAG,"onArrival");//跳转成功
                    }

                    @Override
                    public void onInterrupt(Postcard postcard) {
                        LogUtils.LogE(TAG,"onInterrupt");//被拦截
                    }

注意NavigationCallback 的onInterrupt方法是在子线程,要进行UI操作需要切换线程。

6.降级策略分两种,用于找不到路由的后续处理,第一种是上面的NavigationCallback 回调的onLost方法,这个是单独降级。另外一种是实现DegradeService 接口,是全局降级,例如:

@Route(path = "/degrade/noInit")
public class NoInitDealService implements DegradeService {
    private static final String TAG = "NoInitDealService";
    @Override
    public void onLost(Context context, Postcard postcard) {
        LogUtils.LogE(TAG,"降级策略");
    }

    @Override
    public void init(Context context) {

    }
}

单独降级的优先级高于全局,也就是说写了回调,就不会走全局的onLost。

7.自定义服务可用于组件间调用接口,需要提供一个继承IProvider的接口,例如:

public interface ISayHelloService extends IProvider {
    void say(String msg);
}

然后在需要实现的组件里面新增一个实现类,注意这个类不能为内部类,例如:

@Route(path = "/main/say")
public class SayImpl implements ISayHelloService {
    private static final String TAG = "SayImpl";
    private ICallBack mICallBack;
    @Override
    public void say(String msg) {
        if(mICallBack!=null){
            mICallBack.callBack(msg);
        }
    }

    public void setCallBack(ICallBack iCallBack) {
        mICallBack = iCallBack;
    }

    @Override
    public void init(Context context) {

    }

    public interface ICallBack{
        void callBack(String msg);
    }
}

其他组件调用接口可以用以下几种方式:
注解:

@Autowired
    ISayHelloService s1;
    @Autowired(name = "/main/say")
    ISayHelloService s2;

ARouter.getInstance().inject(this);
        s1.say("1号");
        s2.say("2号");

主动查找:

    private ISayHelloService s3;
    private ISayHelloService s4;

s3 = ARouter.getInstance().navigation(ISayHelloService.class);
        s3.say("3号");
        s4 = (ISayHelloService) ARouter.getInstance().build("/main/say").navigation();
        s4.say("4号");

这里要注意,如果这个接口有多个实现,一定要根据byname的方式来查找,即s2、s4的方式。

8.原理分析:
Arouter的主要原理是通过注解和注解解释器生成java文件,在调用ARouter.init(this)方法时,调用_ARouter的init方法:

public static void init(Application application) {
        if (!hasInit) {
            //...
            hasInit = _ARouter.init(application);
            if (hasInit) {
                _ARouter.afterInit();
            }
            //...
        }
    }

在里面调用LogisticsCenter的init方法,这里初始化了一个主线程的handler:

protected static synchronized boolean init(Application application) {
        //...
        LogisticsCenter.init(mContext, executor);
        //...
        mHandler = new Handler(Looper.getMainLooper());
        return true;
    }

LogisticsCenter的init方法里面,找到前面通过注解解释器生成的java文件类名集合,然后循环调用对象的loadInto,loadInto把path和注解的目标类放到路由表里面:

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;
        //...
        Set<String> routerMap;
       //...
        routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
        //...
       for (String className : routerMap) {
             if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                  //这里先保存各个分组对应的IRouteGroup类,IRouteGroup里面保存了各个路径对应的RouteMeta对象,RouteMeta对象的RouteType是ACTIVITY,且保存了目标class对象
                  ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
            } else if ( className.startsWith ( ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS ) ) {
                //这里根据priority保存了IInterceptor对象
           ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
           } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                //这里根据服务类型,保存了对应的RouteMeta对象,RouteMeta对象里面有目标服务的class对象
                  ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
             }
         }
      //...

getFileNameByPackageName方法扫描目标包下面的所有类的类名:

public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
        final Set<String> classNames = new HashSet<>();
        List<String> paths = getSourcePaths(context);
        final CountDownLatch parserCtl = new CountDownLatch(paths.size());
        for (final String path : paths) {
            DefaultPoolExecutor.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    DexFile dexfile = null;
                    try {
                        if (path.endsWith(EXTRACTED_SUFFIX)) {
                            dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                        } else {
                            dexfile = new DexFile(path);
                        }
                        Enumeration<String> dexEntries = dexfile.entries();
                        while (dexEntries.hasMoreElements()) {
                            String className = dexEntries.nextElement();
                            if (className.startsWith(packageName)) {
                                classNames.add(className);
                            }
                        }
                    } catch (Throwable ignore) {
                        Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                    } finally {
                        if (null != dexfile) {
                            try {
                                dexfile.close();
                            } catch (Throwable ignore) {
                            }
                        }
                        parserCtl.countDown();
                    }
                }
            });
        }
        parserCtl.await();
        return classNames;
    }

这里路由表的初始化就完成了,在调用页面跳转的时候,ARouter.getInstance().build(uri)返回的是一个PostCard对象,navigation方法会调用Arouter对象的navigation方法,接着调用_ARouter的navigation方法,重点看下_ARouter的navigation方法:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        //...
        //这个方法里面会从路由表里面,根据path找到RouteMeta对象,找到了就将目标对象的class赋给PostCard,如果没找到,会先找IRouteGroup对象,并主动调用IRouteGroup的loadInto方法,把对应的class放进路由表,然后递归回来重新找。
       LogisticsCenter.completion(postcard);
        //..try-catch块里面,如果没找到目标对象,会回调 callback.onLost(postcard)
        //没抛异常,说明找到了,会回调onFound
        if (null != callback) {
            callback.onFound(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;
    }

上面的方法没拦截的情况都会走_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);
                }
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }
                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();
                try {
                    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) {
                    logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

跳转完成了,那些参数是怎么自动赋值的呢,主要逻辑在ARouter.getInstance().inject(this)方法里面,在_ARouter的inject方法里面,会调用AutowiredService的autowire方法:

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

AutowiredService的实现类AutowiredServiceImpl里面,autowire做了什么操作呢:

@Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {  // No cache.
                    //这里是重点,找到目标类,并调用inject方法,在注解自动生成的$$ARouter$$Autowired类里面,inject方法会将传入的target对象强转为本身的类型,然后对注解的对象进行赋值,所赋的值是通过getIntent().getXXXExtra()的方式获取的
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }
上一篇下一篇

猜你喜欢

热点阅读