ARouter类简单介绍

前言

ARouter使用上一遍已经讲述了。已经看到了基本页面跳转很方便,其实它还有其他路由功能。包括provider、Fragment等。
为了详细了解ARouter的原理,先从重要的实现类的源码剖析来入手。了解了重要组成类和功能,再从流程上进行分析。

ARouter详细源码参考:https://github.com/alibaba/ARouter

Postcard类: 明信片

A container that contains the roadmap.一个包含路线的容器。
这个类是页面跳转很重要的类,是跳转信息的承载者。如是页面的跳转,最终用户传递的数据都会通过这个类进行中转后转换为Intent数据。
在这里插入图片描述
那看一下PostCard究竟包括了哪些信息?

public class RouteMeta {
    private RouteType type;         // Type of route
    private Element rawType;        // Raw type of route
    private Class<?> destination;   // Destination
    private String path;            // Path of route
    private String group;           // Group of route
    private int priority = -1;      // The smaller the number, the higher the priority
    private int extra;              // Extra data
    private Map<String, Integer> paramsType;  // Param type
}
public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;
    private Object tag;             // A tag prepare for some thing wrong.
    private Bundle mBundle;         // Data to transform
    private int flags = -1;         // Flags of route
    private int timeout = 300;      // Navigation timeout, TimeUnit.Second
    private IProvider provider;     // It will be set value, if this postcard was provider.
    private boolean greenChannel;

    // Animation
    private Bundle optionsCompat;    // The transition animation of activity
    private int enterAnim;
    private int exitAnim;
    ....
    public Postcard withBoolean(@Nullable String key, boolean value) {
        mBundle.putBoolean(key, value);
        return this;
    }
 public void navigation(Activity mContext, int requestCode, NavigationCallback callback) 
  {
  }

RouteMeta 参数说明:
1.RouteType type: 路由类型
public enum RouteType { 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");

2.String path:路径
3.Class<?> destination:跳转的目标类
4.String group:组
5.int priority = -1:优先级

PostCard 参数说明:
1.Uri: Uri参数 启页面参数可以是Uri
2.Bundle mBundle:传递bundle数据,提供了withBoolean等,都是向bundle中填充数据。
3.IProvider provider:标记该PostCard是否为一个provider的信息。
因为路由可以是一个provider或者activity。
4.boolean greenChannel:绿色通道
该参数是后续再进行拦截器时候,判断为绿色通道,则不会进行拦截。否则,会经过拦截器后再进行跳转等。
5.Bundle optionsCompat:
主要用于Activity间跳转时候,如果有此参数,会将额外参数传递给下一个Activity。
6.int timeout = 300:路由超时时间,主限制的拦截器拦截时间
7. navigation(Activity mContext, int requestCode, NavigationCallback callback) :
此方法即进行页面跳转等需要执行。

PostCard使用很频繁, 要进行页面跳转,就需要它。一般是进行页面跳转时ARouter.getInstance().build会返回一个PostCard,再调用navigation()进行的页面跳转。

 ARouter.getInstance()
	.build("/app/ListPage")
	.withString("name", "来自主页")
	.navigation(ArouterMainActivity.this, 100, new NavigationCallback(){

	@Override
	public void onFound(Postcard postcard) {

	}

	@Override
	public void onLost(Postcard postcard) {

	}
});

ARouter、_ARouter

此类就是路由的核心之一。ARouter是一个与用户接触的类,是对_ARouter的一个装饰。_ARouter对用户是不可见的。主要功能还是_ARouter实现。所以来看一下_ARouter中几个比较重要的方法:
navigation():
1.首先会 LogisticsCenter.completion(postcard);将postcard信息补全,主要是通用的一些信息进行补全,
2.判断是否为绿色通过,是则直接调用_navigation()执行跳转。否则,需要通过拦截器处理后再进行跳转。

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, NavigationCallback callback) {
        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());

            if (debuggable()) { // Show friendly tips for user.
                Toast.makeText(mContext, "There's no route matched!\n" +
                        " Path = [" + postcard.getPath() + "]\n" +
                        " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
            }

            if (null != callback) {
                callback.onLost(postcard);
            } else {    // No callback for this invoke, then we use the global degrade service.
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

            return null;
        }

        if (null != callback) {
            callback.onFound(postcard);
        }

        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @Override
                public void onInterrupt(Throwable exception) {
                    logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
                }
            });
        } else {
            return _navigation(context, postcard, requestCode);
        }

        return null;
    }

_navigation():跳转的真正实现函数。
1.根据postcard的类型,进行不同的跳转处理。
Activity:则将postcard的数据拿到为intent启动activity
Provider:返回provider
等。

private Object _navigation(final Context context, final Postcard postcard, final int requestCode) {
        final Context currentContext = null == context ? mContext : context;

        switch (postcard.getType()) {
            case ACTIVITY:
                // Build intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                // Set flags.
                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);
                }

                // Navigation in main looper.
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        if (requestCode > 0) {  // Need start for result
                            ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
                        } else {
                            ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
                        }

                        if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
                            ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
                        }
                    }
                });

                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, "Navigation to fragment error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

Warehouse

仓库:存储router路径、分组等信息。如页面跳转,将路由信息保存到内存中。
代码:

class Warehouse {
    // Cache route and metas
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }

IInterceptor

拦截器

public interface IInterceptor extends IProvider {

    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
}

LogisticsCenter

翻译为物流中心,是管理仓库的。
初始化将路由信息load到内存中,即以上的Warehouse仓库中。另外,负责创建postcard,完善postcard信息等。
方法如下:
在这里插入图片描述

  /**
     * LogisticsCenter init, load all metas in memory. Demand initialization
     */
    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;

        try {
            // These class was generate by arouter-compiler.
            List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

            //
            for (String className : classFileNames) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // This one of root elements, load root.
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // Load interceptorMeta
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // Load providerIndex
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }

            if (Warehouse.groupsIndex.size() == 0) {
                logger.error(TAG, "No mapping files were found, check your configuration please!");
            }

            if (ARouter.debuggable()) {
                logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
            }
        } catch (Exception e) {
            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
        }
    }

InterceptorService

拦截器服务管理:sdk中默认使用InterceptorServiceImpl实现类进行拦截器的处理。
会通过_excute()方法进行拦截器遍历拦截,然后将结果回调到调用者。

public interface InterceptorService extends IProvider {

    /**
     * Do interceptions
     */
    void doInterceptions(Postcard postcard, InterceptorCallback callback);
}

  @Override
    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {

            checkInterceptorsInitStatus();

            if (!interceptorHasInit) {
                callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
                return;
            }

            LogisticsCenter.executor.execute(new Runnable() {
                @Override
                public void run() {
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {
                        _excute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        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);
                        }
                    } catch (Exception e) {
                        callback.onInterrupt(e);
                    }
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }
 private static void _excute(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) {
                    // Last interceptor excute over with no exception.
                    counter.countDown();
                    _excute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    // Last interceptor excute over with fatal exception.

                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    counter.cancel();
                    // Be attention, maybe the thread in callback has been changed,
                    // then the catch block(L207) will be invalid.
                    // The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
//                    if (!Looper.getMainLooper().equals(Looper.myLooper())) {    // You shouldn't throw the exception if the thread is main thread.
//                        throw new HandlerException(exception.getMessage());
//                    }
                }
            });
        }
    }

InterceptorCallback

拦截器回调:根据拦截结果通知调用者。

public interface InterceptorCallback {

    /**
     * Continue process
     *
     * @param postcard route meta
     */
    void onContinue(Postcard postcard);

    /**
     * Interrupt process, pipeline will be destory when this method called.
     *
     * @param exception Reson of interrupt.
     */
    void onInterrupt(Throwable exception);
}

AutowiredServiceImpl

   自动组装服务:
   什么是自动组装?就是对参数的自动组装,如之前使用时候增加的   @Autowired的注解。
   通过@Autowired注解的属性,通过调用ARouter.getInstance().inject(this);可以实现自动注入。我们知道原生的Activity传递数据是通过Bundle携带的。因此,ARouter的数据传递肯定也是基于Bundle,并实现了自动赋值的功能。
@Route(path = "/app/ListPage")
public class ListActivity extends Activity {
    @Autowired
    String name;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ARouter.getInstance().inject(this);
        FrameLayout layout = new FrameLayout(this);
        setContentView(layout);
        Button button = new Button(this);
        button.setWidth(300);
        button.setHeight(100);
        button.setText(name + ",打开主页");
        button.setGravity(Gravity.CENTER);
        layout.addView(button);
    }
}

代码:AutowiredServiceImpl 的autowire方法会将创建实例(如:ListActivity A R o u t e r ARouter ARouterAutowired)并执行inject(Object target)。
这样就可以在activity中直接button.setText(name + “,打开主页”);name不需要自己从intent里面获取了。

在这里插入图片描述
注意这里赋值的操作是直接调用“目标类对象.属性”的方式赋值,因此private修饰的属性无法通过这种方式赋值,并且在赋值时会抛出异常,被AutowiredServiceImpl的autowire方法中的try-catch捕获,存入不需要注入的集合中,最终导致同一个类中的其他非private属性也无法注入。

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    @Override
    public void init(Context context) {
        classCache = new LruCache<>(66);
        blackList = new ArrayList<>();
    }

    @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.
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            // ARouter.logger.error(TAG, "Autowired made exception, in class [" + className + "]");
            blackList.add(className);    // This instance need not autowired.
        }
    }
}

IProviderGroup 、IInterceptorGroup、IRouteRoot

IRouteGroup是生成的分组关系契约,IRouteRoot是单个分组下路由信息契约
public interface IProviderGroup {
/**
* Load providers map to input
*
* @param providers input
*/
void loadInto(Map<String, RouteMeta> providers);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值