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(ActivityDestination
和 FragmentDestination
)
@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
文件。
以上基于慕课网视频,目的是为了做一个备忘