前面阅读了ARouter的源码, 他主要是通过编译时注解来实现主要的逻辑的, 我们仿照他来实现下编译时注解
1. module结构
如上, 我们新建两个Java-library
- my-annotator 用于声明注解
- my-annotator-compiler 用于在编译时生成文件
其中, app的build.gradle的完整内容如下
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.plbear.lxc"
minSdkVersion 26
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
// 我们配置cmake命令
cppFlags "-std=c++11 -frtti -fexceptions"
abiFilters 'armeabi-v7a', 'x86'
arguments "-DANDROID_STL=c++_shared"
}
}
ndk {
abiFilters 'armeabi-v7a', 'x86'
}
}
sourceSets {
main {
//配置我们自己的libs库目录
jniLibs.srcDirs = ['src/main/jniLibs/libs']
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
ndk {
//设置为arm32的, 其他的架构往后面加文件就可以
abiFilters 'armeabi-v7a', 'x86'
}
}
}
externalNativeBuild {
cmake {
// 设置编译文件
path "CMakeLists.txt"
version "3.10.2"
}
}
dataBinding {
enabled = true
}
packagingOptions {
pickFirst 'lib/armeabi-v7a/libopencv_java4.so'
pickFirst 'lib/x86/libopencv_java4.so'
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
compileOnly project(':my-annotator-compiler')
kapt project(':my-annotator-compiler')
implementation project(':my-annotator')
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation project(path: ':base')
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3'
implementation 'com.alibaba:arouter-api:1.5.0'
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
my-annotation的build.gradle完整内容如下
plugins {
id 'java-library'
id 'kotlin'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
my-annotation-compiler的build.gradle完整内容如下
apply plugin: 'kotlin'
apply plugin: 'java-library'
apply plugin: 'kotlin-kapt'
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
kapt 'com.google.auto.service:auto-service:1.0-rc6'
implementation 'com.google.auto.service:auto-service-annotations:1.0-rc6'
implementation 'com.squareup:javapoet:1.8.0'
implementation project(':my-annotator')
}
sourceCompatibility = "7"
targetCompatibility = "7"
2. 注解类
我们首先定义一个接口
package com.plbear.annotator
interface IFruit {
fun getName(): String
}
然后声明一个注解
package com.plbear.annotator
@Target(AnnotationTarget.CLASS)
annotation class FruitFactory(
val name: String
)
接下来在某一个Activity中使用注解
package com.plbear.lxc.module.annotation
import android.content.Context
import android.content.Intent
import com.plbear.annotator.FruitFactory
import com.plbear.lxc.R
import com.plbear.lxc.base.BaseActivity
import com.plbear.lxc.databinding.ActivityBuildAnnotationBinding
@FruitFactory(name = "apple")
class BuildAnnotationActivity : BaseActivity<ActivityBuildAnnotationBinding>() {
companion object {
fun start(context: Context) {
val i = Intent(context, BuildAnnotationActivity::class.java)
context.startActivity(i)
}
}
override fun getLayoutId(): Int {
return R.layout.activity_build_annotation
}
override fun afterActivityCreate() {
}
}
3. 编译的module(my-annotation-compiler)
package com.plbear.buildannotator
import com.google.auto.service.AutoService
import com.plbear.annotator.FruitFactory
import com.squareup.javapoet.*
import java.io.File
import java.lang.reflect.Type
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.util.Elements
import javax.lang.model.util.Types
import javax.tools.Diagnostic
@AutoService(Processor::class)
@SupportedSourceVersion(SourceVersion.RELEASE_8)
class FruitProcessor : AbstractProcessor() {
private lateinit var message: Messager
private lateinit var typeUtils: Types
private lateinit var elementUtils: Elements
private lateinit var mFile:Filer
override fun init(env: ProcessingEnvironment?) {
super.init(env)
if (env == null) return
message = env.messager
typeUtils = env.typeUtils
elementUtils = env.elementUtils
mFile = env.filer
}
override fun process(set: MutableSet<out TypeElement>?, env: RoundEnvironment?): Boolean {
if (set == null || env == null) return false
logcat("process in")
val elementList = env.getElementsAnnotatedWith(FruitFactory::class.java)
val activityType = this.elementUtils.getTypeElement("android.app.Activity").asType()
val fruitInterface = this.elementUtils.getTypeElement("com.plbear.annotator.IFruit")
val stringType = this.elementUtils.getTypeElement(String::class.java.canonicalName).asType()
for (element in elementList) {
logcat(element.toString())
val type = element.asType()
if (typeUtils.isSubtype(type, activityType)) {
logcat("is activity")
//拿到注解
val annotation = element.getAnnotation(FruitFactory::class.java)
val methodSpec = MethodSpec.methodBuilder("getName")
.addAnnotation(Override::class.java)
.addModifiers(Modifier.PUBLIC)
.addCode("return \"${
annotation.name}\";")
.returns(TypeName.get(stringType))
val className = type.toString().substringAfterLast(".")+"$$"+annotation.name
val typeSpec = TypeSpec.classBuilder(className)
.addSuperinterface(ClassName.get(fruitInterface))
.addJavadoc("这是我生成的代码!!!")
.addMethod(methodSpec.build())
.addModifiers(Modifier.PUBLIC)
.build()
JavaFile.builder("com.plbear.buildannotator.out",typeSpec)
.build().writeTo(mFile)
}
}
return false
}
override fun getSupportedAnnotationTypes(): MutableSet<String> {
return setOf(FruitFactory::class.java.canonicalName).toMutableSet()
}
private fun logcat(msg: String?) {
val outMsg = msg ?: "empty"
message.printMessage(Diagnostic.Kind.WARNING, "yanlog $outMsg \n")
}
}
我们编译完整之后, 就可以看到生成了如下文件:
package com.plbear.buildannotator.out;
import com.plbear.annotator.IFruit;
import java.lang.Override;
import java.lang.String;
/**
* 这是我生成的代码!!! */
public class BuildAnnotationActivity$$apple implements IFruit {
@Override
public String getName() {
return "apple";}
}
4. 启动时遍历自动生成的class并调用
如上, 我们已经成功的生成了目标文件, 那么, 我们希望在Application onCreate的过程中调用这个文件, 做法如下
fun getAnnotationFile(context: Context) {
logcat("is VM capable:${
isVMMultidexCapable()}")
val sourceList = getAllDexPaths(context)
for (source in sourceList) {
logcat("source-------$source -------------")
val dexFile = DexFile(source)
val list = dexFile.entries()
while (list.hasMoreElements()) {
val name = list.nextElement()
if (name.startsWith("com.plbear.buildannotator.out")){
logcat("name:$name")
val fruit = (Class.forName(name).getConstructor().newInstance()) as IFruit
logcat(fruit.getName())
}
}
}
}
fun getAllDexPaths(context: Context): ArrayList<String> {
val applicationInfo = context.packageManager.getApplicationInfo(context.packageName, 0)
val sourceDir = applicationInfo.sourceDir
//todo 这么写是不太准确的, 具体可以参考ARouter框架中的getSourcePaths方法
val result = ArrayList<String>()
result.add(sourceDir)
applicationInfo.splitSourceDirs?.let {
result.addAll(it)
}
return result
}
日志打印如下
2020-12-10 21:35:46.049 11360-11360/com.plbear.lxc E/imlog: source-------/data/app/com.plbear.lxc-90UkjzMYA_nwDcskMADmzg==/base.apk -------------
2020-12-10 21:35:46.175 11360-11360/com.plbear.lxc E/imlog: name:com.plbear.buildannotator.out.BuildAnnotationActivity$$apple
2020-12-10 21:35:46.176 11360-11360/com.plbear.lxc E/imlog: apple