美文网首页
Android 通过自定义注解生成 json 配置文件(kotl

Android 通过自定义注解生成 json 配置文件(kotl

作者: ChenME | 来源:发表于2020-05-07 12:01 被阅读0次

Android 通过自定义注解生成 json 配置文件(kotlin 版本)

1. 创建注解 module;

1. 首先先创建一个用于编写 annotation 的 module;

file --> new --> new module --> 选择 Java or Kotlin Library --> 输入 module 名称 libnavannotation

2. 配置 gradle;

apply plugin: 'java-library'
apply plugin: 'kotlin'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

// 设置最低 Java 版本
sourceCompatibility = "8"
targetCompatibility = "8"

3. 创建 annotation(ActivityDestinationFragmentDestination

@Target(AnnotationTarget.CLASS) // 注解使用在类上
annotation class ActivityDestination(
    val pageUrl: String,
    val needLogin: Boolean = false, // 是否需要登录
    val asStarter: Boolean = false // 是否是启动页面
)
@Target(AnnotationTarget.CLASS) // 注解使用在类上
annotation class FragmentDestination(
    val pageUrl: String,
    val needLogin: Boolean = false, // 是否需要登录
    val asStarter: Boolean = false // 是否是启动页面
)

2. 创建注解编译器

1. 首先先创建一个用于编写 compiler 的 module;

file --> new --> new module --> 选择 Java or Kotlin Library --> 输入 module 名称 libnavcompiler

2. 配置 gradle;

apply plugin: 'java-library'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    implementation project(':libnavannotation') // 自定义注解
    implementation 'com.alibaba:fastjson:1.2.59' // 用于生成 json

    /*
     * 注意:
     * com.google.auto.service:auto-service:1.0-rc6 (auto-service 在该本下,对应的 gradle 版本及其插件是兼容的,可以生成对应的 json 文件)
     * distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
     * com.android.tools.build:gradle:3.6.3
     */
    implementation 'com.google.auto.service:auto-service:1.0-rc6' // annotationProcessor  project()应用一下,编译时就能自动执行 @AutoService 所注解的类
//    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' // 如果是 Java 代码,只需使用 annotationProcessor 即可
    kapt 'com.google.auto.service:auto-service:1.0-rc6' // 但是基于 kotlin 项目,必须使用 kapt
}

// 设置最低 Java 版本
sourceCompatibility = "8"
targetCompatibility = "8"

3. 创建解析器(NavProcessor);

import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONObject
import com.google.auto.service.AutoService
import mm.chenme.lib.libnavannotation.ActivityDestination
import mm.chenme.lib.libnavannotation.FragmentDestination
import java.io.File
import java.io.FileOutputStream
import java.io.OutputStreamWriter
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Element
import javax.lang.model.element.TypeElement
import javax.tools.Diagnostic
import javax.tools.StandardLocation
import kotlin.math.abs

@AutoService(Processor::class) // 告诉 annotationProcessor 在 project()(编译)应用时要自动执行该类
@SupportedSourceVersion(SourceVersion.RELEASE_8) // 声明我们所支持的jdk版本
@SupportedAnnotationTypes(// 声明该注解处理器想要处理那些注解
    "mm.chenme.lib.libnavannotation.ActivityDestination",
    "mm.chenme.lib.libnavannotation.FragmentDestination"
)
class NavProcessor : AbstractProcessor() {

    private lateinit var messager: Messager // 日志打印,在java环境下不能使用 android.util.log.e()
    private lateinit var filer: Filer //文件处理工具

    private val OutputFileName = "destination.json" // 输出的文件名称

    override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        messager = processingEnv.messager
        filer = processingEnv.filer
    }

    override fun process(annotations: MutableSet<out TypeElement>, roundEnv: RoundEnvironment): Boolean {

        /*
         * 通过处理器环境上下文 roundEnv 分别获取项目中标记的 ActivityDestination.class 和 FragmentDestination.class 注解。
         * 此目的就是为了收集项目中哪些类被注解标记了
         */
        val activityElements = roundEnv.getElementsAnnotatedWith(ActivityDestination::class.java)
        val fragmentElements = roundEnv.getElementsAnnotatedWith(FragmentDestination::class.java)

        if (activityElements.isNotEmpty() || fragmentElements.isNotEmpty()) {

            // 分别处理 ActivityDestination  和 FragmentDestination 注解类型,并收集到 destMap 中,该 map 中以此就能记录下所有的页面信息了
            val destMap = HashMap<String, JSONObject>()
            handleDestination(activityElements, ActivityDestination::class.java, destMap)
            handleDestination(fragmentElements, FragmentDestination::class.java, destMap)

            /*
             * 将文件写到 app/src/main/assets/ 目录下面
             *
             * createResource(p1, p2, p3)
             * p1:指定文件输出的地方
             *     StandardLocation.CLASS_OUTPUT:java 文件生成 class 文件的位置,在(/app/build/intermediates/javac/debug/classes/)目录下
             *     StandardLocation.SOURCE_OUTPUT:java 文件的位置,一般在(/{项目名}/app/build/generated/source/apt/)目录下
             *     StandardLocation.CLASS_PATH 和 StandardLocation.SOURCE_PATH 用的不多,指的了这个参数,就要指定生成文件的 pkg 包名了
             * p3:输出的文件名称
             *
             * 由于我们想要把json文件生成在 app/src/main/assets/ 目录下,所以这里可以对字符串做一个截取和替换
             */
            val res = filer.createResource(StandardLocation.CLASS_OUTPUT, "", OutputFileName) // 创建源文件
            val resPath = res.toUri().path
            messager.printMessage(Diagnostic.Kind.NOTE, "\nresPath --> $resPath\n")
            val appPath = resPath.substring(0, resPath.indexOf("app") + 4)
            val assetsPath = "${appPath}src/main/assets/"

            // app/src/main/assets/ 目录如果不存在,就创建出来
            val file = File(assetsPath)
            if (!file.exists()) {
                file.mkdirs()
            }

            // 每次都覆盖 app/src/main/assets/{OutputFileName} 文件
            val outputFile = File(file, OutputFileName)
            if (outputFile.exists()) {
                outputFile.delete()
            }
            outputFile.createNewFile()

            val fos = FileOutputStream(outputFile)
            val writer = OutputStreamWriter(fos, "UTF-8")
            writer.write(JSON.toJSONString(destMap))
            writer.flush()

            writer.close()
            fos.close()
        }
        return true
    }

    private fun handleDestination(elements: Set<Element>, annotationClz: Class<out Annotation>, destMap: HashMap<String, JSONObject>) {
        elements.forEach {
            var pageUrl = ""
            val clzName = (it as TypeElement).qualifiedName.toString()
            val id = abs(clzName.hashCode())
            var needLogin = false
            var asStarter = false
            var isFragment = false

            val annotation = it.getAnnotation(annotationClz)
            when (annotation) {
                is ActivityDestination -> {
                    pageUrl = annotation.pageUrl
                    needLogin = annotation.needLogin
                    asStarter = annotation.asStarter
                    isFragment = false
                }
                is FragmentDestination -> {
                    pageUrl = annotation.pageUrl
                    needLogin = annotation.needLogin
                    asStarter = annotation.asStarter
                    isFragment = true
                }
            }

            if (destMap.containsKey(pageUrl)) {
                messager.printMessage(Diagnostic.Kind.ERROR, "不同的页面不允许使用相同的 pageUrl --> $clzName")
            } else {
                val obj = JSONObject()
                obj["pageUrl"] = pageUrl
                obj["clzName"] = clzName
                obj["id"] = id
                obj["needLogin"] = needLogin
                obj["asStarter"] = asStarter
                obj["isFragment"] = isFragment
                destMap[pageUrl] = obj
            }
        }
    }
}

3. 生成 json 文件;

1. 在项目 app gradle 中配置注解编译器;

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    // ... 其他配置省略

    compileOptions {
        // 设置最低 Java 版本
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        // 设置最低 Java 版本
        jvmTarget = "1.8"
    }
}

dependencies {
    // ... 其他配置省略

    implementation project(':libnavannotation') // 注解
//    annotationProcessor project(':libnavcompiler') // 如果是 Java 代码,只需使用 annotationProcessor 即可
    kapt project(":libnavcompiler") // 由于是 Kotlin,所以需要使用 kapt
}

2. 生成 json 文件;

配置完成后,同步 gradle,直接构建项目,构建完成后,就可以在 app/src/main/assets/ 目录下生成 destination.json 文件。


以上基于慕课网视频,目的是为了做一个备忘

相关文章

网友评论

      本文标题:Android 通过自定义注解生成 json 配置文件(kotl

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