目录
文章目录
组件间跳转,ARouter
这里使用ARouter进行页面的跳转,并支持参数传递、拦截器、服务发现等功能,他提供了一种极为方便的跳转方法并且他可以使用拦截器来进行判断是否进入
特点
-
简化页面跳转:使用注解标记目标页面,通过统一的 API 完成跳转。
-
参数传递:支持多种数据类型直接传递,不需要手动打包和解包。
-
模块化支持:对模块化开发友好,每个模块可以独立使用
ARouter
。 -
拦截器:可以全局拦截跳转过程,执行如登录检查、权限验证等操作。
-
服务发现:允许定义服务接口并通过
ARouter
查找实现,类似于服务定位器模式ARouter的注释概述
-
@Route
-
作用:用于标记目标页面,即Android组件,如Activity或Fragment等,以便
ARouter
能够找到并跳转到这些组件。 -
属性
:
path
(必须):指定了跳转的路径,这个路径需要在当前项目中唯一,通常遵循/模块名/页面名
的格式。group
(可选):用于分组,可以按照模块划分,方便管理,如果不指定,ARouter
会根据路径自动生成。(注意:一旦主动指定分组之后,应用内路由需要使用 ARouter.getInstance().build(path, group) 进行跳转,手动指定分组,否则无法找到)
@Route(path = "/app/MainActivity") public class MainActivity extends AppCompatActivity { // ... }
-
-
@Autowired
-
作用:用于标记成员变量,告诉
ARouter
需要自动注入这些变量的值,通常用于接收从其他页面传递过来的参数。 -
属性
:
name
(可选):用于指定注入时使用的key,默认为空,此时ARouter
会使用变量名作为key。required
(可选):标记该字段是否必须存在,如果为true,而传递参数时没有提供,会抛出异常。desc
(可选):字段描述,用于描述这个字段的用途,主要是为了代码的可读性。
public class DetailActivity extends AppCompatActivity { @Autowired(name = "username") String userName; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ARouter.getInstance().inject(this); // 开始注入 // ... } }
-
-
@Interceptor
-
作用:用于标记拦截器类,拦截器用于在跳转之前拦截并执行一些操作。
-
属性
:
priority
(必须):表示拦截器的优先级,数值越小优先级越高。name
(可选):拦截器的名称,主要用于调试和可读性。
@Interceptor(priority = 8, name = "登录状态检查") public class LoginInterceptor implements IInterceptor { @Override public void process(Postcard postcard, InterceptorCallback callback) { // 拦截逻辑 // ... } @Override public void init(Context context) { // 拦截器初始化时调用 // ... } }
-
-
@Provider
- 作用:用于标记服务提供者,即实现了特定接口的类,用于服务发现。
- 属性:无特殊属性,只需标记类即可。
@Route(path = "/service/user") public class UserServiceImpl implements IUserService { // 实现IUserService中的方法 // ...
-
这里只是单纯给出,了解个大概就可以了
跳转
1.准备工作
这里我准备了俩个组件,一个是app,一个是first,这里我将由app .跳转到first
然后就是对first的状态进行处理
if(isrunfirst.toBoolean())
{
apply plugin:'com.android.application'
}else
{
apply plugin:'com.android.library'
}
2.添加依赖和配置
defaultConfig {
......
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
他写在defaultConfig 中,作用是用于设置 ARouter 注解处理器的参数,在编译时生成与当前项目一致的路由表。这样,你就可以在项目中使用 ARouter 进行页面跳转,而无需手动管理路由映射关系(你也可以不用管他)这个代码在所以使用ARouter的组件中都需要处理,否则他将无法正确找到路径
implementation 'com.alibaba:arouter-api:1.5.2'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
导入依赖
这个依赖库可能会更新这个现在是最新的
ARouter的github地址
3.添加批注
在目标ACtivity中添加这里是first
@Route(path ="/first/tttttt")
public class tttttt extends AppCompatActivity
这里解释一下,这个地方并不是地址,他只是一个批注,与地址无关,这样写只是为了确保唯一性
4.初始化 SDK
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(this); // 尽可能早,推荐在Application中初始化
}
}
BuildConfig.DEBUG是判断他是否处于调试模式,如果感觉不明确,可以自己手动更改
这里之所以在MyApplication中初始化是为了尽早初始化 ARouter,这个只需要在app(主模块)中初始化即可,
5.启动路由
ARouter.getInstance().build("/first/tttttt").navigation();
ARouter.getInstance().build("/first/tttttt")
.withString("msg", "5")
.withDouble("msg2", 6.0)
.navigation();
ARouter.getInstance()获取他的单例实例,
build(“/first/tttttt”)指定目标页面跳转路由路径
navigation()跳转到指定的页面。
.withString(“msg”, “5”)对 ARouter 跳转时传递参数的设置操作。它的作用是向即将跳转的页面传递一个名为 “msg” 的字符串参数,参数的取值为 “5”。传递不同类型的参数进行不同设置即可
报错处理
这里有俩个报错,如果他显示资源文件合并出现问题
Manifest merger failed with multiple erpackage com.example.third;
,这个是资源文件迁移的问题,在项目的gradle.properties中添加
android.enableJetifier=true
android.useAndroidX=true
如果他一直显示找不到路径,建议在app build.grad中加入目标组件的引用(同时经历注意不要起同一个名字)
implementation project (':first')
这里注意组件的跳转都是在集成模式下的,在调试模式下各个组件处于application状态各个组件类似于一个独立的app所以他无法进行跳转,组件的跳转都是在集成模式下的,他可能会报错
Dependent features configured but no package ID was set.
6.返回
初始页面app
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button =findViewById(R.id.text);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ARouter.getInstance().build("/first/tttttt")
.navigation(MainActivity.this,897);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == 897 && resultCode == 999){
TextView textView = findViewById(R.id.tett);
String msg = data.getStringExtra("msg");
textView.setText(msg);
}
}
这里使用.navigation(MainActivity.this,897);标注了一个请求码,897
是一个示例的请求码(requestCode),用于在目标页面返回结果时进行标识。请求码可以是任意整数值,用于唯一标识一个请求。
onActivityResult他主要有俩个参数,requestCode和resultCode,第一个就是我们在发起跳转时设置的请求码,第二个是他返回时给我们的请求码,通过这俩个参数确定判断是哪个请求的返回结果。这个方法是一个回调方法,在我们目标的页面被销毁的时候他会被调用
下面是目标页面
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tttt);
Button button=findViewById(R.id.ttttt);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent in = new Intent();
in.putExtra("msg", "从home模块返回的数据");
setResult(999, in);
finish();
}
});
}
这里创建了一个Intent对象,然后向其中传递了数据,然后通过setResult(999, in);设置返回结果,在finish将活动注销的时候他会将结果码和携带的数据传递回上一个页面上一个页面可以通过重写 onActivityResult接收返回结果
7.携带参数跳转
初始页面app
ARouter.getInstance().build("/first/tttttt")
.withString("username", "张三")
.withInt("age", 18)
.navigation(MainActivity.this,897);
目标页面
String username = getIntent().getStringExtra("username");
int age = getIntent().getIntExtra("age", 0);
TextView textView =findViewById(R.id.tetet);
textView.setText("这个参数是"+username+"另一个参数是"+age);
拦截器
拦截跳转比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查。
这里做一个范例来详细解释一下
首先书写拦截器
@Interceptor(priority = 2, name = "拦截器")
public class Interceper implements IInterceptor {
//用于处理跳转逻辑之前的拦截操作
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
//Postcard postcard:这个参数封装了路由过程中的数据和信息,可以获取跳转时传递的参数、目标路径等,并根据这些信息做出相应的处理。
//InterceptorCallback callback:这是一个回调接口,它允许你控制是否继续进行路由跳转或者中断当前的跳转
// 获取传递的 age 参数
int age = postcard.getExtras().getInt("age", -1); // 默认值为 -1
// 判断 age 是否小于 18
if (age < 18) {
// 如果小于 18,则不允许导航,并调用 onInterrupt
Log.d("NavigationDebug", "Navigation is interrupted because age is less than 18.");
callback.onInterrupt(null); // 可以创建一个 Throwable 对象提供给 onInterrupt,这里传 null 表示没有异常对象
} else {
// 如果 age 大于等于 18,则允许导航
Log.d("NavigationDebug", "upload navigation log");
callback.onContinue(postcard);
}
}
//初始化方法,你可以执行一些准备工作
@Override
public void init(Context context) {
}
}
这里给出InterceptorCallback
提供的两个主要的方法:
onContinue(Postcard postcard)
:当你决定允许路由继续进行时,调用此方法。调用后,ARouter 会继续执行后续的拦截器(如果有的话),如果没有其他拦截器中断,则最终会进行页面跳转。onInterrupt(Throwable throwable)
:如果你决定中断路由跳转,调用此方法。可选地,你可以提供一个Throwable
对象作为参数,表示中断的原因。调用此方法后,ARouter 不会继续执行后续的拦截器,也不会进行页面跳转,而是会触发导航回调(如果有的话)中的onInterrupt
方法。
此时拦截器已经被定义,他已经可以通过注解来进行拦截,就是说他不需要显性添加到ARouter中,使用注解方式定义拦截器可以简化代码,使得拦截器的添加更加方便,ARouter 将会自动扫描并注册所有使用了 @Interceptor
注解的拦截器类,无需手动添加拦截器实例,这里注意在使用注解方式定义拦截器时,需要确保拦截器类被正确引入并在编译过程中被扫描到。另外,使用注解方式定义的拦截器优先级是根据 priority
属性来确定的,数值越小优先级越高。如果两个拦截器的优先级一样,项目编译就会报错。所以,不同拦截器定义的优先级属性值不能相同
这里使用了age进行判断,可能你也注意到了,这里 postcard.getExtras().getInt(“age”, -1)有一个默认值,他的作用是什么呢,拦截器会对多个跳转进行判断,如果某次跳转中没有这个参数,那么他会返回默认值,避免空指针错误,
同时你可以通过 postcard.getPath()
方法获取目标路径,然后根据具体的业务逻辑进行判断。如果目标路径满足某个条件,可以调用 callback.onInterrupt(null)
中断跳转;如果不满足条件,可以调用 callback.onContinue(postcard)
继续跳转。
NavigationCallback
// NavigationCallback 是导航过程中的回调接口,它定义了在不同导航事件发生时应该执行的操作。当你使用 ARouter 进行页面跳转时,你可以提供一个 NavigationCallback 实例来监听导航事件。
private NavigationCallback navigationCallback = new NavigationCallback() {
//当导航目标被成功找到时调用。
@Override
public void onFound(Postcard postcard) {
}
//当无法找到导航目标时调用。
@Override
public void onLost(Postcard postcard) {
}
//当导航到达预定目标时调用。
@Override
public void onArrival(Postcard postcard) {
}
//当导航被拦截器中断时调用。
@Override
public void onInterrupt(Postcard postcard) {
// 使用 Handler 在主线程中显示 Toast
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this, "你的年龄过小", Toast.LENGTH_SHORT).show();
}
});
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button =findViewById(R.id.teet);
EditText editText = findViewById(R.id.qqq);
EditText editText1 = findViewById(R.id.qqq1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String ageStr = editText1.getText().toString();
try {
int age = Integer.parseInt(ageStr);
String name = String.valueOf(editText.getText());
ARouter.getInstance().build("/first/tttttt")
.withInt("age", age)
.withString("name", name)
.navigation(null, navigationCallback);//就是在这里使用了拦截器
} catch (NumberFormatException e) {
Toast.makeText(MainActivity.this, "请输入有效的年龄", Toast.LENGTH_SHORT).show();
}
}
});
}
在这里我给出了主要内容的解释,你可能注意到了 .navigation();这个方法以及出现好几个方法重载,下面给出了他的方法重载以及解释
navigation()
- 无参数版本,直接开始路由跳转。如果当前上下文不是 Activity,则需要在
Postcard
中通过withFlags
添加Intent.FLAG_ACTIVITY_NEW_TASK
。
- 无参数版本,直接开始路由跳转。如果当前上下文不是 Activity,则需要在
navigation(Context context)
- 参数
context
:提供一个上下文对象,用于启动目标 Activity。如果传入的是 Application 的 Context,且没有设置FLAG_ACTIVITY_NEW_TASK
标志,则会添加该标志。
- 参数
navigation(Activity activity, int requestCode)
- 参数
activity
:提供一个 Activity 实例,用于启动目标 Activity,并期望返回结果。 - 参数
requestCode
:如果你希望从目标 Activity 获取结果,需要使用这个请求码。
- 参数
navigation(Context context, NavigationCallback callback)
- 参数
context
:与第二个版本相同,提供一个上下文对象。 - 参数
callback
:导航回调,用于接收路由过程中的回调事件,包括找到目标、丢失目标、到达目标和被拦截等情况。
- 参数
navigation(Context context, int requestCode, NavigationCallback callback)
- 参数
context
:与第二个版本相同,提供一个上下文对象。 - 参数
requestCode
:与第三个版本相同,用于从目标 Activity 获取结果的请求码。 - 参数
callback
:与第四个版本相同,导航回调。
- 参数
navigation(Context context, int requestCode, Bundle options, NavigationCallback callback)
- 参数
context
:与第二个版本相同,提供一个上下文对象。 - 参数
requestCode
:与第三个版本相同,用于从目标 Activity 获取结果的请求码。 - 参数
options
:一个 Bundle,可以包含启动 Activity 时的附加选项,通常用于 Activity 转场动画等。 - 参数
callback
:与第四个版本相同,导航回调。
- 参数
最后就是目标页面的代码,这部分没有特殊代码,给出是为了方便理解(出了BUG 的时候方便更改)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tttt);
Button button=findViewById(R.id.ttttt);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent in = new Intent();
in.putExtra("msg", "从home模块返回的数据");
setResult(999, in);
finish();
}
});
// 获取携带的参数
String name = getIntent().getStringExtra("name");
int age = getIntent().getIntExtra("age",0);
TextView textView =findViewById(R.id.text1);
TextView textView1 =findViewById(R.id.text2);
textView.setText("名字是"+name);
textView1.setText("年龄"+age);
}
服务处理
什么是服务?
在 ARouter 框架中,服务通常是指一个模块向外部提供的一组功能或者业务逻辑,它们被定义为一个接口及其实现。其他模块可以通过这个接口与实现的解耦合方式来调用服务,而不需要知道服务的具体实现细节。这样的设计使得模块间的耦合度大大降低,有利于模块的独立开发和维护。
如何实现服务发现?
要实现服务发现,你需要遵循以下步骤:
- 定义服务接口:首先,你需要定义一个服务接口,这个接口包含了其他模块可能调用的方法。
public interface HelloService {
String sayHello(String name);
}
- 实现服务接口:然后,创建一个类来实现这个接口,并使用
@Route
注解标明这个类是一个服务的实现。
@Route(path = "/service/hello", name = "测试服务")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello, " + name + "!";
}
}
- 发现并使用服务:其他模块可以通过 ARouter 调用服务接口,而无需直接依赖实现类。这可以通过 ARouter 的
navigation()
方法实现。
// 获取服务
HelloService helloService = (HelloService) ARouter.getInstance().build("/service/hello").navigation();
// 调用服务
String result = helloService.sayHello("World");
在上面的例子中,我们定义了一个 HelloService
接口和一个 HelloServiceImpl
实现类,实现类通过 @Route
注解注册到了 ARouter 中。当我们需要使用这个服务时,我们只需要通过 ARouter 的 build()
方法传入服务的路径,然后调用 navigation()
方法即可获取到服务的实例,进而调用服务中定义的方法。