IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 2021Android进阶学习资料,ARouter 源码解析:阿里推出的路由框架 -> 正文阅读

[移动开发]2021Android进阶学习资料,ARouter 源码解析:阿里推出的路由框架

            } catch (Throwable ignore) {

                Log.e("ARouter", "Scan map file in dex files made error.", ignore);

            } finally {

                if (null != dexfile) {

                    try {

                        dexfile.close();

                    } catch (Throwable ignore) {

                    }

                }

                parserCtl.countDown();

            }

        }

    });

}

// 所有 path 处理完成后,继续向下走

parserCtl.await();

Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");

return classNames;

}




这里的步骤比较简单,主要是如下的步骤:



1.  通过?`getSourcePaths`?方法获取 dex 文件的 path 集合。

2.  创建了一个?`CountDownLatch`?控制 dex 文件的并行处理,以加快速度。

3.  遍历 path 列表,通过?`DefaultPoolExecutor`?对 path 并行处理。

4.  加载 path 对应的 dex 文件,并对其中的 Entry 进行遍历,若发现了对应 package 下的 ClassName,将其加入结果集合。

5.  所有 dex 处理完成后,返回结果。



关于?`getSourcePaths`?如何获取到的 dex 集合这里就不纠结了,因为我们的关注点不在这里。



### [](
)初始化 Warehouse



`Warehouse`?实际上就是仓库的意思,它存放了?`ARouter`?自动生成的类(`RouteRoot`、`InterceptorGroup`、`ProviderGroup`)的信息。



我们先看看?`Warehouse`?类究竟是怎样的:



class Warehouse {

// 保存 RouteGroup 对应的 class 以及 RouteMeta

static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();

static Map<String, RouteMeta> routes = new HashMap<>();



// 保存 Provider 以及 RouteMeta

static Map<Class, IProvider> providers = new HashMap<>();

static Map<String, RouteMeta> providersIndex = new HashMap<>();



// 保存 Interceptor 对应的 class 以及 Inteceptor

static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");

static List<IInterceptor> interceptors = new ArrayList<>();



static void clear() {

    routes.clear();

    groupsIndex.clear();

    providers.clear();

    providersIndex.clear();

    interceptors.clear();

    interceptorsIndex.clear();

}

}




可以发现?`Warehouse`?就是一个纯粹用来存放信息的仓库类,它的数据的实际上是通过上面的几个自动生成的类在?`loadInto`?中对?`Warehouse`?主动填入数据实现的。



例如我们打开一个自动生成的?`IRouteRoot`?的实现类:



public class ARouter R o o t Root Roothomework implements IRouteRoot {

@Override

public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {

routes.put("homework", ARouter$$Group$$homework.class);

}

}




可以看到,它在?`groupsIndex`?中对这个?`RouteRoot`?中的?`IRouteGroup`?进行了注册,也就是向?`groupIndex`?中注册了 Route Group 对应的?`IRouteGroup`?类。其他类也是一样,通过自动生成的代码将数据填入?`Map`?或?`List`?中。



可以发现,初始化过程主要完成了对自动生成的路由相关类?`RouteRoot`、`Interceptor`、`ProviderGroup`?的加载,对它们通过反射构造后将信息加载进了?`Warehouse`?类中。



[](
)路由跳转

-----------------------------------------------------------------------



### [](
)Postcard 的创建



下面我们看看路由的跳转是如何实现的,我们先看到?`ARouter.build`?方法:



public Postcard build(String path) {

    return _ARouter.getInstance().build(path);

}




它转调到了?`_ARouter`?的?`build`?方法:



protected Postcard build(String path) {

if (TextUtils.isEmpty(path)) {

    throw new HandlerException(Consts.TAG + "Parameter is invalid!");

} else {

    PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);

    if (null != pService) {

        path = pService.forString(path);

    }

    return build(path, extractGroup(path));

}

}




它首先通过?`ARouter.navigation`?获取到了?`PathReplaceService`,它需要用户进行实现,若没有实现会返回?`null`,若有实现则调用了它的?`forString`?方法传入了用户的 Route Path 进行路径的预处理。



最后转调到了?`build(path, group)`,`group`?通过?`extractGroup`?得到:



private String extractGroup(String path) {

if (TextUtils.isEmpty(path) || !path.startsWith("/")) {

    throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");

}

try {

    String defaultGroup = path.substring(1, path.indexOf("/", 1));

    if (TextUtils.isEmpty(defaultGroup)) {

        throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");

    } else {

        return defaultGroup;

    }

} catch (Exception e) {

    logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());

    return null;

}

}




`extractGroup`?实际上就是对字符串处理,取出 Route Group 的名称部分。



protected Postcard build(String path, String group) {

if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {

    throw new HandlerException(Consts.TAG + "Parameter is invalid!");

} else {

    PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);

    if (null != pService) {

        path = pService.forString(path);

    }

    return new Postcard(path, group);

}

}




`build(path, group)`?方法同样也会尝试获取到?`PathReplaceService`?并对?`path`?进行预处理。之后通过?`path`?与?`group`?构建了一个?`Postcard`?类:



public Postcard(String path, String group) {

   this(path, group, null, null);

}

public Postcard(String path, String group, Uri uri, Bundle bundle) {

setPath(path);

setGroup(group);

setUri(uri);

this.mBundle = (null == bundle ? new Bundle() : bundle);

}




这里最终调用到了?`PostCard(path, group, uri, bundle)`,这里只是进行了一些参数的设置。



之后,如果我们调用?`withInt`、`withDouble`?等方法,就可以进行参数的设置。例如?`withInt`?方法:



public Postcard withInt(@Nullable String key, int value) {

mBundle.putInt(key, value);

return this;

}




它实际上就是在对?`Bundle`?中设置对应的?`key`、`value`。



最后我们通过?`navigation`?即可实现最后的跳转:



public Object navigation() {

return navigation(null);

}

public Object navigation(Context context) {

return navigation(context, null);

}

public Object navigation(Context context, NavigationCallback callback) {

return ARouter.getInstance().navigation(context, this, -1, callback);

}

public void navigation(Activity mContext, int requestCode) {

navigation(mContext, requestCode, null);

}

public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {

ARouter.getInstance().navigation(mContext, this, requestCode, callback);

}




通过如上的?`navigation`?可以看到,实际上它们都是最终调用到?`ARouter.navigation`?方法,在没有传入?`Context`?时会使用?`Application`?初始化的?`Context`,并且可以通过?`NavigationCallback`?对?`navigation`?的过程进行监听。



public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {

return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);

}




`ARouter`?仍然只是将请求转发到了?`_ARouter`:



protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {

try {

        // 通过 LogisticsCenter.completion 对 postcard 进行补全

    LogisticsCenter.completion(postcard);

} catch (NoRouteFoundException ex) {

    // ...

}

if (null != callback) {

    callback.onFound(postcard);

}

// 如果设置了 greenChannel,会跳过所有拦截器的执行

if (!postcard.isGreenChannel()) {   

        // 没有跳过拦截器,对 postcard 的所有拦截器进行执行

    interceptorService.doInterceptions(postcard, new InterceptorCallback() {

        @Override

        public void onContinue(Postcard postcard) {

            _navigation(context, postcard, requestCode, callback);

        }



        @Override

        public void onInterrupt(Throwable exception) {

            if (null != callback) {

                callback.onInterrupt(postcard);

            }

            logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());

        }

    });

} else {

    return _navigation(context, postcard, requestCode, callback);

}

return null;

}




上面的代码主要有以下步骤:



1.  通过?`LogisticsCenter.completion`?对?`postcard`?进行补全。

2.  如果?`postcard`?没有设置?`greenChannel`,则对?`postcard`?的拦截器进行执行,执行完成后调用?`_navigation`?方法真正实现跳转。

3.  如果?`postcard`?设置了?`greenChannel`,则直接跳过所有拦截器,调用?`_navigation`?方法真正实现跳转。



### [](
)Postcard 的补全



我们看看?`LogisticsCenter.completion`?是如何实现?`postcard`?的补全的:



public synchronized static void completion(Postcard postcard) {

if (null == postcard) {

    throw new NoRouteFoundException(TAG + "No postcard!");

}

// 通过 Warehouse.routes.get 尝试获取 RouteMeta

RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());

if (null == routeMeta) {

        // 若 routeMeta 为 null,可能是并不存在,或是还没有加载进来

        // 尝试获取 postcard 的 RouteGroup

    Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.

    if (null == groupMeta) {

        throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");

    } else {

            // ...

        // 如果找到了对应的 RouteGroup,则将其加载进来并重新调用 completion 进行补全

        IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();

        iGroupInstance.loadInto(Warehouse.routes);

        Warehouse.groupsIndex.remove(postcard.getGroup());

        // ...

        completion(postcard);   // Reload

    }

} else {

        // 如果找到了对应的 routeMeta,将它的信息设置进 postcard 中

    postcard.setDestination(routeMeta.getDestination());

    postcard.setType(routeMeta.getType());

    postcard.setPriority(routeMeta.getPriority());

    postcard.setExtra(routeMeta.getExtra());

    Uri rawUri = postcard.getUri();

            // 将 uri 中的参数设置进 bundle 中

    if (null != rawUri) {

        Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);

        Map<String, Integer> paramsType = routeMeta.getParamsType();

        if (MapUtils.isNotEmpty(paramsType)) {

            // Set value by its type, just for params which annotation by @Param

            for (Map.Entry<String, Integer> params : paramsType.entrySet()) {

                setValue(postcard,

                        params.getValue(),

                        params.getKey(),

                        resultMap.get(params.getKey()));

            }

            // Save params name which need auto inject.

            postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));

        }

        // Save raw uri

        postcard.withString(ARouter.RAW_URI, rawUri.toString());

    }

    // 对于 provider 和 fragment,进行特殊处理

    switch (routeMeta.getType()) {

        case PROVIDER:

                // 如果是一个 provider,尝试从 Warehouse 中查找它的类并构造对象,然后将其设置到 provider

            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();

            IProvider instance = Warehouse.providers.get(providerMeta);

            if (null == instance) { // There's no instance of this provider

                IProvider provider;

                try {

                    provider = providerMeta.getConstructor().newInstance();

                    provider.init(mContext);

                    Warehouse.providers.put(providerMeta, provider);

                    instance = provider;

                } catch (Exception e) {

                    throw new HandlerException("Init provider failed! " + e.getMessage());

                }

            }

            postcard.setProvider(instance);

            // provider 和 fragment 都会跳过拦截器

            postcard.greenChannel();

            break;

        case FRAGMENT:

                 // provider 和 fragment 都会跳过拦截器

            postcard.greenChannel();

        default:

            break;

    }

}

}




这个方法主要完成了对?`postcard`?的信息与?`Warehouse`?的信息进行结合,以补全?`postcard`?的信息,它的步骤如下:



1.  通过?`Warehouse.routes.get`?根据?`path`?尝试获取?`RouteMeta`?对象。

2.  若获取不到?`RouteMeta`?对象,可能是不存在或是还没有进行加载(第一次都未加载),尝试获取?`RouteGroup`?调用其?`loadInto`?方法将?`RouteMeta`?加载进?`Warehouse`,最后调用?`completion`?重新尝试补全 。

3.  将?`RouteMeta`?的信息设置到?`postcard`?中,其中会将?`rawUri`?的参数设置进?`Bundle`。

4.  对于?`Provider`?和?`Fragment`?特殊处理,其中?`Provider`?会从?`Warehouse`?中加载并构造它的对象,然后设置到?`postcard`。`Provider`?和?`Fragment`?都会跳过拦截器。



`RouteGroup`?的?`loadInto`?仍然是自动生成的,例如下面就是一些自动生成的代码:



public void loadInto(Map<String, RouteMeta> atlas) {

atlas.put("/homework/commit", RouteMeta.build(RouteType.ACTIVITY, HomeworkCommitActivity.class, “/homework/commit”, “homework”, null, -1, -2147483648));

// ...

}




它包括了我们补全所需要的如?`Destination`、`Class`、`path`?等信息,在生成代码时自动根据注解进行生成。



### [](
)执行跳转



我们看看?`navigation`?方法是如何实现的跳转:



private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {

final Context currentContext = null == context ? mContext : context;

switch (postcard.getType()) {

    case ACTIVITY:

        // 对 Activity,构造 Intent,将参数设置进去

        final Intent intent = new Intent(currentContext, postcard.getDestination());

        intent.putExtras(postcard.getExtras());

        // Set flags.

        int flags = postcard.getFlags();

        if (-1 != flags) {

            intent.setFlags(flags);

        } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.

            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        }

        // 切换到主线程,根据是否需要 result 调用不同的 startActivity 方法

        new Handler(Looper.getMainLooper()).post(new Runnable() {

            @Override

            public void run() {

                if (requestCode > 0) {  // Need start for result

                    ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());

                } else {

                    ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());

                }

                if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.

                    ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());

                }

                if (null != callback) { // Navigation over.

                    callback.onArrival(postcard);

                }

            }

        });

        break;

    case PROVIDER:

            // provider 直接返回对应的 provider

        return postcard.getProvider();

    case BOARDCAST:

    case CONTENT_PROVIDER:

    case FRAGMENT:

            // 对于 broadcast、contentprovider、fragment,构造对象,设置参数后返回

        Class fragmentMeta = postcard.getDestination();

        try {

            Object instance = fragmentMeta.getConstructor().newInstance();

            if (instance instanceof Fragment) {

                ((Fragment) instance).setArguments(postcard.getExtras());

            } else if (instance instanceof android.support.v4.app.Fragment) {

                ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());

            }

            return instance;

        } catch (Exception ex) {

            logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));

        }

    case METHOD:

    case SERVICE:

    default:

        return null;

}

return null;

}




可以发现,它会根据?`postcard`?的?`type`?来分别处理:



*   对于?`Activity`,会构造一个?`Intent`?并将之前?`postcard`?中的参数设置进去,之后会根据是否需要 result 调用不同的?`startActivity`?方法。

*   对于?`Provider`,直接返回其对应的?`provider`?对象。

*   对于?`Broadcast`、`ContentProvider`、`Fragment`,反射构造对象后,将参数设置进去并返回。



可以发现?`ARouter`?的初始化和路由跳转的整体逻辑还是不难的,实际上就是对?`Activity`、`Fragment`?的调转过程进行了包装。



[](
)Service 的获取

------------------------------------------------------------------------------



ARouter 除了可以通过?`ARouter.getInstance().build().navigation()`?这样的方式实现页面跳转之外,还可以通过?`ARouter.getInstance().navigation(XXService.class)`?这样的方式实现跨越组件的服务获取,我们看看它是如何实现的:



public T navigation(Class<? extends T> service) {

return _ARouter.getInstance().navigation(service);

}




仍然跳转到了?`_ARouter`?中去实现:



protected T navigation(Class<? extends T> service) {

try {

    Postcard postcard = LogisticsCenter.buildProvider(service.getName());
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-05 11:07:57  更:2021-09-05 11:10:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:45:16-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码