前言
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);
}