Android Kotlin编译时注解

前面阅读了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

猜你喜欢

转载自blog.csdn.net/weixin_43662090/article/details/110954366