Android 组件化使用 ARoute 实现组件化 ( ARoute 初始化 及 界面跳转 源码分析 )

Posted 韩曙亮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 组件化使用 ARoute 实现组件化 ( ARoute 初始化 及 界面跳转 源码分析 )相关的知识,希望对你有一定的参考价值。

在这里插入图片描述

组件化系列博客 :





一、ARoute 初始化源码分析



引入了 ARoute 的应用 , 一般会在主应用的 Application 中的 onCreate 方法中初始化 ARoute ;

package kim.hsl.component;

import android.app.Application;
import com.alibaba.android.arouter.launcher.ARouter;

public class MyApplication extends Application {

    public static MyApplication mApplication;
    public MyApplication() {
        super();
        mApplication = this;
    }
    public static MyApplication getInstance() {
        return mApplication;
    }

    @Override
    public void onCreate() {
        super.onCreate();

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

    public boolean isDebug(){
        return BuildConfig.DEBUG;
    }
}

其中

ARouter.init(this);

是 ARoute 初始化的核心方法 , 其调用了 com.alibaba.android.arouter.launcher.ARouter 类的 init 方法 , com.alibaba.android.arouter.launcher.ARouter 类是一个空壳类 , 实际的代码逻辑都定义在 com.alibaba.android.arouter.launcher._ARouter 类中 ;

package com.alibaba.android.arouter.launcher;

public final class ARouter {
    /**
     * Init, it must be call before used router.
     */
    public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            _ARouter.logger.info(Consts.TAG, "ARouter init start.");
            hasInit = _ARouter.init(application);

            if (hasInit) {
                _ARouter.afterInit();
            }

            _ARouter.logger.info(Consts.TAG, "ARouter init over.");
        }
    }
}

在 ARoute 类的 init 方法中调用了 _ARoute 的 init 方法

_ARouter.init(application);

_ARoute 相关源码如下 :

final class _ARouter {
    protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        logger.info(Consts.TAG, "ARouter init success!");
        hasInit = true;
        mHandler = new Handler(Looper.getMainLooper());

        return true;
    }
}

其中的

LogisticsCenter.init(mContext, executor);

是核心的逻辑 , 在该方法中 , 加载了路由表 ,

路由表类是 注解处理器 在编译时生成的类 , 生成的目录是 " D:\\002_Project\\002_Android_Learn\\ComponentDemo\\app\\build\\generated\\ap_generated_sources\\debug\\out\\com\\alibaba\\android\\arouter\\routes " ;

在这里插入图片描述
生成的路由表示例 :

package com.alibaba.android.arouter.routes;

import com.alibaba.android.arouter.facade.enums.RouteType;
import com.alibaba.android.arouter.facade.model.RouteMeta;
import com.alibaba.android.arouter.facade.template.IRouteGroup;
import java.lang.Override;
import java.lang.String;
import java.util.Map;
import kim.hsl.component.MainActivity;

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$app implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/app/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/mainactivity", "app", null, -1, -2147483648));
  }
}

生成的对应的 Root 表 , 作为路由表的索引 ;

package com.alibaba.android.arouter.routes;

import com.alibaba.android.arouter.facade.template.IRouteGroup;
import com.alibaba.android.arouter.facade.template.IRouteRoot;
import java.lang.Class;
import java.lang.Override;
import java.lang.String;
import java.util.Map;

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("app", ARouter$$Group$$app.class);
  }
}

LogisticsCenter.init(mContext, executor) 方法执行的操作就是创建 ARouter$$Group$$app 类 , 调用该类的 loadInto 方法 , 导入路由表 , 将路由表加载到内存中 ;





二、ARoute 界面跳转源码分析



ARoute 使用时的示例如下 , 在该 Activity 类中 , 涉及到注解使用 , 界面跳转 ;

package kim.hsl.component;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;

import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;

// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/app/MainActivity")
public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onClick(View view) {
        Log.i(TAG, "跳转到 Module1");
        ARouter.getInstance().build("/module1/Module1Activity").navigation();
        finish();
    }
}

当调用到

ARouter.getInstance().build("/module1/Module1Activity").navigation();

代码进行界面跳转时 ,

public final class ARouter {
    /**
     * Build the roadmap, draw a postcard.
     *
     * @param path Where you go.
     */
    public Postcard build(String path) {
        return _ARouter.getInstance().build(path);
    }
}

ARouter.getInstance().build 方法实际上调用的是 _ARoute 的 build(String path) 方法 , 在 该方法中又调用了 build 的重载函数 build(String path, String group, Boolean afterReplace) , 最终返回了一个跳卡 Postcard 对象 ;

final class _ARouter {
    /**
     * Build postcard by path and default group
     */
    protected Postcard build(String path) {
        if (TextUtils.isEmpty(path)) {
        	// 不能传入空字符串地址 , 否则直接报异常  
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            // 核心是调用了 build 的重载函数 
            // extractGroup 函数的作用明显是取出路径中的分组 , 就是前两个 / 之间的字符串内容
            return build(path, extractGroup(path), true);
        }
    }
    
    /**
     * Build postcard by path and group
     */
    protected Postcard build(String path, String group, Boolean afterReplace) {
        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            if (!afterReplace) {
                PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
                if (null != pService) {
                    path = pService.forString(path);
                }
            }
            // 最终创建了一张跳卡 
            return new Postcard(path, group);
        }
    }
}

跳转的实际操作是调用的跳卡的 navigation 方法 , 此时该跳卡中 , 只有 path 路由地址 , group 路由分子名称 , 其它信息还是空的 ;

public final class Postcard extends RouteMeta {
    public Postcard(String path, String group) {
        this(path, group, null, null);
    }
    /**
     * Navigation to the route with path in postcard.
     * No param, will be use application context.
     */
    public Object navigation() {
        return navigation(null);
    }
    /**
     * Navigation to the route with path in postcard.
     *
     * @param context Activity and so on.
     */
    public Object navigation(Context context) {
        return navigation(context, null);
    }
    /**
     * Navigation to the route with path in postcard.
     *
     * @param context Activity and so on.
     */
    public Object navigation(Context context, NavigationCallback callback) {
        return ARouter.getInstance().navigation(context, this, -1, callback);
    }
}

其中的 NavigationCallback 参数 , 传入一个监听回调接口 , 可以监听跳转操作是否完成了 ;

/**
 * Callback after navigation.
 *
 * @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a>
 * @version 1.0
 * @since 2016/9/22 14:15
 */
public interface NavigationCallback {

    /**
     * Callback when find the destination.
     *
     * @param postcard meta
     */
    void onFound(Postcard postcard);

    /**
     * Callback after lose your way.
     *
     * @param postcard meta
     */
    void onLost(Postcard postcard);

    /**
     * Callback after navigation.
     *
     * @param postcard meta
     */
    void onArrival(Postcard postcard);

    /**
     * Callback on interrupt.
     *
     * @param postcard meta
     */
    void onInterrupt(Postcard postcard);
}

调用 Postcard 跳卡对象的 navigation() 方法 , 之后调用了一系列的 navigation 重载方法 , navigation(Context context) , navigation(Context context, NavigationCallback callback) , 最终又调用了 ARouter.getInstance().navigation(context, this, -1, callback) 方法 ;

在 ARouter 的 navigation 方法中 , 依旧是调用了 _ARoute 的 navigation 方法 ;

public final class ARouter {
    /**
     * Launch the navigation.
     *
     * @param mContext    .
     * @param postcard    .
     * @param requestCode Set for startActivityForResult
     * @param callback    cb
     */
    public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
        return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
    }
}

跳卡 Postcard 对象中目前有 路由地址 , 路由分组 两个信息 , 先调用

LogisticsCenter.completion(postcard);

函数, 将跳卡 Postcard 对象补全 , 最重要的是该路由地址对应的 Class 类对象 , 通过之前 ARoute 初始化到内存的路由表补充跳卡中的数据 ;

然后判断该跳转是否是绿色通道 , 如果是继续执行跳转 ;

如果不是 , 则触发拦截器 , 拦截器判定未通过 , 则中断跳转 , 拦截器判定通过 , 则继续执行跳转 ;

这里的拦截器一般用于权限鉴定 , 比如用户是否购买会员 , 是否购买服务 , 是否拥有权限等等 ;

拦截器的详细用法自行去 GitHub 上查看 ;

最终调用 _navigation 方法进行跳转 ;

在 _navigation 方法中 , 跳卡 Postcard 的 Activity 跳转就是 创建 Intent , 并执行 startActivity 方法进行界面跳转 ;

final class _ARouter {
    /**
     * Use router navigation.
     *
     * @param context     Activity or null.
     * @param postcard    Route metas
     * @param requestCode RequestCode
     * @param callback    cb
     */
    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;
        }

        try {
        	// 补充跳卡数据 
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());

            if (debuggable()) {
                // Show friendly tips for user.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(mContext, "There's no route matched!\\n" +
                                " Path = [" + postcard.getPath() + "]\\n" +
                                " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                    }
                });
            }

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

            return null;
        }
	
		// 此处说明跳卡的数据已经补全 , 继续进行后续跳转工作 

        if (null != callback) {
            callback.onFound(postcard);
        }

        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(context, 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 : " + exceptionAndroid Gradle 插件组件化中的 Gradle 构建脚本实现 ④ ( 使用路由实现组件间通信 | 引入 ARoute 框架 | Gradle 构建脚本优化问题 )

错误记录Android Studio 集成 ARoute 编译报错 ( 兼容 support 库和 androidx 库 | add ‘tools:replace=“android:appCo )

Android 组件化路由组件 ( 路由框架概述 )

Android 组件化路由组件 ( 注解处理器调试 )

Android 组件化路由组件 ( 注解处理器中使用 JavaPoet 生成代码 )

Android 组件化使用 Gradle 实现组件化 ( 组件模式与集成模式切换 )