Grundlegende Verwendung und Prinzipanalyse von ARouter

Einführung in ARouter

ARouter ist ein von Alibaba entwickeltes Open-Source-Routing-Framework, das Android-Apps bei der Komponententransformation unterstützt. Es handelt sich um eine Middleware in der Android-Plattform, die Routing-Funktionen für Seiten und Dienste bereitstellt. Sie kann zwischen Aktivitäten in verschiedenen Modulen springen.
Das Besondere an ARouter ist, dass es sehr flexibel ist und dabei helfen kann, Projekte zu entkoppeln.
Zusätzlich zum bekannten Aktivitätssprung unterstützt ARouter auch das Abrufen von Fragmenten, das Entkoppeln von Diensten, um modulübergreifende API-Aufrufe usw. zu ermöglichen. Einzelheiten finden Sie in der offiziellen Dokumentation:
https://github.com/alibaba/ARouter/blob/ master/README_CN.md

Überblick über das ARouter-Prinzip

ARouter verwendet die Annotation @Route und die APT-Technologie, um während der Kompilierung Klassendateien zu generieren und die Zuordnungsbeziehung zwischen Pfad und Aktivitätsklasse zu speichern.
Wenn der App-Prozess startet, ruft er diese Klassendateien ab, liest die darin gespeicherten Zuordnungsbeziehungsdaten in den Speicher und speichert sie in der Routing-Tabellenzuordnung.
Wenn Sie einen Routensprung durchführen, übergeben Sie die Routing-Adresse der zu erreichenden Seite über die build()-Methode von ARouter. ARouter findet die Aktivitätsklasse, die der Routing-Adresse in der Routing-Tabelle entspricht, und dann wird new Intent() über ARouters withString übergeben ()-Methode. Parameter übertragen, intent.putExtra() intern aufrufen, durch ARouters navigation() springen und startActivity(intent) intern aufrufen.
Auf diese Weise sind verschiedene Module nicht voneinander abhängig, sondern können ihre Aktivitäten gegenseitig erfolgreich starten.

Grundlegende Verwendung von ARouter

Abhängigkeiten und Konfiguration hinzufügen

//注解处理插件
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'
    ...
}

Anmerkung hinzufügen

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

SDK initialisieren

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

Routing-Vorgang einleiten

// 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();

Weitere Informationen zur Verwendung finden Sie in der offiziellen Dokumentation:
https://github.com/alibaba/ARouter/blob/master/README_CN.md

Übersicht über die ARouter-Architektur

Codestruktur des ARouter-Projekts

Die Codestruktur des ARouter-Projekts ist wie oben. Die vier Kernteile befinden sich im roten Feld. Die architektonische Beziehung ist wie folgt:

ARouter-Architektur

Das ARouter-Projekt enthält vier Module: API, Compiler, Gradle Plugin und Annotation.

API

ARouter-API

Das API-Modul besteht aus den Untermodulen Launcher, Core, Exception, Thread, Facede, Utils und Base.

  • Launcher: Enthält den Launcher ARouter.
  • Kern: Enthält Klassen wie Logistikzentrum LogsticsCenter und Warehouse Warehouse.
  • Ausnahme: Enthält einige Ausnahmeklassen.
  • Thread: Enthält CancellableCountDownLatch. Die Interceptor-Kette von ARouter wird im untergeordneten Thread ausgeführt und daher verwendet.
  • facede: Enthält Navigationsrückruf NavigationCallback und Interceptor IInterceptor und andere Schnittstellen.
  • Utils: Enthält den benutzerdefinierten Protokolldrucker von ARouter und andere Toolklassen.
  • Basis: Es gibt nur eine UnitqueKeyTreeMap, die zum Speichern des Interceptors verwendet wird.

Compiler

ARouter-Compiler

Das Compiler-Modul wird zum Generieren von Routing-Tabellen verwendet. Die Annotationsprozessoren, die den Annotationen @Autowired, @Interceptor und @Route entsprechen, sind AutowiredProcessor, InterceptorProcessor bzw. RouteProcessor, alle im Compiler.

Plugin registrieren

ARouter-Register-Plugin

Das Register-Plugin-Modul umfasst den Registrierungscode-Generator RegisterCodeGenerator und RegisterTransform. Wenn das ARouter-Plug-in zum Laden der Routing-Tabelle verwendet wird, wird die Routing-Tabelle vom Register-Plug-in geladen.

Anmerkung

ARouter-Anmerkung

Das Anntaion-Modul ist relativ einfach und enthält nur einige Annotationen und Aufzählungsklassen.

APT-Prinzip

Dank der APT-Technologie ist ARouter sehr komfortabel zu bedienen. Die Funktion von APT besteht darin, Anmerkungen im Code während der Kompilierungsphase zu scannen und zu verarbeiten und dann Java-Dateien basierend auf den Anmerkungen auszugeben.
ARouter verwendet außerdem zwei zusätzliche Bibliotheken, um die Implementierung von Annotationsprozessoren zu erleichtern.

  • JavaPoet bietet eine Möglichkeit, Objektmethoden aufzurufen, um den erforderlichen Code zu generieren, ohne dass StringBuilder manuell zum Zusammenfügen des Codes und dann mit IO zum Schreiben der Datei verwendet werden muss.
  • Auto-Service bietet eine einfache Möglichkeit, APT zu registrieren und vermeidet die ursprünglich umständlichen Registrierungsschritte.

@Route

Definition von @Route:

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

    /**
     * Path of route
     */
    String path();
    ......
}
  • @Target({ElementType.TYPE}): Zeigt an, dass es sich bei dieser Annotation um eine geänderte Klasse handelt
  • @Retention(RetentionPolicy.CLASS): Gibt an, dass es bis zur Kompilierungszeit aufbewahrt werden muss

Bei Verwendung dieser Annotation gibt es einen Hauptparameterpfad:

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

Auf diese Weise kann die mit @Route annotierte Klasse während der Kompilierung abgerufen und der Pfad abgerufen werden.

RouteProcessor

RouteProcessor ist der Annotationsprozessor, der der @Route-Annotation entspricht.

@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor 
  • Die Auto-Service-Bibliothek führt die automatische Registrierung für den Prozessor durch.
  • @SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED}): Gibt an, welche Anmerkungen vom aktuellen Prozessor verarbeitet werden.

RouteProcessor erbt von BaseProcessor und ruft den Modulnamen jedes Moduls in der Init-Methode ab.

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

Die Prozessmethode von RouteProcessor dient zur Verarbeitung von Anmerkungen und ruft direkt alle mit @Route annotierten Elemente ab.

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

Nachdem Sie das mit Anmerkungen versehene Element erhalten haben, geben Sie die Methode this.parseRoutes(routeElements) ein. Diese Methode verwendet JavaPoet, um Java-Dateien zu generieren. Wenn Sie diese Bibliothek nicht verwenden, können Sie StringBuilder auch zum Schreiben des Inhalts von Java-Dateien verwenden.

IRouteGroup

Werfen wir zunächst einen Blick auf die von RouteProcessor generierten Produkte. Die von ARouter generierten Produkte können Sie im folgenden Pfad sehen.
Bild.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 enthält die notwendigen Informationen für die mit @Route annotierten Elemente. Das offensichtlichste ist YourActivity.class. Damit können wir durch Intent zu dieser Aktivität springen.

Die Klasse ARouter$$Group$$test erbt von IRouteGroup und implementiert die Methode „loadInto“ in der Schnittstelle.

Die Logik der Methode „loadInto“ ist sehr einfach: Übergeben Sie eine Karte, verwenden Sie den annotierten Pfadwert als Schlüssel und fügen Sie das Element (RouteMeta) als Wert in die Karte ein. Wenn diese Methode abgeschlossen ist, ist die Registrierung der Aktivität abgeschlossen.

IRouteRoot

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 implementiert die IRouteRoot-Schnittstelle und der Inhalt ist sehr ähnlich. Fügen Sie über die Methode „loadInto“ den Inhalt mit dem Gruppennamen als Schlüssel und der IRouteGroup-Implementierungsklasse als Wert in die Karte ein.

Standardmäßig ist „Gruppe“ der Inhalt nach dem ersten Schrägstrich im Pfad (@Route(path="/group/xxx"))

Wenn diese Methode aufgerufen wird, können Sie die Klasse der IRouteGroup-Implementierungsklasse über die Gruppe abrufen. Nachdem die Klasse instanziiert wurde, können Sie Activity.class über die oben genannte Methode abrufen.

Die Gesamtstruktur ist in der folgenden Abbildung dargestellt:
Bild.png

RouteProcessor.process()

Gehen Sie zurück und schauen Sie sich weiterhin die Prozessmethode von RouteProcessor an

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

Alle mit @Route annotierten Elemente werden abgerufen und in die parseRoutes-Methode eingefügt, um IRouteGroup und IRouteRoot zu generieren. Dabei werden die von JavaPoet bereitgestellten Klassen genutzt, um Code in Form von Methodenaufrufen zu generieren.

Generierung einer Routing-Tabelle

Die Prozessmethode von RouteProcessor verarbeitet mit der @Route-Annotation deklarierte Klassen, die grob in vier Schritte unterteilt sind:
1. Routing-Elemente abrufen
2. Routing-Metainformationen erstellen
3. Routing-Metainformationen gruppieren
4. Routing-Dateien generieren

In der obigen Analyse wurden alle mit @Route annotierten Elemente über RoundEnv.getElementsAnnotatedWith() abgerufen und dann in die Methode parseRoutes eingefügt.

Die hier erwähnten Routing-Metainformationen beziehen sich auf RouteMeta. RouteProcessor verknüpft die mit der @Route-Annotation deklarierten Aktivitäten, Anbieter, Dienste oder Fragmente mit einer 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);
}

In diesem Code ist die Konstruktion der RouteMeta-Klasse abgeschlossen und es werden auch die von der Aktivität über die Annotation @AutoWired empfangenen Parameter abgerufen. Gruppieren Sie dann alle RouteMeta mithilfe der Methode „categories“ (routeMeta).

Warum Gruppe? Mit der Iteration des Projekts nimmt die Anzahl der Komponenten zu. Das Einfügen so vieler Komponenteninformationen in eine Karte führt offensichtlich zu großen Problemen im Speicher und erhöht auch die Ladezeit. Die von Arouter verwendete Methode ist „Gruppierung + Laden bei Bedarf“. Gleichzeitig ist die Gruppierung auch praktisch für die Verwaltung.

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

Es gibt eine GroupMap im RouteProcessor. Nachdem die RouteMeta erstellt wurde, gruppiert der RouteProcessor die Gruppen entsprechend ihrer Gruppe als Schlüssel und fügt sie in die GroupMap ein. RouteMeta selbst wird in ein Set eingefügt. Die Gruppen aller RouteMeta im Set sind gleich und dienen als Wert der Karte.

Nachdem RouteProcessor RouteMeta gruppiert hat, verwendet er JavaPoet, um Gruppen-, Provider- und Root-Routing-Dateien zu generieren. Die Routing-Tabelle besteht aus diesen Dateien. JavaPoet ist das Open-Source-Code-Generierungs-Framework von 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);

Die generierten Routing-Dateien sind die Produkte des zuvor gesehenen RouteProcessors, nämlich die folgenden:
Bild.png

Laden der Routing-Tabelle

Beim Laden der Routing-Tabelle wird tatsächlich die von RouteProcessor generierte Klassendatei geladen.

Beim Aufrufen der Initialisierungsmethode init () von ARouter ruft ARouter die Methode init () von LogisticsCenter auf. Bei dieser Methode lädt LoadRouterMap () zuerst die Routing-Tabelle über das Plug-In und bestimmt dann, ob die aktuelle Routing-Tabelle geladen wird Methode ist ein Plug-In. Wenn nicht, laden Sie die Routing-Tabelle von 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() + "]");
        }
    }

Routing-Tabelle von Dex laden

Routing-Tabelle von Dex laden

Der Prozess des Ladens der Routing-Tabelle über Dex ist ungefähr wie in der Abbildung oben dargestellt. Schauen wir uns als Nächstes Schritt für Schritt das Laden der Routing-Tabelle von Dex in der init()-Methode von LogisticsCenter an:

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

Wenn der Debug-Modus eingestellt ist oder es sich um eine neue Version handelt, scannen Sie alle Dex-Dateien, um alle Dateien zu finden, die mit com.alibaba.android.arouter.routes beginnen, und aktualisieren Sie dann auf SharePreferences. Andernfalls lesen Sie den Cache direkt aus SharePreferences, um die Analysezeit zu verkürzen.

ClassUtils wird hier verwendet, um die Dex-Datei zu lesen und die Routing-Tabelle aus der Dex-Datei zu lesen.

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

Nach dem Speichern der Routing-Tabelle in SharedPreferences wird anhand des Suffixes des Klassennamens ermittelt, ob es sich bei der Klasse um IRouteRoot, IInterceptorGroup oder IProviderGroup handelt. Anschließend wird sie in verschiedenen Objekten instanziiert und die Methode „loadInto“ aufgerufen, um den Inhalt der Klassendatei zu laden Der Index.

Routing-Tabelle per Plugin laden

Wenn Sie die Initialisierungszeit von ARouter verkürzen möchten, können Sie das Gradle-Plug-In von ARouter verwenden. Dieses Plug-In kann die Routing-Tabelle automatisch laden, sodass ARouter bei der Initialisierung keine Klasseninformationen lesen muss, wodurch die Initialisierung verkürzt wird Zeit.
Verwenden Sie das Gradle-Plug-in, um das automatische Laden von Routing-Tabellen zu implementieren
Das Funktionsprinzip des Plug-Ins besteht darin, dass das Plug-In beim Kompilieren des Codes die LoadRouterMap-Methode der LogisticsCenter-Klasse findet und dann Routing-bezogenen Code in die Methode einfügt, sodass die Routing-Tabelle nicht gescannt wird aus der Dex-Datei während der Initialisierung.

Routing-Tabellensprung

Verwenden Sie ARouter.getInstance().build(“/test/activity”).navigation(), um einen Routing-Vorgang und einen Sprung zu initiieren, der die navigation()-Methode von _ARouter aufruft.
Es gibt zwei Überladungen der navigation()-Methode von _ARouter. Einer wird zum Erstellen von Diensten und der andere zum Routing von Sprüngen verwendet.

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

Der Serviceprozess zum Erstellen eines Dienstes ist relativ einfach: Erstellen Sie über LogisticsCenter.buildProvider eine Postcard-Postkarte, einschließlich der grundlegendsten Gruppen- und Pfadinformationen, legen Sie dann den Kontext für Postcard fest und rufen Sie schließlich die Routing-Metainformationen über LogisticsCenter.completion ab und füllen Sie sie aus in Postkarte.

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

Der Routing-Jump-Prozess ist etwas komplizierter und umfasst ungefähr die folgenden Schritte: 1. Vorverarbeitungsdienst, 2. Postkarte verbessern, 3. Strategie zur Fehlerverschlechterung verbessern, 4. Interceptor-Link, 5. Springen nach Typ.

Vorverarbeitungsdienste

Mit dem Vorverarbeitungsdienst können wir anhand des Inhalts von PostCard bestimmen, ob der Sprung unabhängig verarbeitet werden soll, bevor ARouter springt. Wenn ja, geben Sie in onPretreatment() false zurück.

Um den Vorbehandlungsdienst zu implementieren, müssen Sie lediglich die PretreatmentService-Schnittstelle implementieren und eine @Route-Annotation mit beliebigem Pfadinhalt hinzufügen.

Wenn der Vorverarbeitungsdienst implementiert ist und onPretreatment() false zurückgibt, wird der Sprungvorgang unterbrochen und die Verarbeitung nicht fortgesetzt.

Postkarte verbessern

Nach dem Aufruf des Vorverarbeitungsdienstes lädt _ARouter mithilfe von LogisticsCenter die Routing-Tabelle, bei der es sich um die von RouteProcessor generierte Routing-Datei handelt.

Wenn _ARouter initialisiert wird, wird auch LogisticsCenter initialisiert. In der Initialisierungsmethode von LogisticsCenter wird die von RouteProcessor erstellte Routing-Tabelle gelesen und dann in den entsprechenden Index eingefügt.

Wenn _ARouter mit dem Index die Completion()-Methode von LogisticsCenter aufruft, kann er den Index verwenden, um Routing-Metainformationen aus den Routen des Warehouse abzurufen.

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

Erstens: Wenn LogisticsCenter das entsprechende RouteMeta basierend auf dem Index nicht finden kann, bedeutet dies, dass die Routen nicht gefüllt wurden. Zu diesem Zeitpunkt ruft LogisticsCenter das RouteMeta der Gruppe ab, füllt dann den Pfad unter der Gruppe in Routen und ruft dann auf Vervollständigung() erneut ausführen. Zu diesem Zeitpunkt können Sie die Informationen zum Ausfüllen der Postkarte abrufen.

Rufen Sie mit routeMeta den Wert von routeMeta ab, legen Sie ihn auf das Postcard-Attribut fest, analysieren Sie die Parameter im Uri, setzen Sie ihn auf das Postcard-Attribut und setzen Sie ihn für nachfolgende Sprünge auf das Bundle.

Nach dem Ausfüllen der Postkarteninformationen führt LogisticsCenter je nach Postkartentyp unterschiedliche Vorgänge aus. Wenn der Typ PROVIDER und FRAGMENT ist, wird der grüne Kanal der Postkarte festgelegt, was tatsächlich bedeutet, dass Provider und Fragment den Interceptor-Link überspringen.

Verbessern Sie die Ausfall-Downgrade-Strategie

Wenn ARouter beim Vervollständigen der Postcard-Informationen auf eine Ausnahme stößt und während der Navigation ein NavigationCallback übergeben wird, wird onLost für diesen Sprungfehler zurückgerufen. Wenn kein Rückruf erfolgt, wird die globale Downgrade-Strategie verwendet:

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

Ähnlich wie beim Vorverarbeitungsdienst müssen Sie zum Implementieren der Downgrade-Strategie nur die DegradeService-Schnittstelle implementieren und eine @Route-Annotation mit beliebigem Pfadinhalt hinzufügen.

Abfangjäger-Link

Wenn es sich nicht um GreenChannel handelt, wird der Interceptor-Link eingegeben, andernfalls wird direkt zum Typ gesprungen. Bei der Verbesserung von Postcard oben legen Provider und Fragment greenChannel fest, wodurch der Interceptor-Link übersprungen wird. Natürlich können Sie auch die Methode greenChannel() vor der Methode navigation() aufrufen, damit der Sprung den Interceptor-Link überspringen kann.

Interceptoren können verwendet werden, um Ereignisse während Sprüngen zu verarbeiten, beispielsweise Anmeldeprüfungen. Interceptoren werden zwischen Sprüngen ausgeführt, und mehrere Interceptoren werden in der Reihenfolge ihrer Priorität ausgeführt.

Um einen Interceptor zu implementieren, müssen Sie nur die IInterceptor-Schnittstelle implementieren und die Annotation @Interceptor(priority = 8, name = „xxxx“) verwenden. Priorität ist die Priorität des Interceptors. Mehrere Interceptors werden in der Reihenfolge ihrer Priorität ausgeführt.

In der Prozessmethode muss die Methode onContinue () oder onInterrupt () aufgerufen werden. Mindestens eine dieser Methoden muss aufgerufen werden, andernfalls wird der Routing-Prozess nicht fortgesetzt. onContinue() zeigt an, dass die Verarbeitung abgeschlossen ist und die Steuerung an ARouter übergeben wird. onInterrupt() zeigt an, dass eine Ausnahme auftritt und der Routing-Prozess unterbrochen werden muss.

Springen Sie nach Typ

Nachdem der Interceptor verarbeitet wurde, wird in navigation () die Methode _navigation () aufgerufen, die auch die spezifische Methode zum Springen ist. Bei dieser Methode erfolgt die Beurteilung anhand des RouteType des Postcard-Routingtyps und der Sprung anhand des Typs.

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

Wenn der RouteType „Activity“ ist, ist der Vorgang zum Starten der Aktivität derselbe wie der übliche Vorgang zum Starten der Aktivität. Erstellen Sie die Absicht, übergeben Sie das Ziel, übergeben Sie die Parameter-Extras, setzen Sie die Flags, legen Sie die Aktion fest und rufen Sie schließlich auf startActivity() im Hauptthread, um die Zielseite zu starten. .

Wenn der RouteType Provider ist, d. h. wenn es sich um einen benutzerdefinierten Dienst handelt, wird der Provider in Postcard zurückgegeben. Der benutzerdefinierte Dienst in ARouter findet den Erkennungsdienst durch Abhängigkeitsinjektion.

Wenn RouteType Fragment, Broadcast oder ContentProvider ist, wird die Instanz mithilfe der Reflexion über das Ziel erstellt. Wenn es sich um Fragment handelt, legt ARouter auch Parameter dafür fest und gibt dann die Instanz zurück.

Referenzartikel:
https://juejin.cn/post/6885932290615509000

Ich denke du magst

Origin blog.csdn.net/yuantian_shenhai/article/details/132299670
Empfohlen
Rangfolge