ARouterはアリが立ち上げたページのルーティングフレームワークです。プロジェクトはARouterによって開発されたコンポーネントベースのアーキテクチャを採用しているため前に再びそのソースコード解析たら今日したがって、ときにそのソース書き込みノートを忘れて、そして見にジャンプページを達成。
(GitHubのリンクを左にプルアップ、あなたはコア書籍のノートのPDFや他のコンテンツに関連するインタビューやインタビューを取得する必要があり、独自のを見つけることができます)
https://github.com/xiangjiana/Android-MS
ARouterベースBenpianソース分析1.2.4
初期化
ARouter呼び出すことにより、使用前にArouter.init
メソッドを渡すとApplication
初期化:
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
ここに呼び出し_ARouter.init
、この_ARouter
クラスはARouterのコアクラスです。
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
// It's not a good idea.
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
}
return true;
}
ここでは、実際の呼び出しですLogisticsCenter.init
:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
Set<String> routerMap;
// 获取存储 ClassName 集合的 routerMap(debug 模式下每次都会拿最新的)
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// 根据指定的 packageName 获取 package 下的所有 ClassName
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
// 存入 SP 缓存
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
} else {
logger.info(TAG, "Load router map from cache.");
// release 模式下,已经缓存了 ClassName 列表
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
// 遍历 ClassName
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 发现是 Root,加载类构建对象后通过 loadInto 加载进 Warehouse.groupsIndex
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 发现是 Interceptor,加载类构建对象后通过 loadInto 加载进 Warehouse.interceptorsIndex
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 发现是 ProviderGroup,加载类构建对象后通过 loadInto 加载进 Warehouse.providersIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
// ...
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
ここでは主に以下の手順は次のとおりです。
入手1.
com.alibaba.android.arouter.routes
保存されたClassName
セットをrouterMap
。
または2.デバッグモードは以前に解析されていない場合routerMap
で、ClassUtils.getFileNameByPackageName
すべてのパッケージに指定された方法ClassName
SPに解析され、保存されました。
3.これは、デバッグモードではなく、以前にSPから直接取得、解析されている場合。(デバッグとしてタイプコードの変更と変化するであろう、たびに更新される必要がある)
。4.ループrouterMap
でClassName
。
- もしそうなら
RouteRoot
、その後、負荷クラスは、オブジェクトの構築loadInto
にロードしますWarehouse.groupsIndex
。- もしそうなら
InterceptorGroup
、その後、負荷クラスは、オブジェクトの構築loadInto
にロードしますWarehouse.interceptorsIndex
。- もしそうなら
ProviderGroup
、その後、負荷クラスは、オブジェクトの構築loadInto 加载进
Warehouse.providersIndex`を。
解決 ClassName
で見てみましょうClassUtils.getFileNameByPackageName
下のパッケージを指定する方法ClassName
、解析されたコレクション:
public static Set<String> getFileNameByPackageName(Context context, final String packageName) {
final Set<String> classNames = new HashSet<>();
// 通过 getSourcePaths 方法获取 dex 文件 path 集合
List<String> paths = getSourcePaths(context);
// 通过 CountDownLatch 对 path 的遍历处理进行控制
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
// 遍历 path,通过 DefaultPoolExecutor 并发对 path 进行处理
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
// 加载 path 对应的 dex 文件
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
// zip 结尾通过 DexFile.loadDex 进行加载
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
// 否则通过 new DexFile 加载
dexfile = new DexFile(path);
}
// 遍历 dex 中的 Entry
Enumeration<String> 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;
}
ステップは、ここでの主な手順は比較的簡単です:
1.
getSourcePaths
取得する方法dex
パス設定ファイルを。
作成2.CountDownLatch
制御dex
ファイルを処理平行に、スピードアップします。
3.リストトラバーサル経路、DefaultPoolExecutor
並列処理経路のために。
4.負荷に対応する経路dex
文書、及び前記トラバースエントリは、対応するパッケージ内に見つかった場合ClassName
、結果のセットに追加。
5.すべてのdex
処理が完了し、リターン結果です
getSourcePaths
に取得する方法dex
我々の焦点はここにはないので、ここで収集、巻き込まれていません。
初期化倉庫
倉庫倉庫は、実際にはARouter自動的に生成されたクラス(のために記憶を意味するRouteRoot
、InterceptorGroup
)ProviderGroup
情報。
私たちは、倉庫クラスが何であるかを見てみましょう:
class Warehouse {
// 保存 RouteGroup 对应的 class 以及 RouteMeta
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// 保存 Provider 以及 RouteMeta
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// 保存 Interceptor 对应的 class 以及 Inteceptor
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();
}
}
見つけることができる倉庫店情報を使用純粋なクラスのリポジトリであり、実際に自動で生成されたデータは、ここでいくつかのクラス上記loadInto
実装されたデータウェアハウスは、活性充填します。
例えば、我々は自動的に生成されたオープンIRouteRoot
実装クラスを:
public class ARouter$$Root$$homework implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("homework", ARouter$$Group$$homework.class);
}
}
それを見ることができgroupsIndex
、この中RouteRoot
ではIRouteGroup
、ある登録されている、するgroupIndex
ルートグループに対応する登録IRouteGroup
クラスを。あまりにも他のクラス、マップのリストに自動的に生成されたコードやデータによる。
見つけることができ、初期化プロセスが自動的に生成されたメインルートに関連するクラスを完了しRouteRoot
、Interceptor
、ProviderGroup
ロード、彼ら意志反映倉庫クラスにロードするコンフィギュレーション情報による。
ルーティングジャンプ
ポストカード作成
ここでは行われているルートをジャンプする方法を見て、見てみましょうARouter.build
メソッド:
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
これは、転送左折_ARouter
ビルド方法を:
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
ユーザーのルートパスの前処理パスに導入する方法を。
最後に転送に曲がるbuild(path, 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
文字列の処理は、実際にルートグループの名前の部分を削除しました。
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
と、パスの前処理。グループ構築するためにパスを通過した後の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;
}
それは実際には、キー、値の対応バンドルに設定されています。
最後に、我々は、合格navigation
への最終的なジャンプを達成するために:
public Object navigation() {
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);
}
ナビゲーションで見ることができるように、彼らは実際には最終的に呼び出している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;
}
上記のコードは、主に以下のステップです。
1.
LogisticsCenter.completion
ハガキを補完すること。
場合2.postcard
設定されていないgreenChannel
場合、postcard
実行のためのインターセプタは、通話終了の実施後_navigation
方法本当にジャンプします。
3.場合は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<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
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
情報ウェアハウス情報を補完するために結合さpostcard
れ、以下のステップの情報を:
1.
Warehouse.routes.get
に従ってパスの試みの取得RouteMeta
オブジェクト。
得られない場合2.RouteMeta
オブジェクトが存在しないか、または(負荷の最初のタイムなしのために)ロードされていない、取得しようとRouteGroup
、その呼び出しをloadInto
する方法をRouteMeta
倉庫、最後の呼び出し完了再試行完了に負荷。
3.RouteMeta
なるはがき、に提供される情報rawUri
束にパラメータのセット。
4のProvider
とFragment
特殊処理、Provider
からなりWarehouse
負荷、そのオブジェクトの構造、及びその後のセットにpostcard
。Provider
そして、Fragment
インターセプタをスキップします。
RouteGroup
loadInto
遺骨が自動的に生成され、例えば、以下が自動的に生成されたコードの一部です。
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/homework/commit", RouteMeta.build(RouteType.ACTIVITY, HomeworkCommitActivity.class, "/homework/commit", "homework", null, -1, -2147483648));
// ...
}
自動的に生成されたコードを生成した注釈によると、全体のために必要なことは、など、私たちの目的地、クラス、パスを補完などの情報が含まれています。
実行ジャンプ
我々は見て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
- 活動、前意図と意志の構成のため
postcard
にに設定されたパラメータ、その後に応じて異なる結果呼び出す必要がありますstartActivity
方法を。 Provider
対応するダイレクトリターンプロバイダオブジェクト、。- 以下のため
Broadcast
、ContentProvider
後にFragment
反射物体を構築し、パラメータセット内に戻されます。
見つけることができますARouter
初期化およびルーティングは、全体的なロジックは、実際には、することがあり、難しいことではありませんジャンプしActivity
、Fragment
プロセスに梱包を回しました。
サービス取得
よるARouterの追加ARouter.getInstance().build().navigation()
ページジャンプはあなたにもできることで、この方法を実現するARouter.getInstance().navigation(XXService.class)
ような方法でサービス取得コンポーネント間で達成し、我々はそれがどのように行われるかを参照してください。
public <T> T navigation(Class<? extends T> service) {
return _ARouter.getInstance().navigation(service);
}
それでも飛び込む_ARouter
達成するために、中央:
protected <T> 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
はがきのビルド名アウト。
ARouterでは古いバージョンでは、ありません、このような完全な名前のサービスを介して取得するために、しかしsimpleNameにより、以下の旧バージョンとの互換性のために、買収は再び構築する方法の古いバージョンを使用しようとしません。
その後、我々は通過しますLogisticsCenter.completion
ハガキに完成した、そして最終的にによってpostcard.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());
}
}
実際にはかなり単純であり、すでに倉庫によって初期化されるprovidersIndex
に従ってserviceName
対応し得るRouteMeta
、以下に従ってRouteMeta
戻り、対応するパスやグループのポストカードを
インターセプターメカニズム
上記の分析では、我々はインターセプタが完成する過程で実行された、メカニズムはARouterインターセプタが存在する見つけることができ、私たちはそのインターセプタ機構の実装を見てみましょう。
私たちは、最初に表示IInterceptor
インターフェースを:
public interface IInterceptor extends IProvider {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
メイン処理法によって完全実行インターセプター、ポストカードをその中に処理されてもよいです。そして、我々はを通じて、というインターセプタの実装を知っているInterceptorServiceImpl.doInterceptions
の実装:
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);
}
ここで最初に構成されて執行を行うことによって行わ、interceptors
数CountDownLatch
_EXECUTE方法続くは、によって行わ。
注釈処理
だから、ARouter
どのように自動的に生成されたRouteRoot
、RouteMeta
、ProviderGroup
、Provider
、Interceptor
そのサブクラス?
https://github.com/xiangjiana/Android実際にARouterはAutoServiceとAnnotationProcessorによって達成し、特定の用途にJavaファイルのために書かれ、生成されたクラスは、主にJavaPoetによって達成されるためJavaPoetは、GitHubのページを見ることができます-MS
注釈処理コードを組み合わせアノテーションを取得特性のほとんどの部分であるのでJavaPoet
、各要素に対応するJavaコードを生成し、コードのこの部分があり、ソースコードのこの部分を見ることが私たちとしないここで、より複雑ではありません興味のある読者は見ることができarouter-complier
、パッケージの具体的な実現。
概要
:ARouterコアプロセスは、3つの部分に分割され
、コンパイル注釈プロセス
によってAnnotationProcessor
フィッティングJavaPoet
実装コンパイル注釈によるとRouteRoot
、RouteMeta
、ProviderGroup
、Provider
、Interceptor
完成倉庫アノテーション情報は、これらのクラスの仕事をロードし、コード生成の他のタイプ。
初期化
ARouter.init
でき、ARouter
初期化することは、それは2つのステップに分かれています。
1.トラバースファイルと自動的に生成されたパッケージのクラスに保存されているクラスを見つけるセットを。前記検索速度を高速化するために、非同期スレッドプールを通して見ていた、とすることにより、すべての非同期タスクの終了を待っているの外観。リリースAPK情報自動的に生成されたクラスは必ずしも変更されませんので、デバッグモードでの検索処理は、非キャッシュであるに応じて種類、構成され、、オブジェクトとその呼び出し、これらのグループの情報の方法を倉庫にロードされ、このプロセスは、特定のかからない負荷を。グループは、主に次の段階のいくつかの情報が含まれている(例えば、対応するクラスオブジェクト、等)、より低いレベルを除去するために、情報負荷から、不要ファイルのDEXを横断しないことだけが必要です。
Apk
dex
ClassName
CountDownLatch
ClassName
RouteRoot
InterceptorGroup
ProviderGroup
loadInto
RouteMeta
RouteGroup
ルーティング
次のステップに分け、ルーティングプロセス:
1.により
ARouter
ビルド(パス)を直接又はそれを通じて、ポストカードを構築する方法navigate(serviceClass)
ポストカードの方法を構築します。
2.ポストカード方法の一連のルーティングを設定は、インターセプタをスキップしようにするかどうかを、パラメータを運ぶなど、提供しました。
:経路による完全な3は、以下のステップであるナビゲーション方法を、ジャンプ
- 。によって
LogisticsCenter.completion
先のポストカードのために、そのような倉庫ポストカードにロードされた情報に基づいて情報を結合する方法として、補足情報を入力し、このプロセスが達成するRouteMeta
ローディング情報を、非スキップされたクラスの迎撃の意志のために1つのインターセプタによって1を呼び出しますインターセプト処理。- B.が完了した後にポストカードのタイプに応じて、プロセスは、(アクティビティコールについて説明したように対応するルーティング方法を呼び出し
startActivity
建物オブジェクトフラグメントに対して、呼をsetArgument
)。4.ナビゲーション(アクティビティがnullを返される)結果を返します
(GitHubのリンクを左にプルアップ、あなたはコア書籍のノートのPDFや他のコンテンツに関連するインタビューやインタビューを取得する必要があり、独自のを見つけることができます)
https://github.com/xiangjiana/Android-MS