Análisis de principios y uso básico de ARouter

Introducción a ARouter

ARouter es un marco de enrutamiento de código abierto desarrollado por Alibaba que ayuda a las aplicaciones de Android a transformar sus componentes. Es un middleware en la plataforma Android que proporciona funciones de enrutamiento para páginas y servicios. Puede saltar entre actividades en diferentes módulos.
La característica de ARouter es que es muy flexible y puede ayudar a desacoplar proyectos.
Además del conocido salto de actividad, ARouter también admite la obtención de fragmentos, el desacoplamiento de servicios para permitir llamadas API entre módulos, etc. Para obtener más detalles, consulte la documentación oficial:
https://github.com/alibaba/ARouter/blob/ maestro/README_CN.md

Descripción general del principio de ARouter

ARouter usa la anotación @Route y usa tecnología APT para generar archivos de clase durante la compilación para almacenar la relación de mapeo entre la ruta y la clase de actividad.
Cuando se inicia el proceso de la aplicación, obtendrá estos archivos de clase, leerá los datos de la relación de mapeo almacenados en ellos en la memoria y los guardará en el mapa de la tabla de enrutamiento.
Al realizar un salto de ruta, pase la dirección de enrutamiento de la página a la que se accederá a través del método build() de ARouter. ARouter encuentra la clase de actividad correspondiente a la dirección de enrutamiento en la tabla de enrutamiento y luego pasa el nuevo Intent() a través de withString de ARouter. () Método: Llevar parámetros, llamar a intent.putExtra() internamente, saltar a través de la navegación() de ARouter y llamar a startActivity(intent) internamente.
De esta manera, los diferentes módulos no dependen unos de otros, sino que pueden iniciar con éxito las actividades de los demás.

Uso básico de ARouter

Agregar dependencias y configuración

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

Agregar anotación

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

Inicializar SDK

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

Iniciar operación de enrutamiento

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

Para obtener más uso, consulte la documentación oficial:
https://github.com/alibaba/ARouter/blob/master/README_CN.md

Descripción general de la arquitectura de ARouter

Estructura del código del proyecto ARouter

La estructura del código del proyecto ARouter es la anterior. Las cuatro partes principales están en el cuadro rojo. La relación arquitectónica es la siguiente:

Arquitectura de enrutador

El proyecto ARouter contiene cuatro módulos: API, compilador, complemento Gradle y anotación.

API

API de enrutador

El módulo API consta de submódulos de iniciador, núcleo, excepción, subproceso, facede, utilidades y base.

  • lanzador: Contiene el lanzador ARouter.
  • núcleo: Contiene clases como centro logístico LogsticsCenter y almacén Warehouse.
  • excepción: contiene algunas clases de excepción.
  • Subproceso: contiene CancellableCountDownLatch. La cadena interceptora de ARouter se ejecuta en el subproceso secundario, por lo que se utiliza.
  • facede: contiene devolución de llamada de navegación NavigationCallback e interceptor IInterceptor y otras interfaces.
  • utils: contiene la impresora de registros personalizada de ARouter y otras clases de herramientas.
  • base: solo se utiliza un UnitqueKeyTreeMap para guardar el interceptor.

Compilador

Compilador ARouter

El módulo Compilador se utiliza para generar tablas de enrutamiento. Los procesadores de anotaciones correspondientes a las anotaciones @Autowired, @Interceptor y @Route son AutowiredProcessor, InterceptorProcessor y RouteProcessor respectivamente, todos en el Compilador.

Registrar complemento

Complemento de registro ARouter

El módulo Register Plugin incluye el generador de códigos de registro RegisterCodeGenerator y RegisterTransform. Si se utiliza el complemento de carga de la tabla de enrutamiento ARouter, el complemento Register cargará la tabla de enrutamiento.

Anotación

Anotación del enrutador

El módulo Anntaion es relativamente simple y solo contiene algunas anotaciones y clases de enumeración.

Principio APT

ARouter es muy cómodo de usar gracias a la tecnología APT. La función de APT es escanear y procesar anotaciones en el código durante la fase de compilación y luego generar archivos Java basados ​​​​en las anotaciones.
ARouter también utiliza dos bibliotecas adicionales para facilitar la implementación de procesadores de anotaciones.

  • JavaPoet proporciona una forma de llamar a métodos de objetos para generar el código requerido, sin la necesidad de usar StringBuilder manualmente para unir el código y luego usar IO para escribir el archivo.
  • Auto-Service proporciona una forma sencilla de registrar APT, evitando los pasos de registro originalmente engorrosos.

@Ruta

Definición de @Ruta:

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

    /**
     * Path of route
     */
    String path();
    ......
}
  • @Target({ElementType.TYPE}): indica que esta anotación es una clase modificada
  • @Retention(RetentionPolicy.CLASS): indica que debe conservarse hasta el momento de la compilación

Hay una ruta de parámetro principal cuando se utiliza esta anotación:

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

De esta manera, la clase anotada con @Route se puede obtener durante la compilación y se puede obtener la ruta.

Procesador de ruta

RouteProcessor es el procesador de anotaciones correspondiente a la anotación @Route.

@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor 
  • La biblioteca de Autoservicio completa el registro automático del Procesador.
  • @SupportedAnnotationTypes ({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED}): indica qué anotaciones procesa el procesador actual.

RouteProcessor hereda de BaseProcessor y obtiene el nombre del módulo de cada módulo en el método 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));
        }

El método de proceso de RouteProcessor es donde se procesan las anotaciones y obtiene directamente todos los elementos anotados con @Route.

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

Después de obtener el elemento anotado, ingresará el método this.parseRoutes(routeElements). Este método utiliza JavaPoet para generar archivos Java. Si no utiliza esta biblioteca, también puede utilizar StringBuilder para escribir el contenido de los archivos Java.

Grupo de rutas

Primero echemos un vistazo a los productos generados por RouteProcessor. Puede ver los productos generados por ARouter en la ruta a continuación.
imagen.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 contiene la información necesaria para los elementos anotados con @Route, el más obvio es YourActivity.class, con él podemos saltar a esta Actividad a través de Intent.

La clase ARouter$$Group$$test hereda de IRouteGroup e implementa el método loadInto en la interfaz.

La lógica del método loadInto es muy simple: pase un mapa, use el valor de la ruta anotada como clave y coloque el elemento (RouteMeta) como valor en el mapa. Si se completa este método, se completa el registro de la Actividad.

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 implementa la interfaz IRouteRoot y el contenido es muy similar. Mediante el método loadInto, inserte el contenido con el nombre del grupo como Clave y la clase de implementación IRouteGroup como Valor en el Mapa.

De forma predeterminada, grupo es el contenido después de la primera barra en la ruta (@Route(path="/group/xxx"))

Si se llama a este método, puede obtener la clase de implementación IRouteGroup a través del grupo. Después de crear una instancia de la clase, puede obtener Activity.class a través del método mencionado anteriormente.

La estructura general se muestra en la siguiente figura:
imagen.png

RutaProcesador.proceso()

Regrese y continúe mirando el método de proceso 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;
    }

Todos los elementos anotados con @Route se obtienen y se colocan en el método parseRoutes para generar IRouteGroup e IRouteRoot. Esto utiliza las clases proporcionadas por JavaPoet para generar código en forma de llamadas a métodos.

Generación de tabla de enrutamiento

El método de proceso de RouteProcessor procesa las clases declaradas con la anotación @Route, que se divide aproximadamente en cuatro pasos:
1. Obtener elementos de enrutamiento
2. Crear metainformación de enrutamiento
3. Agrupar metainformación de enrutamiento
4. Generar archivos de enrutamiento

En el análisis anterior, todos los elementos anotados con @Route se obtuvieron a través de roundEnv.getElementsAnnotatedWith() y luego se colocaron en el método parseRoutes.

La metainformación de enrutamiento mencionada aquí se refiere a RouteMeta. RouteProcessor asociará la Actividad, Proveedor, Servicio o Fragmento declarado con la anotación @Route con 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);
}

En este código se completa la construcción de la clase RouteMeta y también se obtienen los parámetros recibidos por la Actividad a través de la anotación @AutoWired. Luego agrupe todos los RouteMeta mediante el método de categorías (routeMeta).

¿Por qué grupo? A medida que el proyecto se itera, la cantidad de componentes aumentará. Poner tanta información de componentes en un mapa obviamente causará un gran problema en la memoria y el tiempo de carga también aumentará. El método adoptado por Arouter es "agrupación + carga bajo demanda" y, al mismo tiempo, la agrupación también es conveniente para la gestión.

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

Hay un mapa de grupo en el RouteProcessor. Después de crear el RouteMeta, el RouteProcessor agrupará los grupos según su grupo como clave y los colocará en el mapa de grupo. El propio RouteMeta se colocará en un Conjunto. Los grupos de todos los RouteMeta en el Conjunto son los mismos y sirven como Valor del Mapa.

Después de que RouteProcessor agrupe RouteMeta, utilizará JavaPoet para generar archivos de enrutamiento de grupo, proveedor y raíz. La tabla de enrutamiento se compone de estos archivos. JavaPoet es el marco de generación de código fuente abierto 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);

Los archivos de enrutamiento generados son los productos del RouteProcessor visto anteriormente, que son los siguientes:
imagen.png

Carga de la tabla de enrutamiento

Cargar la tabla de enrutamiento en realidad es cargar el archivo de clase generado por RouteProcessor.

Al llamar al método de inicialización init () de ARouter, ARouter llamará al método init () de LogisticsCenter. En este método, loadRouterMap () cargará la tabla de enrutamiento a través del complemento primero y luego determinará si la tabla de enrutamiento actual se está cargando. El método es un complemento. Si no, cargue la tabla de enrutamiento desde 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() + "]");
        }
    }

Cargar tabla de enrutamiento desde Dex

Cargar tabla de enrutamiento desde Dex

El proceso de carga de la tabla de enrutamiento a través de Dex es aproximadamente como se muestra en la figura anterior. A continuación, echemos un vistazo a la carga de la tabla de enrutamiento desde Dex en el método init () de LogisticsCenter paso a paso:

                // 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 el modo de depuración está configurado o es una versión nueva, escanee todos los archivos Dex para encontrar todos los archivos que comiencen con com.alibaba.android.arouter.routes y luego actualice a SharePreferences. De lo contrario, lea el caché directamente desde SharePreferences para reducir el tiempo de análisis.

ClassUtils se utiliza aquí para leer el archivo Dex y leer la tabla de enrutamiento del archivo 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);
                    }
                }

Después de guardar la tabla de enrutamiento en SharedPreferences, determinará si la clase es IRouteRoot, IInterceptorGroup o IProviderGroup según el sufijo del nombre de la clase, y luego la instanciará en diferentes objetos y llamará al método loadInto para cargar el contenido del archivo de clase. El índice.

Cargar tabla de enrutamiento mediante complemento

Si desea acortar el tiempo de inicialización de ARouter, puede utilizar el complemento Gradle de ARouter, que puede cargar automáticamente la tabla de enrutamiento, de modo que ARouter no necesite leer información de clase durante la inicialización, acortando así el tiempo de inicialización. tiempo.
Utilice el complemento Gradle para implementar la carga automática de tablas de enrutamiento
El principio de funcionamiento del complemento es que cuando se compila el código, el complemento encontrará el método loadRouterMap de la clase LogisticsCenter y luego insertará el código relacionado con el enrutamiento en el método, de modo que la tabla de enrutamiento no se escanee. del archivo dex durante la inicialización.

Salto de la tabla de enrutamiento

Utilice ARouter.getInstance().build(“/test/activity”).navigation() para iniciar una operación de enrutamiento y saltar, que llamará al método de navegación() de _ARouter.
Hay dos sobrecargas del método de navegación() de _ARouter. Uno se utiliza para crear servicios y el otro para enrutar saltos.

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

El proceso de creación de un servicio es relativamente simple: cree una postal a través de LogisticsCenter.buildProvider, incluida la información más básica del grupo y la ruta, luego configure el contexto para la postal y, finalmente, obtenga la metainformación de ruta a través de LogisticsCenter.completion y complétela. en Postal.

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

El proceso de salto de enrutamiento es un poco más complicado y consta aproximadamente de los siguientes pasos: 1. Servicio de preprocesamiento, 2. Mejora de la postal, 3. Mejora de la estrategia de degradación de fallas, 4. Enlace interceptor, 5. Salto por tipo.

Servicios de preprocesamiento

El servicio de preprocesamiento nos permite determinar si procesar el salto de forma independiente en función del contenido de PostCard antes del salto de ARouter. Si es así, devuelve falso en onPretreatment().

Para implementar el servicio de pretratamiento, solo necesita implementar la interfaz PretreatmentService y agregar una anotación @Route con cualquier contenido de ruta.

Si se implementa el servicio de preprocesamiento y onPretreatment() devuelve falso, el proceso de salto se interrumpirá y el procesamiento no continuará.

Mejorar Postal

Después de llamar al servicio de preprocesamiento, _ARouter utilizará LogisticsCenter para cargar la tabla de enrutamiento, que es el archivo de enrutamiento generado por RouteProcessor.

Cuando se inicializa _ARouter, LogisticsCenter también se inicializará. En el método de inicialización de LogisticsCenter, se leerá la tabla de enrutamiento creada por RouteProcessor y luego se colocará en el índice correspondiente.

Con el índice, cuando _ARouter llama al método de finalización () de LogisticsCenter, puede usar el índice para obtener metainformación de ruta de las rutas del Almacén.

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

Primero, si LogisticsCenter no puede encontrar el RouteMeta correspondiente según el índice, significa que las rutas no se han completado. En este momento, LogisticsCenter obtendrá el RouteMeta del grupo, luego completará la ruta debajo del grupo en rutas y luego llamará. complete() nuevamente. En este momento, puede recuperar la información para completar la postal.

Con routeMeta, obtenga el valor de routeMeta, configúrelo en el atributo de postal, analice los parámetros en el Uri, configúrelo en el atributo de postal y configúrelo en Bundle para saltos posteriores.

Después de completar la información de la postal, LogisticsCenter realizará diferentes operaciones según el tipo de postal, y cuando el tipo sea PROVEEDOR y FRAGMENTO, configurará el canal verde de la postal, lo que en realidad significa que el Proveedor y el Fragmento omitirán el enlace del interceptor.

Mejorar la estrategia de degradación de fallas

Cuando ARouter encuentra una excepción en el proceso de mejorar la información de la postal, si se pasa NavigationCallback durante la navegación, se volverá a llamar a onLost por este error de salto. Si no hay devolución de llamada, se utilizará la estrategia de degradación global:

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

De manera similar al servicio de preprocesamiento, para implementar la estrategia de degradación, solo necesita implementar la interfaz DegradeService y agregar una anotación @Route con cualquier contenido de ruta.

enlace interceptor

Cuando no sea greenChannel, ingresará al enlace del interceptor; de lo contrario, saltará directamente a escribir. Al mejorar la Postal anterior, el Proveedor y el Fragmento configuran greenChannel, que omitirá el enlace del interceptor. Por supuesto, también puede llamar al método greenChannel() antes del método de navegación() para permitir que el salto omita el enlace del interceptor.

Los interceptores se pueden utilizar para procesar eventos durante los saltos, como comprobaciones de inicio de sesión. Los interceptores se ejecutarán entre saltos y se ejecutarán varios interceptores en orden de prioridad.

Para implementar un interceptor, solo necesita implementar la interfaz IInterceptor y usar la anotación @Interceptor (prioridad = 8, nombre = "xxxx"). La prioridad es la prioridad del interceptor. Se ejecutarán varios interceptores en orden de prioridad.

El método onContinue () o onInterrupt () debe llamarse en el método de proceso y al menos uno de estos métodos debe llamarse; de ​​lo contrario, el proceso de enrutamiento no continuará. onContinue () indica que el procesamiento se completa y el control se intercambia a ARouter, onInterrupt () indica que ocurre una excepción y el proceso de enrutamiento debe interrumpirse.

Saltar por tipo

Una vez procesado el interceptor, se llamará al método _navigation() en navegación(), que también es el método específico para saltar. En este método, se juzgará según el tipo de ruta de Postcard y el salto se basará en el tipo.

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

Cuando RouteType es Actividad, el proceso de iniciar la Actividad es el mismo que el proceso habitual de iniciar la Actividad: crear el Intent, pasar el destino, pasar los parámetros extras, configurar las banderas, configurar la acción y finalmente llamar. startActivity() en el hilo principal para iniciar la página de destino.

Cuando RouteType es Proveedor, es decir, si es un servicio personalizado, se devuelve el Proveedor en Postal. El servicio personalizado en ARouter encuentra el servicio de descubrimiento mediante inyección de dependencia.

Cuando RouteType es Fragment, Broadcast o ContentProvider, la instancia se creará utilizando la reflexión a través del destino. Si es Fragment, ARouter también establecerá parámetros para ella y luego devolverá la instancia.

Artículo de referencia:
https://juejin.cn/post/6885932290615509000

Supongo que te gusta

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