ARouterソースを解決するためのステップであなたの一歩を踏み出します

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すべてのパッケージに指定された方法ClassNameSPに解析され、保存されました。
3.これは、デバッグモードではなく、以前にSPから直接取得、解析されている場合。(デバッグとしてタイプコードの変更と変化するであろう、たびに更新される必要がある)
。4.ループrouterMapClassName

  • もしそうなら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自動的に生成されたクラス(のために記憶を意味するRouteRootInterceptorGroupProviderGroup情報。

私たちは、倉庫クラスが何であるかを見てみましょう

  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クラスを。あまりにも他のクラス、マップのリストに自動的に生成されたコードやデータによる。

見つけることができ、初期化プロセスが自動的に生成されたメインルートに関連するクラスを完了しRouteRootInterceptorProviderGroupロード、彼ら意志反映倉庫クラスにロードするコンフィギュレーション情報による。

ルーティングジャンプ

ポストカード作成

ここでは行われているルートをジャンプする方法を見て、見てみましょう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)ここではいくつかのパラメータを設定しました。

我々は呼んでいる場合その後、withIntwithDoubleというように、あなたはパラメータを設定することができます。例のための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、そして可能NavigationCallbacknavigationリスニングプロセス。

  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のProviderFragment特殊処理、ProviderからなりWarehouse負荷、そのオブジェクトの構造、及びその後のセットにpostcardProviderそして、Fragmentインターセプタをスキップします。

RouteGrouploadInto遺骨が自動的に生成され、例えば、以下が自動的に生成されたコードの一部です。

  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;
  }

見つけることができる、それが基づいて行われますそれぞれに対処します:postcardtype

  • 活動、前意図と意志の構成のためpostcardにに設定されたパラメータ、その後に応じて異なる結果呼び出す必要がありますstartActivity方法を。
  • Provider対応するダイレクトリターンプロバイダオブジェクト、。
  • 以下のためBroadcastContentProvider後にFragment反射物体を構築し、パラメータセット内に戻されます。

見つけることができますARouter初期化およびルーティングは、全体的なロジックは、実際には、することがあり、難しいことではありませんジャンプしActivityFragmentプロセスに梱包を回しました。

サービス取得

よる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);
     }

ここで最初に構成されて執行を行うことによって行わ、interceptorsCountDownLatch_EXECUTE方法続くは、によって行わ。

注釈処理

だから、ARouterどのように自動的に生成されたRouteRootRouteMetaProviderGroupProviderInterceptorそのサブクラス?

https://github.com/xiangjiana/Android実際にARouterはAutoServiceとAnnotationProcessorによって達成し、特定の用途にJavaファイルのために書かれ、生成されたクラスは、主にJavaPoetによって達成されるためJavaPoetは、GitHubのページを見ることができます-MS

注釈処理コードを組み合わせアノテーションを取得特性のほとんどの部分であるのでJavaPoet、各要素に対応するJavaコードを生成し、コードのこの部分があり、ソースコードのこの部分を見ることが私たちとしないここで、より複雑ではありません興味のある読者は見ることができarouter-complier、パッケージの具体的な実現。

概要

:ARouterコアプロセスは、3つの部分に分割され
、コンパイル注釈プロセス
によってAnnotationProcessorフィッティングJavaPoet実装コンパイル注釈によるとRouteRootRouteMetaProviderGroupProviderInterceptor完成倉庫アノテーション情報は、これらのクラスの仕事をロードし、コード生成の他のタイプ。

初期化

ARouter.initでき、ARouter初期化することは、それは2つのステップに分かれています。

1.トラバースファイルと自動的に生成されたパッケージのクラスに保存されているクラスを見つけるセットを。前記検索速度を高速化するために、非同期スレッドプールを通して見ていた、とすることにより、すべての非同期タスクの終了を待っているの外観。リリースAPK情報自動的に生成されたクラスは必ずしも変更されませんので、デバッグモードでの検索処理は、非キャッシュであるに応じて種類、構成されオブジェクトとその呼び出し、これらのグループの情報の方法を倉庫にロードされ、このプロセスは、特定のかからない負荷を。グループは、主に次の段階のいくつかの情報が含まれている(例えば、対応するクラスオブジェクト、等)、より低いレベルを除去するために、情報負荷から、不要ファイルのDEXを横断しないことだけが必要です。ApkdexClassNameCountDownLatch
ClassNameRouteRootInterceptorGroupProviderGrouploadIntoRouteMetaRouteGroup

ルーティング

次のステップに分け、ルーティングプロセス:

1.によりARouterビルド(パス)を直接又はそれを通じて、ポストカードを構築する方法navigate(serviceClass)ポストカードの方法を構築します。
2.ポストカード方法の一連のルーティングを設定は、インターセプタをスキップしようにするかどうかを、パラメータを運ぶなど、提供しました。
:経路による完全な3は、以下のステップであるナビゲーション方法を、ジャンプ

  • 。によってLogisticsCenter.completion先のポストカードのために、そのような倉庫ポストカードにロードされた情報に基づいて情報を結合する方法として、補足情報を入力し、このプロセスが達成するRouteMetaローディング情報を、非スキップされたクラスの迎撃の意志のために1つのインターセプタによって1を呼び出しますインターセプト処理。
  • B.が完了した後にポストカードのタイプに応じて、プロセスは、(アクティビティコールについて説明したように対応するルーティング方法を呼び出しstartActivity建物オブジェクトフラグメントに対して、呼をsetArgument)。

4.ナビゲーション(アクティビティがnullを返される)結果を返します

GitHubのリンクを左にプルアップ、あなたはコア書籍のノートのPDFや他のコンテンツに関連するインタビューやインタビューを取得する必要があり、独自のを見つけることができます
https://github.com/xiangjiana/Android-MS

おすすめ

転載: blog.51cto.com/14541311/2464730