你们好, 此文用一个较详细的叙述来介绍 Android
的组件化框架 Component, 我从 17 年开始设计而且研究组件化框架的. 以及和其余框架相比, 为何更优秀, 更好用。下文且听我细细道来~java
其实最简短的介绍就是下面几件事:git
Activity
跳转 和 Fragment
获取其实上面的模块交互中, 核心是 跨模块调用
, 可是为何路由跳转也一样重要呢?并且要单拎出来.github
是由于在 Android
环境中, 处处充斥着 Activity
跳转、获取Fragment
. 这种操做特别多. 若是你利用跨模块调用去作, 你会发现虽然能作, 可是十分的不方便, 并且不人性化, 因此路由跳转也一样重要web
其实只要解决了最根本的跨模块调用功能的都算组件化框架。下面的分析不带有攻击性~~~网络
好比 CC, 它的设计就是解决了跨模块调用的. 它的出发点就是基于解决 跨模块调用这点
. 它将这方面作得很好.框架
基本上除了它以外的其余框架, 都是基于 URI
设计的. 会比较侧重于路由方面的功能, 跨模块调用为辅. 好比ide
Component
介绍Component
除了实现了基本的路由跳转, 跨服务调用等基本功能外. 还有不少使人欣喜的功能svg
ActivityResult
咱们不少人均可能遇到一个不是常常遇到的问题, 也就是在某一个地方( Adapter
, Dialog
,Service
), 突然要跳转一个界面去获取这个界面返回的 ActivityResult
, 这个不少人都会说, 可使用 EventBus
之类的库呀. 那你赢了好吧. 确实能够用, 可是不鼓励工具
若是不借助任何的库, 你得先拿到 Activity
去调用 startActivityForResult
, 而后重写 Activity
的 onActivityResult
方法去获取到 Intent
进而拿到数据, 传给须要的地方. 流程图以下:组件化
很明显, 代码不只写得多, 并且彻底没有技术含量, 写多了真的会吐的有没有. 并且每次还得和 Activity
创建接口或者方法的交互.
那 Component
是怎么作的呢?
Router .with(getContext()) .host("component1") .path("main") .putString("name", "cxj") .putString("pass", "123") .requestCode(456) // requestCode .forwardForResult(new BiCallback.BiCallbackAdapter<ActivityResult>() { @Override public void onSuccess(@NonNull RouterResult result, @NonNull ActivityResult activityResult) { super.onSuccess(result, activityResult); // 你就拿到了 ActivityResult } });
Activity
交互的环节,Component
可让你的代码更加的紧凑, 不会由于你须要 startActivityForResult
而分割你的代码.是否是很爽?会很好的让你写代码心情愉悦. 内部实现原理其实和 Glide
和 RxPermission
一致, 都是经过预埋一个 Fragment
实现的.
固然了这部分代码其实能够抽取出来作成一个工具类啥的, 有兴趣的能够研究下. 并非只能依赖 Component
才能拥有此功能
什么是页面拦截器
?
咱们知道不少框架都有拦截器
的概念. 页面拦截器
说白了. 就是只对某些路由请求有效的拦截器. 画图以下
上图能够很清晰的说明, 当你的路由是 router://order/detail
你目标是订单详情界面, 会通过两个全局拦截器.
当框架发现你跳转的界面有一个 登录拦截器
( 页面拦截器
), 则框架就会先执行登录拦截器, 登录拦截器会完成整个登录的过程, 在登录成功以后, 自动完成以前的跳转.
上图解决了一个啥问题呢?
你在平时的写代码中, 你处理这些场景只有两种方式:
在每一次跳转以前先解决好跳转到目标界面的先决条件
在目标界面进行须要的先决条件的处理.
两种方式都有各自的缺点, 都挺恶心的. Component
呢就很巧妙的在跳转前处理了, 可是却不用发起的界面多写任何一句代码.
有些人说 ARouter
也能实现啊, 那么区别和优点在哪里呢?
Arouter
每个用注解声明的拦截器都是全局拦截器, 也就是说每个路由都会执行. 而 Component
的页面拦截器只有当最终跳转的目标是某个界面的时候, 某个界面上标记的拦截器才会被执行. 因此页面拦截器不会每个路由都会执行到.ARouter
当判断到须要登陆的时候, 会帮你跳转到登陆, 本次跳转就结束了. 当你完成登录以后, 你须要再次发起跳转.Component
在执行到某个拦截器, 拦截器会负责帮你完成须要的工做, 而后才会继续以前的跳转. 不用用户去再次发起跳转.此处展现一个订单详情的标记范例:
@RouterAnno( // host 可省略不写 host = "order", path = "detail", // 页面拦截器(此处是一个登录拦截器, 完成自动登录) interceptorNames = "user.login", desc = "订单详情界面" ) public class OrderDetailAct extends AppCompatActivity{ // ........ }
Intent
标记第三方界面或者系统界面咱们用过组件化框架的人都知道, 全部组件化框架都是针对项目中的 Activity
能够经过框架路由. 可是系统和第三方的界面就不能囊括在内. 由于基于 URI
设计的那些组件化框架都无法对系统的界面进行一个路由标记. 因此无法跳转. 可是其实这又是一个颇有必要的功能.
因此 Component
支持你自定义 Intent
. 以下就是自定义了一个静态方法, 返回系统的拍照界面的 Intent
, 而且对拍照界面的 Intent
标记了一个 页面拦截器
去处理拍照权限的问题. 这时候你在任何的地方只须要
Router.with(this).hostAndPath("system/takePhone").forward()
便可完成跳转, 而且不须要关心权限问题.
/** * 拍照界面,须要拍照权限 */ @Nullable @RouterAnno( host = "system", path = "takePhone", // 拍照权限申请拦截器 interceptorNames = "help.cameraPermision" ) public static Intent takePictureIntent(@NonNull RouterRequest request) { Intent intent = new Intent(); // 指定开启系统相机的Action intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); intent.addCategory(Intent.CATEGORY_DEFAULT); return intent; }
自定义 Intent
能够实现让你对任何一个界面进行标记, 系统界面、第三方界面、你本身写的界面. 从而实现任何一个界面均可路由.
而自定义 Intent
和 页面拦截器
配合就能够达到更妙的效果. 以下为示意图.
咱们都用过 Retrofit
, 它为什么受欢迎?是由于它把对一个网络接口的调用用一个接口方法来描述出来. 方便、易用. 而且让你的全部网络接口都汇集在一块. 好维护
那咱们组件化为什么会有路由 Api 呢?
有不少朋友反馈呀. 当跳转到一个界面的入口特别多的时候. 以下的代码就会被 copy
不少遍, 当有改动的时候改的地方多, 而且很差找.
Router.with(this) .hostAndPath("order/detail") .putString("xxx1","xxx") .putInt("xxx2", 5) .putLog("xxx3", 555L) .forward();
在没有组件化以前, 代码没有分离以前, 不少小伙伴会在目标界面写一个静态的 startAct
方法, 跳转到该界面都会调用此方法. 如今组件化了不少时候都像上述同样 Copy
, 很差维护.
首先这个问题在我看来, 其实算是一个问题也不算一个问题. 在我看来, 组件化以后, 代码和资源的隔离, 确定会出现这种状况. 其实也是一个必然性. 可是为了广大的朋友, 我这边特别注意到一个开源库的路由就是使用路由 Api
作的
ARetrofit 我以为很棒的思路. 因而后面我也支持了这个功能. 最简单的一个范例:
@RouterApiAnno() @HostAnno("user") public interface UserApi { @PathAnno("login") void toLoginView( Context context, @ParameterAnno("name") String name, @ParameterAnno("pass") String pass, ); } // 声明一个上述的接口, 而后你就能够下面这样子使用啦 Router.withApi(UserApi.class).toLoginView(this, "xiaojinzi", "123");
这种方式能够很好地让某一些入口特别多的跳转集中调用这个方法, 达到维护方便的做用!
看我的喜爱. 我我的会更喜欢直接使用代码跳转.
为何会聊这个话题. 有些人以为拦截器
的执行线程有啥好聊的, 确定在子线程
啊. 可是我也得告诉你. 得分场景.
不少路由框架, 拦截器
的执行线程在子线程
, 是为了能让拦截器
作任何事情. 由于它们的设计上, 路由跳转和服务发现(也能够叫作跨模块功能调用) 是设计在一块的. 拦截器
既能拦截到路由跳转, 也能拦截到功能的调用.
我不评价这种设计的好坏. Component
的服务发现和路由跳转是彻底分离的两个过程.
Component
中只有 路由跳转
有拦截器的概念. 那么针对 路由跳转
. 不针对 功能调用
的场景下, 其实 拦截器
的执行线程应该在主线程更合理. 缘由以下:
sdk
, 框架
, 网络库
… 换句话说就是纯功能的库或者组件.子线程
你想弹个加载框框, 你切线程?让当前线程先等着?而后各类线程的唤起?我想这个不是你想要的void
, 何时往下执行, 须要用户手动调用.好比这个权限申请的拦截器, 你何时处理好你要作的事情, 你何时调用 chain.proceed(chain.request());
让拦截器继续, 若是处理失败, 你调用 Callback
的 onError
返回错误便可. 这种功能, 其余框架处理的话, 就麻烦喽。。。
/** * 电话权限申请的拦截器 */ @InterceptorAnno("help.callPhoePermision") public class CallPhoePermisionInterceptor implements RouterInterceptor { @Override public void intercept(final Chain chain) throws Exception { PermissionsUtil.with(chain.request().getRawContext()) .request(Manifest.permission.CALL_PHONE) .execute(new PermissionsCallback() { @Override public void onResult(boolean granted) { if (granted) { chain.proceed(chain.request()); } else { chain.callback() .onError( new Exception("fail to request call phone permision") ); } } }); } }
当你用上组件化
框架去跳转, 你会发现整个路由的过程就不是立马跳转了, 而是通过一段时间的.
你能够任何和处理一个任务、一个耗时请求同样. 因此自动取消路由功能看起来没啥用, 其实挺有用:
好比用户点击了一个按钮, 发起跳转. 中间有一个拦截器呢, 发现要处理一个任务. 因而去作任务了.
这时候用户等不及了, 点击了返回键。。。。可想而知, 以前发出去的路由若是不能被取消, 当他路由失败的时候, 回调的时候. 由于你没有判断界面是否销毁, 处理不得当. 奔溃了。。。
因此 Component
中, 若是你的路由发起的时候是 Fragment
或者 Activity
. 当相关的 Fragment
或者 Activity
销毁了, 路由自动取消. 不会回调的.
Component
也彻底支持 RxJava
Component
可让一个跳转返回一个 [Observable]
, 让你能够把跳转和其余 [Observable]
去结合使用.
好比大家用的 Retrofit
支持 RxJava
返回一个 [Observable]
. 同理, Component
也彻底支持
好比下面的范例
Completable completable = RxRouter .with(this) .host("order") .path("detail") .call(); completable.subscribe(); Single<ActivityResult> single = RxRouter .with(this) .host("help") .path("address") .activityResultCall(); single.subscribe(activityResult -> { System.out.println("拿到 ActivityResult"); });
Fragment
Androidx
Idea Plugin
H5
module
和其余基于 URI
框架相比, Component
的功能算是最全面了, 并且稳定!
有兴趣的请去个人 github
查看源码. 喜欢个人框架的小伙帮请不要吝啬你的 star
为我加油吧!