文章目錄
第一章 註解
一、基本使用
1.首先聲明註解類
annotation class MyAnnotation(val value: String, val name: String)
2.在Activity中使用反射調用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val methods = javaClass.declaredMethods
methods.forEach {
if (it.isAnnotationPresent(MyAnnotation::class.java)) {
val annotation = it.getAnnotation(MyAnnotation::class.java)
Log.d("~~~", "value = ${annotation.value}, name = ${annotation.name}")
}
}
}
@MyAnnotation("1.0","Cassin")
fun testAnnotation(){
}
}
運行程序,Log控制檯顯示如下:
~~~: value = 1.0, name = Cassin
二、元註解
1.@Retention
@Retention標誌了註解存活的時間
@Retention的源碼如下:
/**
* This meta-annotation determines whether an annotation is stored in binary output and visible for reflection. By default, both are true.
*
* @property value necessary annotation retention (RUNTIME, BINARY or SOURCE)
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Retention(val value: AnnotationRetention = AnnotationRetention.RUNTIME)
其中,AnnotationRentention源碼如下:
/**
* Contains the list of possible annotation's retentions.
*
* Determines how an annotation is stored in binary output.
*/
public enum class AnnotationRetention {
/** Annotation isn't stored in binary output */
SOURCE,
/** Annotation is stored in binary output, but invisible for reflection */
BINARY,
/** Annotation is stored in binary output and visible for reflection (default retention) */
RUNTIME
}
根據源碼的註釋,可知:
- @Retention設置爲SOURCE時,註解僅存活在源碼中,不會存儲到二進制輸出文件中。
- 設置爲BINARY時,註解會存儲到二進制輸出文件中,但是對反射不可見。
- 設置爲RUNTIME時,註解會存儲到二進制輸出文件中,並且對反射可見。
對於同一個註解設置不同的@Retention,筆者測試結果是:
AnnotationRetention.BINARY註解打包出的apk體積最大
AnnotationRetention.RUNTIME註解打包出的apk體積第二大
AnnotationRetention.SOURCE註解打包出的apk體積最小
如果註解是用來配置程序運行必需的參數,則Retention必須是AnnotationRetention.RUNTIME,否則打包出來的apk無法獲取配置的參數。
2.@MustBeDocumented
表示將註解添加到Javadoc中,默認爲不添加。
@MustBeDocumented的源碼如下:
/**
* This meta-annotation determines that an annotation is a part of public API and therefore should be included in the generated
* documentation for the element to which the annotation is applied.
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class MustBeDocumented
這裏順便講一下Kotlin怎麼生成Javadoc
2.1.Kotlin使用dokka生成javadoc
dokka庫的github地址爲:https://github.com/Kotlin/dokka
2.2.dokka使用方式
1.在project的build.gradle中添加:
classpath 'org.jetbrains.dokka:dokka-android-gradle-plugin:0.9.18'
2.在app模塊的build.gradle中添加:
apply plugin: 'org.jetbrains.dokka-android'
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
options.encoding = "UTF-8"
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
outputFormat = "javadoc"
outputDirectory = javadoc.destinationDir
}
task generateJavadoc(type: Jar, dependsOn: dokkaJavadoc) {
group = 'jar'
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives generateJavadoc
}
sync之後,打開terminal,Windows環境下,輸入:
gradlew generateJavadoc
Mac環境下,輸入:
./gradlew generateJavadoc
Build Success之後,在app模塊下的 build/libs 文件夾下會生成一個app-javadoc.jar,使用解壓工具將其解壓,就可以看到每個類對應的html格式的javadoc了。
上例中,不添加@MustBeDocumented生成的javadoc如下:
添加了@MustBeDocumented生成的javadoc如下:
3.@Target
Target指定此註解能夠對哪些類型進行註解。
@Target源碼如下:
/**
* This meta-annotation indicates the kinds of code elements which are possible targets of an annotation.
*
* If the target meta-annotation is not present on an annotation declaration, the annotation is applicable to the following elements:
* [CLASS], [PROPERTY], [FIELD], [LOCAL_VARIABLE], [VALUE_PARAMETER], [CONSTRUCTOR], [FUNCTION], [PROPERTY_GETTER], [PROPERTY_SETTER].
*
* @property allowedTargets list of allowed annotation targets
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
@MustBeDocumented
public annotation class Target(vararg val allowedTargets: AnnotationTarget)
其中,AnnotationTarget源碼如下:
/**
* Contains the list of code elements which are the possible annotation targets
*/
public enum class AnnotationTarget {
/** Class, interface or object, annotation class is also included */
CLASS,
/** Annotation class only */
ANNOTATION_CLASS,
/** Generic type parameter (unsupported yet) */
TYPE_PARAMETER,
/** Property */
PROPERTY,
/** Field, including property's backing field */
FIELD,
/** Local variable */
LOCAL_VARIABLE,
/** Value parameter of a function or a constructor */
VALUE_PARAMETER,
/** Constructor only (primary or secondary) */
CONSTRUCTOR,
/** Function (constructors are not included) */
FUNCTION,
/** Property getter only */
PROPERTY_GETTER,
/** Property setter only */
PROPERTY_SETTER,
/** Type usage */
TYPE,
/** Any expression */
EXPRESSION,
/** File */
FILE,
/** Type alias */
@SinceKotlin("1.1")
TYPEALIAS
}
根據源碼可知,@Target各屬性的含義是:
- AnnotationTarget.CLASS 可以給一個類型進行註解 , 類 、 接口 、 對象 、 甚至註解類本身
- AnnotationTarget.ANNOTATION_CLASS 可以給一個註解類進行註解
- AnnotationTarget.TYPE_PARAMETER 泛型參數 ( 暫未支持 )
- AnnotationTarget.VALUE_PARAMETER 方法 、 構造函數的參數
- AnnotationTarget.PROPERTY (計算)屬性(該註解Java不可見)
- AnnotationTarget.PROPERTY_GETTER 屬性getter方法
- AnnotationTarget.PROPERTY_SETTER 屬性setter方法
- AnnotationTarget.FIELD 字段變量 , 包括PROPERTY的備用字段 ( backing field)
- AnnotationTarget.LOCAL_VARIABLE 局部變量
- AnnotationTarget.CONSTRUCTOR 構造函數
- AnnotationTarget.FUNCTION 方法 、 函數 ( 不包括構造函數 )
- AnnotationTarget.FILE 文件整體
- AnnotationTarget.TYPE 泛型支持
- AnnotationTarget.TYPEALIAS 類型別名
- AnnotationTarget.EXPRESSION 表達式
4.@Inherited
表示如果某個類註解了此註解,其子類是否繼承此註解,默認不繼承。
@Inherited源碼如下:
/**
* Indicates that an annotation type is automatically inherited. If
* an Inherited meta-annotation is present on an annotation type
* declaration, and the user queries the annotation type on a class
* declaration, and the class declaration has no annotation for this type,
* then the class's superclass will automatically be queried for the
* annotation type. This process will be repeated until an annotation for this
* type is found, or the top of the class hierarchy (Object)
* is reached. If no superclass has an annotation for this type, then
* the query will indicate that the class in question has no such annotation.
*
* <p>Note that this meta-annotation type has no effect if the annotated
* type is used to annotate anything other than a class. Note also
* that this meta-annotation only causes annotations to be inherited
* from superclasses; annotations on implemented interfaces have no
* effect.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.3.3 @Inherited
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
我們做個簡單的測試:
1.先聲明一個不帶@Inherited的註解:
annotation class MyAnnotation(val value: String)
2.在BaseActivity中使用此註解:
// 測試父類上的註解能否被繼承
@MyAnnotation("superClass")
abstract class BaseActivity : AppCompatActivity() {
// 測試父類方法中的註解能否被繼承
@MyAnnotation("superFun")
protected fun superFun() {
}
// 測試子類重寫父類方法是能否繼承註解
@MyAnnotation("superOverrideFun")
open fun superOverrideFun() {
}
// 測試父類抽象方法中的註解能否被繼承
@MyAnnotation("superAbstractFun")
protected abstract fun superAbstractFun()
}
3.MainActivity繼承BaseActivity
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val annotations = javaClass.annotations
annotations.forEach {
if (it is MyAnnotation) {
Log.d("~~~annotations", "value = ${it.value}")
}
}
val methods = javaClass.methods
methods.forEach {
if (it.isAnnotationPresent(MyAnnotation::class.java)) {
val annotation = it.getAnnotation(MyAnnotation::class.java)
Log.d("~~~methods", "${it.name}:value = ${annotation.value}")
}
}
}
override fun superOverrideFun() {
}
override fun superAbstractFun() {
}
}
使用 javaClass.annotations 反射獲取類上的所有註解,使用 javaClass.methods 反射獲取類中所有方法。運行程序,Log控制檯輸出如下:
~~~methods: superFun:value = superFun
4.在註解中加上@Inherited:
@Inherited
annotation class MyAnnotation(val value: String)
運行程序,Log控制檯輸出如下:
~~~annotations: value = superClass
~~~methods: superFun:value = superFun
所以我們可以得出結論:
無@Inherited | 有@Inherited | |
---|---|---|
子類能否繼承父類的類上的註解 | 否 | 能 |
子類能否繼承到父類抽象方法的註解 | 否 | 否 |
子類能否繼承父類方法上的註解 | 能 | 能 |
子類重寫父類上的方法,能否繼承到註解 | 否 | 否 |
5.@Repeatable
表示此註解是否可重複註解。
@Repeatable源碼如下:
/**
* This meta-annotation determines that an annotation is applicable twice or more on a single code element
*/
@Target(AnnotationTarget.ANNOTATION_CLASS)
public annotation class Repeatable
設置@Repeatable時,註解的@Retention必須被申明爲僅存活在源碼中,暫不支持其他@Retention。
例如:
聲明一個@Repeatable的註解:
@Repeatable
@Retention(AnnotationRetention.SOURCE)
annotation class MyAnnotation(val value: String)
多次使用註解:
@MyAnnotation("a")
@MyAnnotation("b")
@MyAnnotation("c")
class MainActivity : AppCompatActivity() {
@MyAnnotation("a")
@MyAnnotation("b")
@MyAnnotation("c")
val field = 1
@MyAnnotation("a")
@MyAnnotation("b")
@MyAnnotation("c")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
@MyAnnotation("a")
@MyAnnotation("b")
@MyAnnotation("c")
fun testAnnotation() {
}
}
6.系統自帶註解
6.1.@SupressWarning
@SuppressWarnings註解用來忽略代碼警告,源碼轉換成kotlin如下:
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FILE,
AnnotationTarget.FIELD,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.LOCAL_VARIABLE
)
@Retention(AnnotationRetention.SOURCE)
annotation class SuppressWarnings(vararg val value: String)
@Retention是AnnotationRetention.SOURCE,說明只會在源碼中保留註解,打包時會忽略此註解。由@Target可知可以在類、文件、字段、方法、getter方法、setter方法、值參數、構造方法、本地變量上使用此註解。
6.2.@Deprecated
@Deprecated標誌代碼已過時,源碼轉換成Kotlin如下:
@Target(
AnnotationTarget.CLASS,
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY,
AnnotationTarget.ANNOTATION_CLASS,
AnnotationTarget.CONSTRUCTOR,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.TYPEALIAS
)
@MustBeDocumented
public annotation class Deprecated(
val message: String,
val replaceWith: ReplaceWith = ReplaceWith(""),
val level: DeprecationLevel = DeprecationLevel.WARNING
)
@MustBeDocumented標誌着此註解會生成到javadoc中,由@Target可知可以在類、方法、屬性、註解、構造方法、setter方法、getter方法,類型別名上使用此註解。
6.3.@Override
表示重寫方法,源碼轉換成Kotlin如下:
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.SOURCE)
annotation class Override
@Retention是AnnotationRetention.SOURCE,表示此註解只會在源碼中保留,由@Target可知,可以在方法、getter、setter中使用此註解
還有一些系統自帶註解,如@SafeVarargs、@FunctionalInterface,有興趣的讀者可以自己瞭解
第二章 meta-data
基本使用
1.在application中聲明meta-data
<!--保存值-->
<meta-data android:name="value_key" android:value="value"/>
<!--保存id-->
<meta-data android:name="resource_key" android:resource="@string/app_name"/>
<!--保存id對應的值-->
<meta-data android:name="resource_value" android:value="@string/app_name"/>
2.通過PackageManager調用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val applicationInfo = packageManager.getApplicationInfo(packageName,PackageManager.GET_META_DATA)
val metaData = applicationInfo.metaData
/**獲取meta-data中的參數*/
Log.d("~~~","value_key:${metaData.get("value_key")}\n" +
"resource_key:${getString(metaData.get("resource_key") as Int)}\n" +
"resource_value:${metaData.get("resource_value")}")
}
}
運行程序,顯示如下:
~~~: value_key:value
resource_key:TestMetaData
resource_value:TestMetaData
參考文章
- 秒懂,Java 註解 (Annotation)你可以這樣學:https://blog.csdn.net/briblue/article/details/73824058
- Kotlin 註解:https://www.jianshu.com/p/ce5b422d93e9
- Android studio 生成帶Kotlin文檔簡易教程:https://blog.csdn.net/qq_30034925/article/details/79445072
- 子類可以繼承到父類上的註解嗎–有結論了:https://elf8848.iteye.com/blog/1621392