android注解apt实现收集activity列表

需求:配合Arouter使用,将每个子module的activity的path和name收集起来,这样就可以在app下生成一个依赖的module的activity列表,方便调式.点击直接进入activity.

activitylist.png

预期方式:直接在每个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 set, RoundEnvironment roundEnvironment) {
        Set 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

你可能感兴趣的:(android注解apt实现收集activity列表)