// zip 结尾通过 DexFile.loadDex 进行加载
dexfile = DexFile.loadDex(path, path + “.tmp”, 0);
} else {
// 否则通过 new DexFile 加载
dexfile = new DexFile(path);
}
// 遍历 dex 中的 Entry
Enumeration dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
// 如果是对应的 package 下的类,则添加其 className
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();
}
}
});
}
// 所有 path 处理完成后,继续向下走
parserCtl.await();
Log.d(Consts.TAG, “Filter " + classNames.size() + " classes by packageName <” + packageName + “>”);
return classNames;
}
这里的步骤比较简单,主要是如下的步骤:
通过 getSourcePaths
方法获取 dex 文件的 path 集合。
创建了一个 CountDownLatch
控制 dex 文件的并行处理,以加快速度。
遍历 path 列表,通过 DefaultPoolExecutor
对 path 并行处理。
加载 path 对应的 dex 文件,并对其中的 Entry 进行遍历,若发现了对应 package 下的 ClassName,将其加入结果集合。
所有 dex 处理完成后,返回结果。
关于 getSourcePaths
如何获取到的 dex 集合这里就不纠结了,因为我们的关注点不在这里。
Warehouse
实际上就是仓库的意思,它存放了 ARouter
自动生成的类(RouteRoot
、InterceptorGroup
、ProviderGroup
)的信息。
我们先看看 Warehouse
类究竟是怎样的:
class Warehouse {
// 保存 RouteGroup 对应的 class 以及 RouteMeta
static Map
static Map
// 保存 Provider 以及 RouteMeta
static Map
static Map
// 保存 Interceptor 对应的 class 以及 Inteceptor
static Map
static List interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
可以发现 Warehouse
就是一个纯粹用来存放信息的仓库类,它的数据的实际上是通过上面的几个自动生成的类在 loadInto
中对 Warehouse
主动填入数据实现的。
例如我们打开一个自动生成的 IRouteRoot
的实现类:
public class ARouter R o o t Root Roothomework implements IRouteRoot {
@Override
public void loadInto(Map
routes.put(“homework”, ARouter G r o u p Group Grouphomework.class);
}
}
可以看到,它在 groupsIndex
中对这个 RouteRoot
中的 IRouteGroup
进行了注册,也就是向 groupIndex
中注册了 Route Group 对应的 IRouteGroup
类。其他类也是一样,通过自动生成的代码将数据填入 Map
或 List
中。
可以发现,初始化过程主要完成了对自动生成的路由相关类 RouteRoot
、Interceptor
、ProviderGroup
的加载,对它们通过反射构造后将信息加载进了 Warehouse
类中。
路由跳转
下面我们看看路由的跳转是如何实现的,我们先看到 ARouter.build
方法:
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
它转调到了 _ARouter
的 build
方法:
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + “Parameter is invalid!”);
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path));
}
}
它首先通过 ARouter.navigation
获取到了 PathReplaceService
,它需要用户进行实现,若没有实现会返回 null
,若有实现则调用了它的 forString
方法传入了用户的 Route Path 进行路径的预处理。
最后转调到了 build(path, group)
,group
通过 extractGroup
得到:
private String extractGroup(String path) {
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 ‘/’!”);
}
try {
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + “Extract the default group failed! There’s nothing between 2 ‘/’!”);
} else {
return defaultGroup;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
extractGroup
实际上就是对字符串处理,取出 Route Group 的名称部分。
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + “Parameter is invalid!”);
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
build(path, group)
方法同样也会尝试获取到 PathReplaceService
并对 path
进行预处理。之后通过 path
与 group
构建了一个 Postcard
类:
public Postcard(String path, String group) {
this(path, group, null, null);
}
public Postcard(String path, String group, Uri uri, Bundle bundle) {
setPath(path);
setGroup(group);
setUri(uri);
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
这里最终调用到了 PostCard(path, group, uri, bundle)
,这里只是进行了一些参数的设置。
之后,如果我们调用 withInt
、withDouble
等方法,就可以进行参数的设置。例如 withInt
方法:
public Postcard withInt(@Nullable String key, int value) {
mBundle.putInt(key, value);
return this;
}
它实际上就是在对 Bundle
中设置对应的 key
、value
。
最后我们通过 navigation
即可实现最后的跳转:
public Object nav
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
igation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
public void navigation(Activity mContext, int requestCode) {
navigation(mContext, requestCode, null);
}
public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}
通过如上的 navigation
可以看到,实际上它们都是最终调用到 ARouter.navigation
方法,在没有传入 Context
时会使用 Application
初始化的 Context
,并且可以通过 NavigationCallback
对 navigation
的过程进行监听。
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
ARouter
仍然只是将请求转发到了 _ARouter
:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
// 通过 LogisticsCenter.completion 对 postcard 进行补全
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
// …
}
if (null != callback) {
callback.onFound(postcard);
}
// 如果设置了 greenChannel,会跳过所有拦截器的执行
if (!postcard.isGreenChannel()) {
// 没有跳过拦截器,对 postcard 的所有拦截器进行执行
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);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
上面的代码主要有以下步骤:
通过 LogisticsCenter.completion
对 postcard
进行补全。
如果 postcard
没有设置 greenChannel
,则对 postcard
的拦截器进行执行,执行完成后调用 _navigation
方法真正实现跳转。
如果 postcard
设置了 greenChannel
,则直接跳过所有拦截器,调用 _navigation
方法真正实现跳转。
我们看看 LogisticsCenter.completion
是如何实现 postcard
的补全的:
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + “No postcard!”);
}
// 通过 Warehouse.routes.get 尝试获取 RouteMeta
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// 若 routeMeta 为 null,可能是并不存在,或是还没有加载进来
// 尝试获取 postcard 的 RouteGroup
Class extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
throw new NoRouteFoundException(TAG + “There is no route match the path [” + postcard.getPath() + “], in group [” + postcard.getGroup() + “]”);
} else {
// …
// 如果找到了对应的 RouteGroup,则将其加载进来并重新调用 completion 进行补全
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
iGroupInstance.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
// …
completion(postcard); // Reload
}
} else {
// 如果找到了对应的 routeMeta,将它的信息设置进 postcard 中
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
// 将 uri 中的参数设置进 bundle 中
if (null != rawUri) {
Map
Map
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
// 对于 provider 和 fragment,进行特殊处理
switch (routeMeta.getType()) {
case PROVIDER:
// 如果是一个 provider,尝试从 Warehouse 中查找它的类并构造对象,然后将其设置到 provider
Class extends IProvider> providerMeta = (Class extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There’s no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
// provider 和 fragment 都会跳过拦截器
postcard.greenChannel();
break;
case FRAGMENT:
// provider 和 fragment 都会跳过拦截器
postcard.greenChannel();
default:
break;
}
}
}
这个方法主要完成了对 postcard
的信息与 Warehouse
的信息进行结合,以补全 postcard
的信息,它的步骤如下:
通过 Warehouse.routes.get
根据 path
尝试获取 RouteMeta
对象。
若获取不到 RouteMeta
对象,可能是不存在或是还没有进行加载(第一次都未加载),尝试获取 RouteGroup
调用其 loadInto
方法将 RouteMeta
加载进 Warehouse
,最后调用 completion
重新尝试补全 。
将 RouteMeta
的信息设置到 postcard
中,其中会将 rawUri
的参数设置进 Bundle
。
对于 Provider
和 Fragment
特殊处理,其中 Provider
会从 Warehouse
中加载并构造它的对象,然后设置到 postcard
。Provider
和 Fragment
都会跳过拦截器。
RouteGroup
的 loadInto
仍然是自动生成的,例如下面就是一些自动生成的代码:
public void loadInto(Map
atlas.put("/homework/commit", RouteMeta.build(RouteType.ACTIVITY, HomeworkCommitActivity.class, “/homework/commit”, “homework”, null, -1, -2147483648));
// …
}
它包括了我们补全所需要的如 Destination
、Class
、path
等信息,在生成代码时自动根据注解进行生成。
我们看看 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:
// 对 Activity,构造 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);
}
// 切换到主线程,根据是否需要 result 调用不同的 startActivity 方法
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());
}
if (null != callback) { // Navigation over.
callback.onArrival(postcard);
}
}
});
break;
case PROVIDER:
// provider 直接返回对应的 provider
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
// 对于 broadcast、contentprovider、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;
}
可以发现,它会根据 postcard
的 type
来分别处理:
对于 Activity
,会构造一个 Intent
并将之前 postcard
中的参数设置进去,之后会根据是否需要 result 调用不同的 startActivity
方法。
对于 Provider
,直接返回其对应的 provider
对象。
对于 Broadcast
、ContentProvider
、Fragment
,反射构造对象后,将参数设置进去并返回。
可以发现 ARouter
的初始化和路由跳转的整体逻辑还是不难的,实际上就是对 Activity
、Fragment
的调转过程进行了包装。
Service 的获取
ARouter 除了可以通过 ARouter.getInstance().build().navigation()
这样的方式实现页面跳转之外,还可以通过 ARouter.getInstance().navigation(XXService.class)
这样的方式实现跨越组件的服务获取,我们看看它是如何实现的:
public T navigation(Class extends T> service) {
return _ARouter.getInstance().navigation(service);
}
仍然跳转到了 _ARouter
中去实现:
protected T navigation(Class extends T> service) {
try {
Postcard postcard = LogisticsCenter.buildProvider(service.getName());
// Compatible 1.0.5 compiler sdk.
// Earlier versions did not use the fully qualified name to get the service
if (null == postcard) {
// No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName());
}
if (null == postcard) {
return null;
}
LogisticsCenter.completion(postcard);
return (T) postcard.getProvider();
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
return null;
}
}
这里首先通过 LogisticsCenter.buildProvider
传入 service.class
的 name 构建出了一个 postcard
。
而在 ARouter 老版本中,并不是通过这样一个完整的 name 来获取 Service
的,而是通过 simpleName,下面为了兼容老版本,在获取不到时会尝试用老版本的方式重新构建一次。
之后会通过 LogisticsCenter.completion
对 postcard
进行补全,最后通过 postcard.Provider
获取对应的 Provider
。
除了 buildProvider
之外,其他方法我们已经在前面进行过分析,就不再赘述了:
public static Postcard buildProvider(String serviceName) {
RouteMeta meta = Warehouse.providersIndex.get(serviceName);
if (null == meta) {
return null;
} else {
return new Postcard(meta.getPath(), meta.getGroup());
}
}
这里实际上非常简单,就是通过 Warehouse
中已经初始化的 providersIndex
根据 serviceName 获取对应的 RouteMeta
,之后根据 RouteMeta
的 path
和 group
返回对应的 Postcard
。
拦截器机制
通过前面的分析,可以发现 ARouter 中存在一套拦截器机制,在 completion
的过程中对拦截器进行了执行,让我们看看它的拦截器机制的实现。
我们先看到 IInterceptor
接口:
public interface IInterceptor extends IProvider {
/**
The operation of this interceptor.
@param postcard meta
@param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
拦截器中主要通过 process
方法完成执行过程,可以在其中对 postcard
进行处理。而拦截器的执行我们知道,是通过 InterceptorServiceImpl.doInterceptions
实现的: