组件化的开发的优点
1.提高编译速度
2.超级解耦
3.功能重用
4.便于团都开发
组件化开发需要注意的几点
1.包名和资源文件名命名冲突问题
2.Gradle中版本号的统一管理
3.组件中application和library的切换
4.AndroidManifest.xml文件的区分
ARouter实现原理
通过注解和注解处理器在编译代码的时候自动生成一个类,每一个模块都会自动生成一个类,通过注解标记得到所有的Activity,并且把这些Activity都添加到一个集合中,这个集合就添加了所有模块下的注解标记的Activity。再通过ARouter从集合中拿到key对应的Activity实现界面的跳转和传递参数。
demo结构图
gradle.properties配置
在项目gradle.properties中需要设置一些参数和开关,用来控制module的编译,版本号的统一 ,如下`
#SDK编译版本
COMPLE_SDK_VERSION =29
BUILD_TOOLS_VERSION= 29.0.3
MIN_SDK_VERSION=16
#目标SDK版本
TARGET_SDK_VERSION= 29
#版本号
VERSION_CODE= 1
#版本名字
VERSION_NAME= 1.0
#依赖库
APP_COMPAT=
#是否是Application
IS_APPLICATION=false
清单文件配置
module清单文件需要配置两个,一个作为独立项目的清单文件,一个作为库的清单文件,以Login模块为例:
manifest作为依赖库的清单文件,和独立项目的清单文件区别是如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.fishman.zxy.login">
<application
>
<activity android:name=".LoginActivity">
</activity>
<activity android:name=".TowActivity" />
</application>
</manifest>
gradle配置
if(IS_APPLICATION.toBoolean()){
apply plugin: 'com.android.application'
}else {
apply plugin: 'com.android.library'
}
android {
compileSdkVersion COMPLE_SDK_VERSION.toInteger()
buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
if(IS_APPLICATION.toBoolean()){
applicationId "com.fishman.zxy.login"
}
minSdkVersion MIN_SDK_VERSION.toInteger()
targetSdkVersion TARGET_SDK_VERSION.toInteger()
versionCode VERSION_CODE.toInteger()
versionName VERSION_NAME
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets{
main{
if(IS_APPLICATION.toBoolean()){
manifest.srcFile 'src/main/AndroidManifest.xml'
}else {
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(path: ':marouter')
annotationProcessor project(path: ':annotion_compiler')
implementation project(path: ':annotion')
}
主APP gradle 配置
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(path: ':marouter')
implementation project(path: ':annotion')
annotationProcessor project(path: ':annotion_compiler')
if(!IS_APPLICATION.toBoolean()){
implementation project(path: ':login')
implementation project(path: ':transaction')
}
}
建立一个注解类
@Target(ElementType.TYPE) //作用域
@Retention(RetentionPolicy.CLASS) //生命周期
public @interface BindPath {
String value();
}
建立一个注解处理器
注解处理器中的gradle配置,这里得注意AS版本低的不是这样,这个版本是3.6.3的
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
compileOnly 'com.google.auto.service:auto-service:1.0-rc4'
implementation project(path: ':annotion')
}
注解处理器代码如下:
@AutoService(Processor.class)
public class AnnotionCompiler extends AbstractProcessor {
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer=processingEnvironment.getFiler();
}
/*编译时执行的方法
* @param set
* @param roundEnvironment
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//获取到当前模块用到BindPath的结点
// TypeElement 类节点
//ExecutableElement 方法节点
//VariableElement 成员变量节点
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindPath.class);
Map<String,String>map=new HashMap<>();
for (Element element:elementsAnnotatedWith) {
TypeElement typeElement= (TypeElement) element;
//获取到activity 上的BindPath注解
BindPath annotation = typeElement.getAnnotation(BindPath.class);
String key=annotation.value();
//获取到包名加类名
Name activityName = typeElement.getQualifiedName();
map.put(key,activityName+".class");
}
//写文件
if(map.size()>0){
Writer writer=null;
//文件类名
String activityUtil="ActivityUtil"+System.currentTimeMillis();
//生成java文件
try {
JavaFileObject sourceFile=filer.createSourceFile("com.fishman.zxy.util."+activityUtil);
writer=sourceFile.openWriter();
StringBuffer buffer=new StringBuffer();
buffer.append("package com.fishman.zxy.util;\n");
buffer.append("import com.fishman.zxy.marouter.ARouter;\n" +
"import com.fishman.zxy.marouter.IRout;\n" +
"\n" +
"public class "+activityUtil+" implements IRout {\n" +
" @Override\n" +
" public void putActivity() {\n");
Iterator<String> iterator=map.keySet().iterator();
while (iterator.hasNext()){
String key=iterator.next();
String ClassnName=map.get(key);
buffer.append(" ARouter.getInstance().AddActivity(\""+key+"\","+ClassnName+");\n");
}
buffer.append("\n }\n}");
writer.write(buffer.toString());
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if(writer!=null){
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
}
ARouter类实现Activity的跳转
首先这个类设计成单例模式,并定义一个初始化方法:
通过遍历指定包名下面的类(编译时自动生成的类),再通过反射的方法去执行调用这个类里面的方法(添加Activity的方法),通常这个init()在application中去调用。
public void init(Context context){
this.context=context;
List<String> classNames=getClassName(packagename);
for (String className:classNames) {
try {
Class<?>aClass=Class.forName(className);
//判断这个类是不是IRout 子类
if(IRout.class.isAssignableFrom(aClass)){
//通过接口获取实例并调用方法
IRout iRout= (IRout) aClass.newInstance();
iRout.putActivity();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Activity直接的跳转方法
/**
* 跳转方法
* @param key
* @param bundle
*/
public void GoToActivity(String key, Bundle bundle){
Class<? extends Activity> ActyaClass = maps.get(key);
if(ActyaClass!=null){
Intent intent=new Intent();
if(bundle!=null){
intent.putExtras(bundle);
}
intent.setClass(context,ActyaClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
context.startActivity(intent);
}
}
从APP模块跳转到Login模块的调用
public void GoToActivity(View view) {
ARouter.getInstance().GoToActivity("login/login",null);
}
模块中的Activity都需添加注解并传递key值:
demo链接:https://github.com/zhouxingyi/MRout