在之前的组件化开发中,如果ModuleA想要调用另一个ModuleB中的类或者方法,只能通过添加依赖的方式,而且是强依赖;这种形式存在的一个问题就是:如果ModuleB中的类发生改变,那么ModuleA也需要改变该类,甚至该类已经无法继续使用了,扩展性不强,耦合性太高。
但是开源的ARouter路由就解决了这个问题,Module之间不再是处于强耦合的关系,而是使用ARouter作为中间人,所有的Module在ARouter中注册后,那么其他的Module都可以调用ARouter中该Module的类和方法,而且Module在ARouter中注册之后,不管Module中怎么改变,其他获取到仍然是之前的类,不会因为其他的Module改变而需要去改变。
例如Module在ARouter注册一个类 B.a,ModuleA调用这个类,ModuleB此后将B.a改成了B.c,那么其他Module调用的依然是B.a,并不受ModuleB改变受到影响。
1、ARouter的基础使用
《阿里巴巴ARouter手册》具体使用可以从这里获取更多,还有源码解析等,这里主要讲解ARouter的主要用法。
(1)依赖配置
在defaultConfig
以及dependencies
中添加以下依赖
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
implementation 'com.alibaba:arouter-api:1.3.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.1.3'
(2)Activity之间的跳转
在之前没有使用ARouter的时候,使用Activity之间跳转的方式是通过startActivity的方式,尤其是使用显示意图Intent跳转的时候,耦合性太高。
在使用ARouter时,首先需要在Application中注册初始化之后,才可以使用
//初始化ARouter
if(isDebug()){
ARouter.openLog();
ARouter.openDebug();
}
ARouter.init(this);
现在就可以实现Activity之间的跳转了,在当前Activity中,如果想要跳转到目标Activity,只需要以下一行代码即可。
btn_jump.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ARouter.getInstance().build(Router.MAIN_ACTIVITY).navigation();
}
});
build中,代表跳转的目标Activity,所以在目标Activity中,需要使用@Route注解来注释。
@Route(path = Router.MAIN_ACTIVITY)
public class MainActivity extends AppCompatActivity {
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注入
ARouter.getInstance().inject(this);
}
}
一旦使用注解,那么就需要将ARouter注入!!!
(3)Activity携带参数跳转
在ARouter提供的API中,可传递的数据类型和Intent基本一致:基本数据类型、可序列化数据(2种)。。。
ARouter.getInstance()
.build(Router.MAIN_ACTIVITY)
.withString("name","ClearLove")
.withInt("age",12)
.navigation();
目标Activity:
@Route(path = Router.MAIN_ACTIVITY)
public class MainActivity extends AppCompatActivity {
private TextView tv_name,tv_age;
@Autowired(name = "name") //这里可以不用写,因为携带的key是一致的
String name; // String mName; 如果这样声明就需要加name
@Autowired()
int age;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注入
ARouter.getInstance().inject(this);
tv_name = findViewById(R.id.tv_name);
tv_age = findViewById(R.id.tv_age);
tv_name.setText(name);
tv_age.setText(age+"");
}
}
通过@Autowired
注解来修饰,需要注意的是,如果携带数据的key一致,在@Autowired
不需要声明name。
(4)Uri跳转
Uri uri = Uri.parse(Router.MAIN_ACTIVITY);
ARouter.getInstance()
.build(uri)
.withString("name","ClearLove")
.withInt("age",12)
.navigation();
(5)动画
Uri uri = Uri.parse(Router.MAIN_ACTIVITY);
ARouter.getInstance()
.build(uri)
.withTransition(Animation.REVERSE,Animation.ZORDER_TOP)
.withString("name","ClearLove")
.withInt("age",12)
.navigation();
使用withTransition
可以设置入场动画和出场动画,来设置跳转动画。
(6)路由回调 NavigationCallback
ARouter.getInstance()
.build(uri)
.withTransition(Animation.REVERSE,Animation.ZORDER_TOP)
.withString("name","ClearLove")
.withInt("age",12)
.navigation(VideoActivity.this, new NavigationCallback() {
@Override
public void onFound(Postcard postcard) {
Log.e("TAG",postcard.getPath()+","+postcard.getGroup());
}
@Override
public void onLost(Postcard postcard) {
}
@Override
public void onArrival(Postcard postcard) {
}
@Override
public void onInterrupt(Postcard postcard) {
}
});
使用NavigationCallback可以完成对路由的回调,主要分为4个方面,分别为onFound(路由寻址成功)、onLost(路由寻址失败)、onArrival(路由导航成功)、onInterrupt(拦截器),在四个方法中,都涉及到了一个类Postcard
,有两个比较关键的属性值,分别是path和group,path指的是路由的路径,group指的是分组。
2020-05-31 17:37:06.335 29646-29646/com.example.glide E/TAG: /app/main_activity,app
默认情况下,group的分组为app,也就是二级目录/xx/xxx,第一个位置的数据。
在navigation中除了可以完成路由的回调,还可以完成带结果的回调,也就是startActivityForResult
,可以传入requestCode
来实现。
2、ARouter实现服务化
以上只是ARouter使用的一些常用方法,最关键的还是在组件化开发中,避免模块间的强依赖,使用ARouter服务化进行模块解耦,才是组件化中的核心。
下面拉回开篇的场景:有两个ModuleA和ModuleB,ModuleA需要使用ModuleB中的一个类,如果相互依赖组件,本来没有关系的两个组件变得耦合性太高;所以对于业务层的抽离,可以在lib_base组件中,创建一个服务接口,该接口可以对外暴露;该接口的具体实现类是在ModuleB中实现的,具体的实现就是ModuleA想要调用ModuleB中的部分方法;那么当ModuleA想要调用ModuleB中类的方法时,直接依赖lib_base组件,通过ARouter路由获取,这样通过lib_base组件将两个不相干的组件联系起来。
当前存在lib_video
lib_base
lib_share
三个组件,lib_share
想要调用lib_video
中的方法,那么首先在lib_base
中创建一个接口,继承自IProvider。
public interface VideoService extends IProvider {
String getVideoName();
}
lib_video:
lib_video需要依赖lib_base库,完成VideoService 的具体实现
@Route(path = "/lib_video/video_service")
public class VideoServiceImpl implements VideoService {
@Override
public String getVideoName() {
Video video = new Video("唐人街探案",3);
return video.getVideoName();
}
@Override
public void init(Context context) {
}
}
lib_share:
lib_share同样需要依赖lib_base库,而不需要和lib_video库依赖。
@Autowired(name = "/lib_video/video_service")
VideoService mVideoService;
private ShareSDKManager(){
ARouter.getInstance().inject(this);
}
private static ShareSDKManager manager;
public static ShareSDKManager getInstance(){
if(manager == null){
synchronized (ShareSDKManager.class){
if(manager == null){
manager = new ShareSDKManager();
}
}
}
return manager;
}
//分享需要2类数据,参数和平台
public void shareData(ShareData data,PlatformShareDataListener listener){
this.listener = listener;
/*省略部分代码*/
String videoName = mVideoService.getVideoName();
Log.e("TAG","name=="+videoName);
}
组件路由服务化用法1:声明服务注意要加上路由地址,否则找不到该实现类,通过服务接口直接调用方法实现。
方法2:通过navigation导航
//不需要再加AutoWried注解
VideoService mVideoService2;
mVideoService2 = ARouter.getInstance().navigation(VideoService.class);
String videoName = mVideoService2.getVideoName();
方法3:通过build导航
//不需要再加AutoWried注解
VideoService mVideoService2;
mVideoService2 = (VideoService) ARouter.getInstance().build("/lib_video/video_service").navigation();
String videoName = mVideoService2.getVideoName();
3、ARouter分组与拦截器
ARouter拦截器,有些类似于之前在OkHttp组件开发中,讲到的拦截器。ARouter拦截器主要是拦截事件跳转,先看下代码
@Interceptor(priority = 1,name = "login_interceptor")
public class LoginInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
if(postcard.getGroup() == Router.NEED_LOGIN){
//如果是需要登录分组
ARouter.getInstance().build(Router.LOGIN_ACTIVITY).navigation();
}else{
callback.onContinue(postcard);
}
}
@Override
public void init(Context context) {
}
}
这是一个与登录相关的拦截器,核心在于process,在process方法中有两个形参,分别是Postcard 和 InterceptorCallback 。
Postcard 在之前介绍过,包含路由地址和分组Group,分组Group是可以在Route注解中声明的,在路由导航的时候,可以在build中添加分组信息。
@Route(path = Router.MAIN_ACTIVITY,group = Router.NEED_LOGIN)
public class MainActivity extends AppCompatActivity {
ARouter.getInstance()
// 路由地址 分组
.build(Router.MAIN_ACTIVITY,Router.NEED_LOGIN)
当然加了分组之后,就可以通过拦截器,通过Postcard来获取路由地址的分组,如果获取到需要分组的信息,那么就需要判断登录信息是否过期(这里没有做这个操作,在实际开发中是需要做的),如果没有过期,那么就通过InterceptorCallback
执行onContinue
继续路由到对应地址,如果登录信息过期,那么就需要路由到登录界面。
如果获取到的分组信息是不需要登录的,那么就直接通过InterceptorCallback
执行onContinue
继续路由到对应地址。