美文网首页
android注解apt实现收集activity列表

android注解apt实现收集activity列表

作者: 神的漾 | 来源:发表于2020-10-29 16:09 被阅读0次

    需求:配合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<String> getSupportedAnnotationTypes() {
            Set<String> 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<String> 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列表

          本文链接:https://www.haomeiwen.com/subject/frokvktx.html