前语
在上一篇文章中,咱们完成了组件的创立、gradle统一办理、组件形式办理和切换,那么这一篇文章,咱们需求做的便是组件之间的通讯了。
正文
组件化是将本来复杂的App分解成一个个组件,在调试运转的时分各个组件之间能够独自测验,而在打包的时分需求将其他的组件打包在app组件中,作为一个apk时,必定会有不同组件之前的通讯,举一个简略的比如,咱们在app组件中写一个启动页,假如之前用户有登录过,则进入personal组件的PersonalActivity,假如没有登录过则进入login组件的LoginActivity。而LoginActivity登录成功之后要进入personal组件的PersonalActivity,要完成这样一个简略的比如,咱们需求做的便是组件之间的相互通讯,而在通讯之前首先要找到通讯的目标。
这儿需求用到编译时技术,在之前的学习注解和注解处理器中我提到过,而组件中用到的便是类似于ARouter的路由框架,下面咱们简略来写一下。
一、注解
仍是之前的StudyComponent项目,这儿咱们再创立一个Module,这儿要留意创立的是java Module,留意我挑选的形式。
创立Java Module的时分需求创立一个默认的类,这儿咱们改变一下类名为BindPath,稍后还将改成注解类。
① 创立注解类
Module创立好之后修正这个BindPath,代码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface BindPath {
String value();
}
这个库里边代码其实就这么一点,那么咱们怎样运用这个注解呢?
② 运用注解类
要运用这个注解类,首先要依靠这个注解库,那么咱们之前所写的config.gradle就排上用场了,还记得它的作用吗?办理工程中所有的gradle,那么增加依靠库自然是能够的,何况咱们之前还增加过,还记得吗?帮你回想一下,咱们的app、login、personal都需求依靠basic库,之前经过config.gradle中配置能够一步到位,那么这个注解库也是一样的道理,所以咱们只需求改动一个地方就能够完成所有组件关于注解库的依靠,
现在Sync Now同步一下就能够了,咱们分别在app、login、personal组件中运用这个注解,如下图所示:
留意看,这儿在Activity上面增加注解,然后里边的值便是当时的模块名斜杠再加上当时的类名,好了下面你能够暂且运转一下,看看报不报错,不管报不报错都持续往后走。
二、路由
这儿咱们做注解是要符号一个Activity,然后保存到路由中,那么这个路由就担任组件之间通讯,这儿的路由,你能够独自创立一个library库,也能够写在basic中,这儿我就写在basic模块中,在com.llw.basic包下新建一个router包,router包下新建IRouter接口,代码如下:
public interface IRouter {
void putActivity();
}
然后咱们再创立一个ARouter类,代码如下:
public class ARouter {
@SuppressLint("StaticFieldLeak")
private static final ARouter aRouter = new ARouter();
private final Map<String, Class<? extends Activity>> map;
private Context context;
private ARouter() {
map = new HashMap<>();
}
public static ARouter getInstance() {
return aRouter;
}
/**
* 初始化
*/
public void init(Context context) {
this.context = context;
//执行生成的东西类中的办法 将Activity的类目标加入到路由表中
List<String> classNames = getClassName();
for (String className : classNames) {
try {
Class<?> utilClass = Class.forName(className);
if (IRouter.class.isAssignableFrom(utilClass)) {
IRouter iRouter = (IRouter) utilClass.newInstance();
iRouter.putActivity();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 增加Activity
* @param key 注解中的值 例如 "main/MainActivity"
* @param clazz 目标Activity
*/
public void addActivity(String key, Class<? extends Activity> clazz) {
//假如Key不会空,activity不为空,且map中没有这个key
if (key != null && clazz != null && !map.containsKey(key)) {
map.put(key, clazz);
}
}
/**
* 跳转Activity
* @param key 注解中的值 例如 "main/MainActivity"
*/
public void jumpActivity(String key) {
jumpActivity(key, null);
}
/**
* 跳转Activity 带参数
* @param key 注解中的值 例如 "main/MainActivity"
* @param bundle 参数包
*/
public void jumpActivity(String key, Bundle bundle) {
Class<? extends Activity> aClass = map.get(key);
if (aClass == null) {
return;
}
Intent intent = new Intent(context, aClass);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (bundle != null) {
intent.putExtras(bundle);
}
context.startActivity(intent);
}
/**
* 经过包名获取这个包下面的所有的类名
*/
private List<String> getClassName() {
//创立一个class目标的集合
List<String> classList = new ArrayList<>();
try {
DexFile df = new DexFile(context.getPackageCodePath());
Enumeration<String> entries = df.entries();
while (entries.hasMoreElements()) {
String className = entries.nextElement();
if (className.contains("com.llw.util")) {
classList.add(className);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classList;
}
}
这儿面的代码便是存放和运用Activity、组件之间跳转Activity操作。现在注解和路由都有了,要使咱们的注解能够收效,还需求一个注解处理器,顾名思义便是用来处理被注解的类型。
三、注解处理器
这儿咱们再创立一个Module,这儿要留意创立的是java Module,留意我挑选的形式。
这儿修正模块名和包名和类名,等候注解处理器这个库创立完成。
① 增加依靠
这儿的注解处理器相较于注解稍稍有一些不同,首先咱们改动一下注解处理器模块的build.gradle,增加代码如下:
dependencies {
implementation 'com.google.auto.service:auto-service:1.0-rc7'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
implementation 'com.squareup:javapoet:1.13.0'
implementation project(path: ':annotation')
}
增加位置如下图所示
这儿前面两句依靠是增加注解处理器,然后便是生成编译时文件需求用到的库,最后便是依靠注解库,这儿和之前稍有不同,咱们不运用config.gradle中的配置,这也是注解处理器的特别之处,增加完依靠之后点击Sync Now。
② 注解处理器编写
同步完成之后咱们能够编写AnnotationCompiler类的代码,如下所示:
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
// 界说用来生成APT目录下面的文件的目标(例如:ActivityRouterUtil1668396026324)
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
}
/**
* 支撑的注解类型
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindPath.class.getCanonicalName());
return types;
}
/**
* 支撑版本
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
/**
* 经过注解处理器处理注解,生成代码到build文件夹中
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//获取注解 例如 :@BindPath("main/MainActivity")
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(BindPath.class);
Map<String, String> map = new HashMap<>();
for (Element element : elementsAnnotatedWith) {
TypeElement typeElement = (TypeElement) element;
//key为注解的Activity 例如:MainActivity
String key = typeElement.getQualifiedName().toString() + ".class";
//value为注解办法中的值 例如:"main/MainActivity"
String value = typeElement.getAnnotation(BindPath.class).value();
map.put(key, value);
}
makefile(map);
return false;
}
private void makefile(Map<String, String> map) {
if (map.size() > 0) {
//界说编译时类生成时的包名
String packageName = "com.llw.util";
//界说处理器的包名
String routerPackageName = "com.llw.basic.router";
//获取接口名IRouter
ClassName interfaceName = ClassName.get(routerPackageName, "IRouter");
//获取类名 ARouter
ClassName className = ClassName.get(routerPackageName, "ARouter");
//创立类结构器,例如ActivityRouterUtil 加上时间戳是为了防止生成的编译时类名重复报错
TypeSpec.Builder classBuilder = TypeSpec.classBuilder("ActivityRouterUtil" + System.currentTimeMillis())
//增加修饰符 public
.addModifiers(Modifier.PUBLIC)
//增加完成接口,例如 implements IArouter
.addSuperinterface(interfaceName);
//创立办法结构器 办法名putActivity()
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("putActivity")
//增加注解
.addAnnotation(Override.class)
//增加修饰符
.addModifiers(Modifier.PUBLIC);
//这儿遍历是为了给每一个增加了注解进行代码生成
for (String activityName : map.keySet()) {
String value = map.get(activityName);
//例如 com.llw.arouter.ARouter.getInstance().addActivity("login/LoginActivity",com.llw.login.LoginActivity.class);
methodBuilder.addStatement("$L.getInstance().addActivity($S, $L)", className, value, activityName);
}
//在类结构器中增加办法
classBuilder.addMethod(methodBuilder.build());
try {
//最后写入文件
JavaFile.builder(packageName, classBuilder.build())
.build()
.writeTo(filer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
代码中的注解现已很清楚了,便是生成一个编译时类,编译时类的代码如下图:
③ 注解处理器运用
要使这个注解处理器收效,需求分别在app、login、personal的build.gradle中的denpendencies{}下增加如下所示代码:
annotationProcessor project(path: ':annotation_compiler')
增加的位置如下面三图所示:
增加好之后Sync Now,然后运转一下,运转之后在app模块下会生成一个build文件夹,然后层层打开,终究如下图所示:
咱们方才的AnnotationCompiler中所写的代码便是为了生成这个编译时文件,假如你没有找到这个文件,点击这个刷新按钮,刷新一下项目文件。
Android Studio有时分文件查看不是很及时,所以手动刷新一下,看有没有生成这个文件。假如文件生成了,那么你再依次查看一下login、personal组件中的build文件夹中有没有生成相关文件。
四、运用路由
下面要做的便是能够进行组件之间的Activity跳转,例如从app的MainActivity跳转到login的LoginActivity,再从LoginActivity跳转到personal的PersonalActivity,要做到这一步咱们需求对路由进行初始化,能够在basic模块中的BaseApplication中完成。
而为了使BaseApplication收效,咱们需求在各自组件中的AndroidManifest.xml进行注册,实际上咱们各个组件应该自己写一个Application类承继自BaseApplication,可是现在咱们的功用比较简略,所以就不这样写了,直接运用BaseApplication进行注册即可,下面我在app组件的AndroidManifest.xml中注册。
其他的组件自己去注册一下。
① 页面跳转
然后咱们在MainActivity中增加这样一行代码。
ARouter.getInstance().jumpActivity("login/LoginActivity");
这儿目的很明显,我要跳转到LoginActivity,那么咱们在LoginActivity的onCreate()办法中增加
ARouter.getInstance().jumpActivity("personal/PersonalActivity");
我相信你知道怎样增加这行代码,这样就能跳转到PersonalActivity中了,下面咱们运转测验一下。
这儿能够看到,直接就进入了PersonalActivity,可是你会发现还有LoginActivity的Toast显示出来,这证明确实是从MainActivity过来的,终究抵达PersonalActivity,你要是延时跳转那就会很明显,自行测验吧。
② 页面带参跳转
修正一下LoginActivity的onCreate()办法,进行传参,代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
showMsg("LoginActivity");
Bundle bundle = new Bundle();
bundle.putString("data","Very Good!");
ARouter.getInstance().jumpActivity("personal/PersonalActivity", bundle);
}
然后修正PersonalActivity中的onCreate()办法,接收参数,代码如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_personal);
String data = getIntent().getExtras().getString("data");
if (data != null) {
showMsg(data);
}
}
下面从头运转一下:
OK,现在页面的组件通讯就初步完成了。
五、源码
欢迎 Star 和 Fork
源码地址:StudyComponent