1. 前言
如果大佬有兴趣可以直接到github
中参考作者的说明文件进行学习。项目地址:ActivityRouter
之前看B
站的组件化视频的时候,无意间了解到了ActivityRouter
,这里就来简单的学习下这个开源的路由框架。
- 支持
Activity
跳转、URL
跳转;
2. 简单使用
按照学习的惯例,先来在一个项目中集成,然后简单使用下这个开源的框架。然后再尝试来阅读下ActivityRouter
的源码。
按照github
中的提示操作,我们在build.gradle(:app)
中添加依赖:
implementation 'com.github.mzule.activityrouter:activityrouter:1.2.2'
annotationProcessor 'com.github.mzule.activityrouter:compiler:1.1.7'
当然这种方式为添加annotaitonProcessor
方式。至于另一种apt
方式我们这里就不再考虑。
然后在AndroidManifest.xml
文件中的application
标签中插入:
<activity android:name="com.github.mzule.activityrouter.router.RouterActivity"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="weizu" /><!--改成自己的scheme-->
</intent-filter>
</activity>
这里需要修改的android:scheme
是在进行配置触发跳转时候所需要的一个类型标识,因为这个项目中支持多种方式,不再是我们简单案例中的Activity
之间的跳转。我这里将之定义为weizu
。
然后,在项目中再添加一个OtherActivity
并添加相应的xml
文件,然后对其进行路由标识的关联,如下:
@Router("main")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView viewById = findViewById(R.id.text);
viewById.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Routers.open(MainActivity.this, "weizu://other");
}
});
}
}
@Router("other")
public class OtherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
}
}
跳转命令也就是Routers.open(MainActivity.this, "weizu://other");
。
其链接的写法有点类似于常见的网络链接地址的书写。确实感觉很正式。
另不要忘记了在AndroidManifest.xml
文件中注册OtherActivity
:
<activity android:name=".OtherActivity"/>
然后就可以实现点击后跳转。对于这个部分的实现,我觉得其逻辑应该和昨天的那个简单的案例中的一样,所以不妨来看看在这个项目中,这个部分的代码是如何实现的。当然,我们还需要测试不同模块下的Activity
的跳转。
不妨新建一个test
模块,我这里选择Android Library
。由于我们之前的依赖添加在build.gradle(:app)
,所以在test
中必不可以访问,所以这里还需要添加依赖。
但是经过测试发现模块之间的跳转却无法实现???
是我操作失误?是的!
在末尾我找到了:
支持多模块
- 每个包含 activity 的 module 都要添加 apt 依赖
- 每个 module(包含主项目) 都要添加一个 @Module(name) 的注解在任意类上面,name 是项目的名称
- 主项目要添加一个 @Modules({name0, name1, name2}) 的注解,指定所有的 module 名称集合
但是还是不得行。。。
然后,我使用了build
,然后找到了它生成了两个文件分别是RouterInit
和RouterMapping
。
RouterInit
生成的内容如下:
public final class RouterInit {
public static final void init() {
RouterMapping.map();
}
}
RouterMapping
生成的内容如下:
import com.weizu.activityrouterstudy.MainActivity;
import com.weizu.activityrouterstudy.OtherActivity;
public final class RouterMapping {
public static final void map() {
java.util.Map<String,String> transfer = null;
com.github.mzule.activityrouter.router.ExtraTypes extraTypes;
transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("main", MainActivity.class, null, extraTypes);
transfer = null;
extraTypes = new com.github.mzule.activityrouter.router.ExtraTypes();
extraTypes.setTransfer(transfer);
com.github.mzule.activityrouter.router.Routers.map("other", OtherActivity.class, null, extraTypes);
}
}
这里可以发现,其实思想是类似的。但是至于模块间的路由该如何实现确实勾起了我的好奇心。
难道真好换成apt 方式
而不能使用annotaitonProcessor 方式
来引入依赖?
毕竟作者是这样说的:
每个包含 activity 的 module 都要添加 apt 依赖
- 至少到两个设计模式了,单例模式、解释器模式;确实我还没有用到,所以需要
一直感觉apt
不就是注解处理器的缩写,这两者之间有什么差别吗?所以接下来我需要先找找这个问题的答案,因为这个项目2.8k
的start可不是开玩笑的。
apt
但是在当我换成apt
的时候,出现了下面的错误:
A problem occurred configuring project ':app'.
> android-apt plugin is incompatible with the Android Gradle plugin. Please use 'annotationProcessor' configuration instead.
那么这两者之间到底有什么关系呢?感觉这篇讲的还可以:gradle之apt与annotationProcessor与kapt_火星男孩的分享空间-CSDN博客
annotationProcessor和android-apt的功能是一样的,它们是替代关系。
APT(Annotation Processing Tool)是一种处理注释的工具,它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
Android Gradle插件2.2版本发布后,Android 官方提供了annotationProcessor来代替android-apt,annotationProcessor同时支持 javac 和 jack 编译方式,而android-apt只支持 javac 方式。
同时android-apt作者宣布不在维护,当然目前android-apt仍然可以正常运行,如果你没有想支持 jack 编译方式的话,可以继续使用 android-apt。
也就是说,对于Gradle2.2
以后,都使用Android
官方提供的annotationProcessor
,就不再使用android-apt
。但是对于为什么多个模块之间没办法实现跳转,这里问题确实还需要学习下。
决定先使用阿里巴巴的ARouter
来简单使用看看,然后再继续看看之前看的那个组件化开发的视频,看看模块间跳转大佬是如何做的。
ARouter
在这个github
地址中,介绍了这个框架的使用,地址:ARouter/README_CN.md
在功能介绍中有:
- 支持多模块工程使用
所以这个也就是我们目前学习所预期的。然后来体验下使用ActivityRouter
没有实现的模块间的跳转。
当然在这之前,有个典型应用:
- 从外部URL映射到内部页面,以及参数传递与解析
- 跨模块页面跳转,模块间解耦
- 拦截跳转过程,处理登陆、埋点等逻辑
- 跨模块API调用,通过控制反转来做组件解耦
确实厉害!
还是上面的那个项目,也就是一个主模块,一个测试模块,然后我们去除在build.gradle
中的ActivityRouter
依赖,然后相应的在build.gradle(:app)
中添加1和2:
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.weizu.activityrouterstudy"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// 1
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
// 2
implementation 'com.alibaba:arouter-api:1.5.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.0'
implementation project(':test')
}
然后,我们需要按照说明进行SDK
的初始化工作。即新建一个类,继承自Application
,然后在这个类中完成初始化。
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
ARouter.init(this); // 尽可能早,推荐在Application中初始化
}
}
然后在AndroidManifest.xml
配置,将之关联到整个应用,即:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name="com.weizu.myapp.MyApp"
android:theme="@style/Theme.ActivityRouterStudy">
然后我们为Activity
添加注解,注意到:这里的路径需要注意的是至少需要有两级,如:
@Route(path = "/test/activity")
public class YourActivity extend Activity {
...
}
然后,我们就可以发起路由,这里我们只是为了体验,并不需要传递参数,所以这里就使用:
ARouter.getInstance().build("/test/activity").navigation();
对应的在app
模块中的MainActivity
代码如下:
@Route(path = "/app/main")
public class MainActivity extends AppCompatActivity {
private String TAG = "Main";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView viewById = findViewById(R.id.text);
viewById.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ARouter.getInstance().build("/app/other").navigation();
}
});
}
}
OtherActivity
代码如下:
@Route(path = "/app/other")
public class OtherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
}
}
然后测试结果正常。
现在我们开始在test
模块中添加一样的1和2的依赖,然后添加路由,如:
@Route(path = "/test/another")
public class AnotherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
最后发现不得行,嗯??看来在ActivityRouter
中也是我操作的问题。
然后我修改了这个OtherActivity
布局文件的名字叫做activity_another
,最后在app
模块中加载的那个activity_main
居然也变了。所以这里的问题一定是我文件名命名整相同的问题。所以重新修改后,在AnotherActivity
中加载activity_another
。
最终测试跳转结果正常。
看来有必要重新来使用ActivityRouter
再试一次。
先清除ARouter
中的相关依赖,然后再添加annotaitonProcessor
方式的ActivityRouter
依赖:
dependencies {
compile 'com.github.mzule.activityrouter:activityrouter:1.2.2'
annotationProcessor 'com.github.mzule.activityrouter:compiler:1.1.7'
}
在AndroidManifest.xml
配置:
<activity
android:name="com.github.mzule.activityrouter.router.RouterActivity"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mzule" /><!--改成自己的scheme-->
</intent-filter>
</activity>
对于app/MainActivity
:
@Router("main")
@Module("main")
@Modules({"main", "test"})
public class MainActivity extends AppCompatActivity {
private String TAG = "Main";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView viewById = findViewById(R.id.text);
viewById.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Routers.open(MainActivity.this, "weizu://other");
}
});
}
}
对于app/OtherActivity
:
@Router("other")
@Module("main")
public class OtherActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
}
}
模块内Activity
跳转正常。
然后我们修改下app/MainActivity
中的跳转路由:
Routers.open(MainActivity.this, "weizu://another");
模块之间的Activity
跳转正常。
我可真是个废物,不过还好要吸取教训!
也就是不同模块之间的xml
文件还是要遵守规范,比如:
Activity_name_modulename.xml
所以接下来我们需要阅读下ActivityRouter
的源码。理解其中涉及到的一些设计模式和设计思想。