需求:配合Arouter使用,将每个子module的activity的path和name收集起来,这样就可以在app下生成一个依赖的module的activity列表,方便调式.点击直接进入activity.
预期方式:直接在每个activity上写上注解,自动收集,省去每次都更改的问题.
实现:
注解是比较好的方式.
1.在studio中创建一个javalibrary.创建annotation类.
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface CollectActivity {
String activityPath();
String activityName();
}
2.在studio中创建一个javalibrary,用来处理annotation类.
修改下面的build.gradle,依赖上面创建的annotation的library.和谷歌的Gson后面用来解析和创建json文件.
plugins {
id 'java-library'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api project(':practice-annotation')
api dps.gson
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
3.实现processor类对annotation处理.
public class CollectProcessor extends AbstractProcessor {
public static final String DATA_FILE_NAME = "DATA.json";
private Messager mMessager;
private Types mTypeUtils;
private Filer mFiler;
private Elements mElementUtils;
// 是否是app的module
private boolean mIsApp;
private CollectData mCollectData;
// 初始化
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mMessager = processingEnv.getMessager();
mTypeUtils = processingEnv.getTypeUtils();
mFiler = processingEnv.getFiler();
mElementUtils = processingEnv.getElementUtils();
mIsApp = "true".equals(processingEnvironment.getOptions().get("isApp"));
mCollectData = new CollectData();
// 当app模块下没有注解不走process方法.直接读取注解数据
if (mIsApp) {
readAnnotationData();
writeToFile();
}
}
// 支持的java版本
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
// 支持的注解
@Override
public Set getSupportedAnnotationTypes() {
Set annotations = new LinkedHashSet<>();
annotations.add(CollectActivity.class.getCanonicalName());
return annotations;
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
Set extends Element> elements = roundEnvironment.getElementsAnnotatedWith(CollectActivity.class);
// 先将所有的注解保存起来.
for (Element element : elements) {
if (element.getKind() != ElementKind.CLASS) {
// 打印错误日志
error(element, "Only classes can be annotated with @%s", CollectActivity.class.getSimpleName());
// 退出
return true;
}
CollectActivity annotation = element.getAnnotation(CollectActivity.class);
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.ActivityName = annotation.activityName();
activityInfo.ActivityPath = annotation.activityPath();
mCollectData.activityList.add(activityInfo);
}
if (!mIsApp) {
saveAnnotationData();
}
return false;
}
private void writeToFile() {
StringBuilder builder = new StringBuilder();
builder.append("package com.jlhan.collect;\n\n");
builder.append("import com.jlhan.core.ActivityFactory;\n");
builder.append("public class ActivityListHolder { \n");
builder.append("public static void init() {\n");
for (ActivityInfo info : mCollectData.activityList) {
// 检查这个注解是否是一个类
builder.append("ActivityFactory.addActivity(\"");
builder.append(info.ActivityPath);
builder.append("\",\"");
builder.append(info.ActivityName);
builder.append("\");\n");
}
builder.append("}\n");
builder.append("}\n");
try {
JavaFileObject source = mFiler.createSourceFile("com.jlhan.collect.ActivityListHolder");
Writer writer = source.openWriter();
writer.write(builder.toString());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
note(">>> collect is finish... <<<");
}
private void readAnnotationData() {
// 从ClassLoader中的urls中读取每个module生成的jar包.拿到里面生成的DATA.json
// 这种方式只适合当前gradle版本,尝试过6.1时,打包时不会再执行createFullJarDebug,导致URLCalssloader中无法拿到jar包.
ClassLoader loader = getClass().getClassLoader();
try {
if (loader instanceof URLClassLoader) {
List fileList = new ArrayList<>();
URL[] urls = ((URLClassLoader) loader).getURLs();
for (URL url : urls) {
String filePath = url.getFile();
File file = new File(filePath);
if (file.getName().endsWith(".jar") && file.exists()) {
ZipFile zipFile = new ZipFile(file);
ZipEntry entry = zipFile.getEntry(DATA_FILE_NAME);
if (entry == null) {
continue;
}
InputStream inputSteam = zipFile.getInputStream(entry);
final int bufferSize = 1024;
final char[] buffer = new char[bufferSize];
final StringBuilder out = new StringBuilder();
InputStreamReader in = new InputStreamReader(inputSteam, StandardCharsets.UTF_8);
for (; ; ) {
int rsz = in.read(buffer, 0, buffer.length);
if (rsz < 0) {
break;
}
out.append(buffer, 0, rsz);
fileList.add(out.toString());
}
}
}
if (fileList.size() > 0) {
for (String bindingString : fileList) {
CollectData collectData = new Gson().fromJson(bindingString, CollectData.class);
mCollectData.activityList.addAll(collectData.activityList);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void saveAnnotationData() {
try {
FileObject file = mFiler.createResource(StandardLocation.CLASS_OUTPUT, "", DATA_FILE_NAME);
Writer writer = file.openWriter();
writer.write(new Gson().toJson(mCollectData));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void error(Element element, String s, String simpleName) {
// 当使用这个error打印时会退出processor,导致打包也结束.
mMessager.printMessage(Diagnostic.Kind.ERROR, String.format(s, simpleName), element);
}
private void note(String sNote) {
mMessager.printMessage(Diagnostic.Kind.NOTE, sNote);
}
}
不足:
1.由于高版本的gradle的URLClassloader不能拿到full_jar下面打好的当前module包,所以暂时只能用低版本的.需要在根build.gradle中使用3.6.3版本的gradle.
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
在gradle-wrapper中修改这个版本
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
2.有可能频繁读取所有jar包会延长打包时间.
后续有好方法再来更新.
项目地址:https://github.com/JalongHan/Practice
项目中practice-annotation,practice-comipler