Utilisation de base d'ARouter et analyse des principes

Introduction à ARouteur

ARouter est un framework de routage open source développé par Alibaba qui aide les applications Android à subir la transformation des composants. Il s'agit d'un middleware de la plate-forme Android qui fournit des fonctions de routage pour les pages et les services. Il peut passer d'une activité à l'autre dans différents modules.
La caractéristique d’ARouter est qu’il est très flexible et peut aider à découpler les projets.
En plus du saut d'activité bien connu, ARouter prend également en charge l'obtention de fragments, le découplage de services pour activer les appels API inter-modules, etc. Pour plus de détails, consultez la documentation officielle :
https://github.com/alibaba/ARouter/blob/ maître/README_CN.md

Présentation du principe ARouter

ARouter utilise l'annotation @Route et utilise la technologie APT pour générer des fichiers de classe lors de la compilation afin de stocker la relation de mappage entre le chemin et l'activitéClass.
Lorsque le processus de l'application démarre, il récupère ces fichiers de classe, lit les données de relation de mappage qui y sont stockées dans la mémoire et les enregistre dans la carte de la table de routage.
Lors d'un saut d'itinéraire, transmettez l'adresse de routage de la page à atteindre via la méthode build() d'ARouter. ARouter trouve l'activityClass correspondant à l'adresse de routage dans la table de routage, puis new Intent() est transmis via withString d'ARouter. (). Transporter des paramètres, appeler intent.putExtra() en interne, parcourir la navigation() d'ARouter et appeler startActivity(intent) en interne.
De cette manière, les différents modules ne dépendent pas les uns des autres, mais peuvent démarrer avec succès les activités de chacun.

Utilisation de base d'ARouter

Ajouter des dépendances et une configuration

//注解处理插件
plugins {
    ...
    id 'kotlin-kapt'
}

//注解处理选项
kapt {
    arguments {
        arg("AROUTER_MODULE_NAME", project.getName())
    }
}

//依赖
dependencies {
    // 替换成最新版本, 需要注意的是api要与compiler匹配使用,均使用最新版可以保证兼容
    implementation 'com.alibaba:arouter-api:x.x.x'
    kapt 'com.alibaba:arouter-compiler:x.x.x'
    ...
}

Ajouter une annotation

// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
    ...
}

Initialiser le SDK

if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化

Lancer une opération de routage

// 1. 应用内简单的跳转(通过URL跳转在'进阶用法'中)
ARouter.getInstance().build("/test/activity").navigation();

// 2. 跳转并携带参数
ARouter.getInstance().build("/test/1")
            .withLong("key1", 666L)
            .withString("key3", "888")
            .withObject("key4", new Test("Jack", "Rose"))
            .navigation();

Pour plus d'utilisation, consultez la documentation officielle :
https://github.com/alibaba/ARouter/blob/master/README_CN.md

Présentation de l'architecture du routeur ARouter

Structure du code du projet ARouter

La structure du code du projet ARouter est la même que ci-dessus. Les quatre parties principales sont dans l'encadré rouge. La relation architecturale est la suivante :

Architecture du routeur

Le projet ARouter contient quatre modules : API, Compilateur, Gradle Plugin et Annotation.

API

API ARouteur

Le module API comprend les sous-modules lanceur, noyau, exception, thread, facede, utils et base.

  • launcher : contient le lanceur ARouter.
  • noyau : contient des classes telles que le centre logistique LogsticsCenter et l'entrepôt Warehouse.
  • exception : contient des classes d'exception.
  • Thread : contient CancellableCountDownLatch. La chaîne d'interception d'ARouter est exécutée dans le thread enfant, elle est donc utilisée.
  • facede : contient le rappel de navigation NavigationCallback et l'intercepteur IInterceptor et d'autres interfaces.
  • utils : contient l'imprimante de journaux personnalisée d'ARouter et d'autres classes d'outils.
  • base : Il n’existe qu’un seul UnitqueKeyTreeMap utilisé pour enregistrer l’intercepteur.

Compilateur

Compilateur ARouter

Le module Compiler permet de générer des tables de routage. Les processeurs d'annotations correspondant aux annotations @Autowired, @Interceptor et @Route sont respectivement AutowiredProcessor, InterceptorProcessor et RouteProcessor, tous présents dans le Compiler.

Enregistrer le plugin

Plugin de registre ARouter

Le module Register Plugin comprend le générateur de code d'enregistrement RegisterCodeGenerator et RegisterTransform. Si le plug-in de chargement de table de routage ARouter est utilisé, la table de routage sera chargée par le plug-in Register.

Annotation

Annotation du routeur

Le module Anntaion est relativement simple et ne contient que quelques annotations et classes d'énumération.

Principe APT

ARouter est très pratique à utiliser grâce à la technologie APT. La fonction d'APT est d'analyser et de traiter les annotations dans le code pendant la phase de compilation, puis de générer des fichiers Java basés sur les annotations.
ARouter utilise également deux bibliothèques supplémentaires pour faciliter la mise en œuvre de processeurs d'annotations.

  • JavaPoet fournit un moyen d'appeler des méthodes objet pour générer le code requis, sans avoir besoin d'utiliser manuellement StringBuilder pour fusionner le code, puis d'utiliser IO pour écrire le fichier.
  • Auto-Service fournit un moyen simple d'enregistrer APT, évitant les étapes d'enregistrement initialement fastidieuses.

@Itinéraire

Définition de @Route :

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {

    /**
     * Path of route
     */
    String path();
    ......
}
  • @Target({ElementType.TYPE}) : indique que cette annotation est une classe modifiée
  • @Retention(RetentionPolicy.CLASS) : indique qu'il doit être conservé jusqu'au moment de la compilation

Il existe un chemin de paramètre principal lors de l'utilisation de cette annotation :

// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
    ...
}

De cette façon, la classe annotée avec @Route peut être obtenue lors de la compilation, et le chemin peut être obtenu.

Processeur d'itinéraire

RouteProcessor est le processeur d'annotation correspondant à l'annotation @Route.

@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor 
  • La bibliothèque Auto-Service effectue l'enregistrement automatique du processeur.
  • @SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED}) : indique quelles annotations sont traitées par le processeur actuel.

RouteProcessor hérite de BaseProcessor et obtient le moduleName de chaque module dans la méthode init.

        // Attempt to get user configuration [moduleName]
        Map<String, String> options = processingEnv.getOptions();
        if (MapUtils.isNotEmpty(options)) {
            moduleName = options.get(KEY_MODULE_NAME);
            generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
        }

La méthode de traitement de RouteProcessor est l'endroit où les annotations sont traitées. Elle obtient directement tous les éléments annotés avec @Route.

Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);

Après avoir obtenu l'élément annoté, vous entrerez dans la méthode this.parseRoutes(routeElements). Cette méthode utilise JavaPoet pour générer des fichiers Java. Si vous n'utilisez pas cette bibliothèque, vous pouvez également utiliser StringBuilder pour écrire le contenu des fichiers Java.

IRouteGroup

Jetons d'abord un coup d'œil aux produits générés par RouteProcessor. Vous pouvez voir les produits générés par ARouter dans le chemin ci-dessous.
image.png

public class ARouter$$Group$$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/test/activity", RouteMeta.build(RouteType.ACTIVITY, YourActivity.class, "/test/activity", "test", null, -1, -2147483648));
  }
}

RouteMeta contient les informations nécessaires pour les éléments annotés avec @Route. Le plus évident est YourActivity.class. Avec lui, nous pouvons accéder à cette activité via Intent.

La classe ARouter$$Group$$test hérite de IRouteGroup et implémente la méthode loadInto dans l'interface.

La logique de la méthode loadInto est très simple : passer une carte, utiliser la valeur du chemin annoté comme clé et mettre l'élément (RouteMeta) comme valeur dans la carte. Si cette méthode est complétée, l'enregistrement de l'Activité est terminé.

IRouteRacine

public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("test", ARouter$$Group$$test.class);
  }
}

ARouter$$Root$$app implémente l'interface IRouteRoot et le contenu est très similaire. Grâce à la méthode loadInto, insérez le contenu avec le nom du groupe comme clé et la classe d'implémentation IRouteGroup comme valeur dans la carte.

Par défaut, le groupe est le contenu après la première barre oblique du chemin (@Route(path="/group/xxx"))

Si cette méthode est appelée, vous pouvez obtenir la classe d'implémentation IRouteGroup via group. Une fois la classe instanciée, vous pouvez obtenir Activity.class via la méthode mentionnée ci-dessus.

La structure globale est présentée dans la figure ci-dessous :
image.png

RouteProcessor.process()

Revenez en arrière et continuez à regarder la méthode de processus de RouteProcessor

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        if (CollectionUtils.isNotEmpty(annotations)) {
            Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
            try {
                logger.info(">>> Found routes, start... <<<");
                this.parseRoutes(routeElements);

            } catch (Exception e) {
                logger.error(e);
            }
            return true;
        }

        return false;
    }

Tous les éléments annotés avec @Route sont obtenus et placés dans la méthode parseRoutes pour générer IRouteGroup et IRouteRoot. Celui-ci utilise les classes fournies par JavaPoet pour générer du code sous forme d'appels de méthode.

Génération de table de routage

La méthode de traitement de RouteProcessor traite les classes déclarées avec l'annotation @Route, qui est grossièrement divisée en quatre étapes :
1. Obtenir les éléments de routage
2. Créer des méta-informations de routage
3. Regrouper les méta-informations de routage
4. Générer des fichiers de routage

Dans l'analyse ci-dessus, tous les éléments annotés avec @Route ont été obtenus via roundEnv.getElementsAnnotatedWith(), puis placés dans la méthode parseRoutes.

Les méta-informations de routage mentionnées ici font référence à RouteMeta. RouteProcessor associera l'activité, le fournisseur, le service ou le fragment déclaré avec l'annotation @Route à un RouteMeta.

for (Element element : routeElements) {
    ......
    // Activity or Fragment
    if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
        // Get all fields annotation by @Autowired
        Map<String, Integer> paramsType = new HashMap<>();
        Map<String, Autowired> injectConfig = new HashMap<>();
        injectParamCollector(element, paramsType, injectConfig);

        if (types.isSubtype(tm, type_Activity)) {
            // Activity
            logger.info(">>> Found activity route: " + tm.toString() + " <<<");
            routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
        } else {
            // Fragment
            logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
            routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
        }

        routeMeta.setInjectConfig(injectConfig);
    } 
    ......
    categories(routeMeta);
}

Dans ce code, la construction de la classe RouteMeta est terminée et les paramètres reçus par l'activité via l'annotation @AutoWired sont également obtenus. Regroupez ensuite tous les RouteMeta via la méthode catégories (routeMeta).

Pourquoi grouper ? Au fur et à mesure des itérations du projet, le nombre de composants augmentera. Mettre autant d'informations sur les composants dans une carte entraînera évidemment un gros problème de mémoire, et le temps de chargement augmentera également. La méthode adoptée par Arouter est « regroupement + chargement à la demande ». En même temps, le regroupement est également pratique pour la gestion.

    private void categories(RouteMeta routeMete) {
        if (routeVerify(routeMete)) {
            logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
            Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
            if (CollectionUtils.isEmpty(routeMetas)) {
                ......
                routeMetaSet.add(routeMete);
                groupMap.put(routeMete.getGroup(), routeMetaSet);
            } else {
                routeMetas.add(routeMete);
            }
        }
        ......
    }

Il y a un groupMap dans le RouteProcessor. Une fois le RouteMeta créé, le RouteProcessor regroupera les groupes en fonction de leur groupe en tant que clé et les placera dans le groupMap. RouteMeta lui-même sera placé dans un ensemble. Les groupes de tous les RouteMeta de l'ensemble sont les mêmes et servent de valeur de la carte.

Une fois que RouteProcessor a regroupé RouteMeta, il utilisera JavaPoet pour générer des fichiers de routage de groupe, de fournisseur et de racine. La table de routage est composée de ces fichiers. JavaPoet est le framework de génération de code open source de Square.

            // Write root meta into disk.
            String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                    TypeSpec.classBuilder(rootFileName)
                            .addJavadoc(WARNING_TIPS)
                            .addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
                            .addModifiers(PUBLIC)
                            .addMethod(loadIntoMethodOfRootBuilder.build())
                            .build()
            ).build().writeTo(mFiler);

Les fichiers de routage générés sont les produits du RouteProcessor vu précédemment, qui sont les suivants :
image.png

Chargement de la table de routage

Charger la table de routage consiste en fait à charger le fichier de classe généré par RouteProcessor.

Lors de l'appel de la méthode d'initialisation init() d'ARouter, ARouter appellera la méthode init() de LogisticsCenter. Dans cette méthode, loadRouterMap() chargera d'abord la table de routage via le plug-in, puis déterminera si la table de routage actuelle est en cours de chargement. La méthode est un plug-in. Sinon, chargez la table de routage depuis Dex.

    /**
     * LogisticsCenter init, load all metas in memory. Demand initialization
     */
    public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        ......
        try {
            long startInit = System.currentTimeMillis();
            //load by plugin first
            loadRouterMap();
            if (registerByPlugin) {
                //通过插件加载路由表
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
                //从Dex中加载路由表
                ......
            }
            ......
        } catch (Exception e) {
            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
        }
    }

Charger la table de routage depuis Dex

Charger la table de routage depuis Dex

Le processus de chargement de la table de routage via Dex est à peu près comme indiqué dans la figure ci-dessus. Examinons ensuite étape par étape le chargement de la table de routage depuis Dex dans la méthode init() de LogisticsCenter :

                // It will rebuild router map every times when debuggable.
                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                    logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                    // These class was generated by arouter-compiler.
                    routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                    if (!routerMap.isEmpty()) {
                        context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                    }

                    PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
                } else {
                    logger.info(TAG, "Load router map from cache.");
                    routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
                }

Si le mode de débogage est défini ou s'il s'agit d'une nouvelle version, analysez tous les fichiers Dex pour rechercher tous les fichiers commençant par com.alibaba.android.arouter.routes, puis mettez à jour vers SharePreferences. Sinon, lisez le cache directement depuis SharePreferences pour réduire le temps d'analyse.

ClassUtils est utilisé ici pour lire le fichier Dex et lire la table de routage à partir du fichier Dex.

                for (String className : routerMap) {
                    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);
                    }
                }

Après avoir enregistré la table de routage dans SharedPreferences, il déterminera si la classe est IRouteRoot, IInterceptorGroup ou IProviderGroup en fonction du suffixe du nom de classe, puis l'instanciera dans différents objets et appellera la méthode loadInto pour charger le contenu du fichier de classe dans l'index.

Charger la table de routage via un plugin

Si vous souhaitez raccourcir le temps d'initialisation d'ARouter, vous pouvez utiliser le plug-in Gradle d'ARouter. Ce plug-in peut charger automatiquement la table de routage, de sorte qu'ARouter n'ait pas besoin de lire les informations de classe lors de l'initialisation, raccourcissant ainsi l'initialisation. temps.
Utilisez le plug-in Gradle pour implémenter le chargement automatique des tables de routage
Le principe de fonctionnement du plug-in est que lorsque le code est compilé, le plug-in trouvera la méthode loadRouterMap de la classe LogisticsCenter, puis insérera le code lié au routage dans la méthode, afin que la table de routage ne soit pas analysée. à partir du fichier dex lors de l'initialisation.

Saut de table de routage

Utilisez ARouter.getInstance().build(“/test/activity”).navigation() pour lancer une opération de routage et un saut, qui appellera la méthode navigation() de _ARouter.
Il existe deux surcharges de la méthode navigation() de _ARouter. L’un est utilisé pour créer des services et l’autre pour les sauts de routage.

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

            // Set application to postcard.
            postcard.setContext(mContext);

            LogisticsCenter.completion(postcard);
            return (T) postcard.getProvider();
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            return null;
        }
    }

Le processus de création d'un service est relativement simple. Créez une carte postale via LogisticsCenter.buildProvider, y compris les informations de groupe et de chemin les plus élémentaires, puis définissez le contexte de la carte postale, et enfin obtenez les méta-informations de routage via LogisticsCenter.completion et remplissez-les. dans la carte postale.

    protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        //预处理服务
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
            // Pretreatment failed, navigation canceled.
            return null;
        }

        // Set context to postcard.
        postcard.setContext(null == context ? mContext : context);

        try {
            //完善Postcard
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
          //完善失败降级策略
          ......
        }
        ......
        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(postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @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(postcard, requestCode, callback);
        }

        return null;
    }

Le processus de saut de routage est un peu plus compliqué, avec à peu près les étapes suivantes : 1. Service de prétraitement, 2. Amélioration de la carte postale, 3. Amélioration de la stratégie de dégradation des pannes, 4. Lien intercepteur, 5. Saut par type.

Services de prétraitement

Le service de prétraitement nous permet de déterminer s'il faut traiter le saut indépendamment en fonction du contenu de PostCard avant le saut d'ARouter. Si c'est le cas, renvoyez false dans onPretreatment().

Pour implémenter le service de prétraitement, il vous suffit d'implémenter l'interface PretreatmentService et d'ajouter une annotation @Route avec n'importe quel contenu de chemin.

Si le service de prétraitement est implémenté et que onPretreatment() renvoie false, le processus de saut sera interrompu et le traitement ne continuera pas.

Améliorer la carte postale

Après avoir appelé le service de prétraitement, _ARouter utilisera LogisticsCenter pour charger la table de routage, qui est le fichier de routage généré par RouteProcessor.

Lorsque _ARouter est initialisé, LogisticsCenter sera également initialisé. Dans la méthode d'initialisation de LogisticsCenter, la table de routage créée par RouteProcessor sera lue puis placée dans l'index correspondant.

Avec l'index, lorsque _ARouter appelle la méthode Completion() de LogisticsCenter, il peut utiliser l'index pour obtenir des méta-informations de routage à partir des itinéraires de l'entrepôt.

public synchronized static void completion(Postcard postcard) {
        ......
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            ......
        } else {
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            Uri rawUri = postcard.getUri();
            if (null != rawUri) {   // Try to set params into bundle.
                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());
            }

            switch (routeMeta.getType()) {
                case PROVIDER:  // if the route is provider, should find its instance
                    // Its provider, so it must implement IProvider
                    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) {
                            logger.error(TAG, "Init provider failed!", e);
                            throw new HandlerException("Init provider failed!");
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

Tout d'abord, si LogisticsCenter ne peut pas trouver la RouteMeta correspondante en fonction de l'index, cela signifie que les itinéraires n'ont pas été remplis. À ce stade, LogisticsCenter obtiendra la RouteMeta du groupe, puis remplira le chemin sous le groupe dans les itinéraires, puis appellera finish() à nouveau. À ce moment, vous pouvez récupérer les informations pour remplir la carte postale.

Avec routeMeta, obtenez la valeur de routeMeta, définissez-la sur l'attribut de carte postale, analysez les paramètres dans l'Uri, définissez-la sur l'attribut de carte postale et définissez-la sur le Bundle pour les sauts suivants.

Après avoir rempli les informations de la carte postale, LogisticsCenter effectuera différentes opérations en fonction du type de carte postale, et lorsque le type est PROVIDER et FRAGMENT, définira le greenChannel de la carte postale, ce qui signifie en fait que Provider et Fragment ignoreront le lien intercepteur.

Améliorer la stratégie de rétrogradation en cas d'échec

Lorsqu'ARouter rencontre une exception dans le processus d'amélioration des informations de la carte postale, si NavigationCallback est transmis pendant la navigation, onLost sera rappelé pour cet échec de saut. S'il n'y a pas de rappel, la stratégie globale de rétrogradation sera utilisée :

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

Semblable au service de prétraitement, pour implémenter la stratégie de rétrogradation, il vous suffit d'implémenter l'interface DegradeService et d'ajouter une annotation @Route avec n'importe quel contenu de chemin.

lien intercepteur

Lorsqu'il n'est pas greenChannel, il entrera dans le lien intercepteur, sinon il passera directement au type. Lors de l'amélioration de Postcard ci-dessus, Provider et Fragment définissent greenChannel, qui ignorera le lien intercepteur. Bien sûr, vous pouvez également appeler la méthode greenChannel() avant la méthode navigation() pour permettre au saut d'ignorer le lien intercepteur.

Les intercepteurs peuvent être utilisés pour traiter des événements pendant les sauts, tels que les vérifications de connexion. Les intercepteurs seront exécutés entre les sauts et plusieurs intercepteurs seront exécutés par ordre de priorité.

Pour implémenter un intercepteur, il vous suffit d'implémenter l'interface IInterceptor et d'utiliser l'annotation @Interceptor(priority = 8, name = « xxxx »). La priorité est la priorité de l'intercepteur. Plusieurs intercepteurs seront exécutés par ordre de priorité.

La méthode onContinue() ou onInterrupt() doit être appelée dans la méthode process. Au moins une de ces méthodes doit être appelée, sinon le processus de routage ne continuera pas. onContinue() indique que le traitement est terminé et que le contrôle est échangé vers ARouter. onInterrupt() indique qu'une exception se produit et que le processus de routage doit être interrompu.

Sauter par type

Une fois l'intercepteur traité, la méthode _navigation() sera appelée dans navigation(), qui est également la méthode spécifique pour sauter. Dans cette méthode, il sera jugé en fonction du type de routage RouteType de Postcard et le saut sera basé sur le type.

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = postcard.getContext();

        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 (0 != flags) {
                    intent.setFlags(flags);
                }

                // Non activity, need FLAG_ACTIVITY_NEW_TASK
                if (!(currentContext instanceof Activity)) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                // Set Actions
                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

                // Navigation in main looper.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

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

        return null;
    }

Lorsque RouteType est Activity, le processus de démarrage de l'activité est le même que le processus habituel de démarrage de l'activité. Créez l'intention, transmettez la destination, transmettez les paramètres supplémentaires, définissez les indicateurs, définissez l'action et enfin appelez startActivity() dans le thread principal pour démarrer la page cible. .

Lorsque RouteType est Provider, c'est-à-dire s'il s'agit d'un service personnalisé, le fournisseur dans Postcard est renvoyé. Le service personnalisé dans ARouter trouve le service de découverte via l'injection de dépendances.

Lorsque RouteType est Fragment, Broadcast ou ContentProvider, l'instance sera créée en utilisant la réflexion via la destination. S'il s'agit d'un Fragment, ARouter définira également les paramètres pour celui-ci, puis renverra l'instance.

Article de référence :
https://juejin.cn/post/6885932290615509000

Acho que você gosta

Origin blog.csdn.net/yuantian_shenhai/article/details/132299670
Recomendado
Clasificación