日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android Annotation注解详解

發布時間:2024/9/30 Android 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android Annotation注解详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/119874435
本文出自【趙彥軍的博客】

文章目錄

  • Java注解
  • 元注解說明
    • @Retention
    • @Target
  • 通過反射獲取注解信息
  • 方法使用注解
  • 方法的參數使用注解
  • Android 自帶的注解
    • @LayoutRes
    • @MainThread
    • @IntDef
    • @StringRes
    • @ColorInt
    • @IdRes
    • @DrawableRes
    • @NotNull
    • @Nullable
    • @Keep
    • @RequiresPermission
    • @Deprecated
    • CallSuper
    • @IntRange
    • @FloatRange
    • @CheckResult
    • @size
  • 總結:

Java注解

Java 注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制。
Java 語言中的類、方法、變量、參數和包等都可以被標注。和 Javadoc 不同,Java 標注可以通過反射獲取標注內容。在編譯器生成類文件時,標注可以被嵌入到字節碼中。Java 虛擬機可以保留標注內容,在運行時可以獲取到標注內容 。 當然它也支持自定義 Java 標注。

Java內置了多種標準注解,其定義在java.lang中。

  • Override 表示當前的方法定義將覆蓋父類中的方法

  • Deprecated 被此注解標記的元素表示被廢棄

  • SuppressWarnings 關閉不當的編譯器警告信息

    在上面我們看到了 @Target 、@Retention , 這些也是注解,我們暫且可以稱之為注解的注解。

元注解說明

@Retention

表示需要在什么級別保留該注解信息

  • RetentionPolicy.SOURCE:只在源代碼中保留 一般都是用來增加代碼的理解性或者幫助代碼檢查之類的,比如我們的Override
  • RetentionPolicy.CLASS : 默認的選擇,能把注解保留到編譯后的字節碼class文件中,僅僅到字節碼文件中,運行時是無法得到的
  • RetentionPolicy.RUNTIME :注解不僅能保留到class字節碼文件中,還能在運行通過反射獲取到,這也是我們最常用的

@Target

表示該注解可以用在什么地方

  • ElementType.FIELD : 能修飾成員變量
  • ElementType.METHOD:能修飾方法
  • ElementType.CONSTRUCTOR : 能修飾構造器
  • ElementType.PACKAGE : 能修飾包
  • ElementType.PARAMETER : 能修飾方法參數
  • ElementType.TYPE : 能修飾類、接口或枚舉類型
  • ElementType.ANNOTATION_TYPE : 能修飾注解
  • ElementType.LOCAL_VARIABLE:能修飾局部變量
  • ElementType.MODULE :

通過反射獲取注解信息

Annotation是被動的元數據,永遠不會有主動行為,所以我們需要通過使用反射,才能讓我們的注解產生意義。

首先我們定義一個端口注解:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Port {String value() default "8080"; }

RetentionPolicy.RUNTIME 在運行時保留注解,作用于是字段。

反射獲取字段

object BindPort {/*** 綁定的目的* 1、通過反射獲取注解的值* 2、通過反射給目標設置值*/fun bind(activity: Activity) {//獲取所有字段val fields = activity.javaClass.declaredFieldsfields.forEach { field ->//獲取所有注解val ans = field.annotationsans.forEach {if (it is Port) {//獲取注解值var port = it.value//通過屬性反射給屬性注入值field.isAccessible = truefield.set(activity, port)}}}}}

上面代碼的邏輯很簡單:

首先遍歷循環所有的屬性,如果當前屬性被指定的注解所修飾,那么就將當前屬性的值修改為注解中成員變量的值。

這里setAccessible(true)的使用時因為,我們在聲明port變量時,其類型為private,為了確保可以訪問這個變量,防止程序出現異常。

注解的使用:

class MainActivity : AppCompatActivity() {@Portvar port: String? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)Log.d("yy--", "反射前:$port")BindPort.bind(this)Log.d("yy--", "反射后:$port")} }

運行結果:

com.example.myapplication D/yy--: 反射前:null com.example.myapplication D/yy--: 反射后:8080

當然,我們也可以在注解時,自定義我們的屬性值,比如:

class MainActivity : AppCompatActivity() {@Port("8090")var port: String? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)Log.d("yy--", "反射前:$port")BindPort.bind(this)Log.d("yy--", "反射后:$port")} }

運行結果:

com.example.myapplication D/yy--: 反射前:null com.example.myapplication D/yy--: 反射后:8090

方法使用注解

我們來模擬一個http請求, 定義一個 Request 注解

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Request {Method value();enum Method {GET, POST} }

注解的方式簡單,@Retention(RetentionPolicy.RUNTIME) 在運行時保留,@Target(ElementType.METHOD) 作用域在方法上。

下面我們編寫,反射的方法,定義 HttpBind

object HttpBind {/*** 綁定的目的* 1、通過反射獲取注解的值* 2、通過反射給目標設置值*/fun bind(activity: Activity) {//獲取所有方法val methods = activity.javaClass.methodsmethods.forEach { method ->//獲取所有注解val ans = method.annotationsans.forEach {if (it is Request) {//獲取注解值var requestMethod = it.valueif (requestMethod == Request.Method.GET) {//發起get請求method.invoke(activity, requestMethod.name)} else if (requestMethod == Request.Method.POST) {//發起post請求method.invoke(activity, requestMethod.name)}}}}}}

注解使用

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)//使用反射,解析注解HttpBind.bind(this)}@Request(Request.Method.GET)fun http(method: String) {Log.d("yy--", "網絡請求:$method")} }

我們運行一下,看看效果

D/yy--: 網絡請求:GET

方法的參數使用注解

先定義一個參數注解 Path

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER) public @interface Path {String value() default ""; }

這個注解也很簡單,在運行時保留,作用域在參數上

下面我們使用反射來獲取參數注解的值

object HttpBind {/*** 綁定的目的* 1、通過反射獲取注解的值* 2、通過反射給目標設置值/反射調用方法*/fun bind(activity: Activity) {//獲取所有方法val methods = activity.javaClass.methodsmethods.forEach { method ->//獲取所有參數注解,一個方法有多個參數,一個參數有多個注解,所以類型是二維數組val ans = method.parameterAnnotationsans.forEach { annotationArray ->annotationArray.forEach { parameterAnnotation ->if (parameterAnnotation is Path) {var parameter = parameterAnnotation.valuemethod.invoke(activity, parameter)}}}}} }

使用如下:

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)HttpBind.bind(this)}fun http(@Path("zhaoyanjun") user: String) {Log.d("yy--", "參數注解:$user")} }

運行結果如下:

D/yy--: 參數注解:zhaoyanjun

Android 自帶的注解

Android 系統已經幫我內置了很多有用的注解,在我們的開發過程中可以很方便的使用。

implementation 'androidx.annotation:annotation:1.2.0'

示例如下

我們舉幾個常見的例子:

資源限制類

  • @AnimatorRes :animator資源類型
  • @AnimRes:anim資源類型
  • @AnyRes:任意資源類型
  • @ArrayRes:array資源類型
  • @AttrRes:attr資源類型
  • @BoolRes:boolean資源類型
  • @ColorRes:color資源類型
  • @DimenRes:dimen資源類型。
  • @DrawableRes:drawable資源類型。
  • @FractionRes:fraction資源類型
  • @IdRes:id資源類型
  • @IntegerRes:integer資源類型
  • @InterpolatorRes:interpolator資源類型
  • @LayoutRes:layout資源類型
  • @MenuRes:menu資源類型
  • @PluralsRes:plurals資源類型
  • @RawRes:raw資源類型
  • @StringRes:string資源類型
  • @StyleableRes:styleable資源類型
  • @StyleRes:style資源類型
  • @TransitionRes:transition資源類型
  • @XmlRes:xml資源類型

線程限制類

Thread annotations 線程執行限制類:用于限制方法或者類必須在指定的線程執行。如果方法代碼運行線程和標注的線程不一致,則會導致警告。

  • @AnyThread
  • @BinderThread
  • @MainThread
  • @UiThread
  • @WorkerThread

數值限制類

Value Constraint Annotations 類型范圍限制類:用于限制標注值的值范圍

  • @FloatRang
  • @IntRange

@LayoutRes

這個是layout 資源類型,我們看一下 Activity 的 setContentView 源碼:

@Overridepublic void setContentView(@LayoutRes int layoutResID) {getDelegate().setContentView(layoutResID);}

本質上,layoutResID 是一個 int 類型,如果不做限定的話,可以傳入任意整形,但是有 @LayoutRes 注解的限制,值只能傳入 R.layou.xx , 如果傳入其他的類型就會報錯。舉例如下:

需要注意的是,報錯只是編譯器的檢查出錯,提醒開發者改正錯誤用法,提前規避風險,并不影響編譯運行

@MainThread

限定方法執行的線程,如果方法代碼運行線程和標注的線程不一致,不會報錯,更多是起一個提醒作用

@MainThreadfun run() {}

@IntDef

IntDef 的源碼如下:

@Retention(SOURCE) @Target({ANNOTATION_TYPE}) public @interface IntDef {/** Defines the allowed constants for this element */int[] value() default {};/** Defines whether the constants can be used as a flag, or just as an enum (the default) */boolean flag() default false;/*** Whether any other values are allowed. Normally this is* not the case, but this allows you to specify a set of* expected constants, which helps code completion in the IDE* and documentation generation and so on, but without* flagging compilation warnings if other values are specified.*/boolean open() default false; }

可以看到 Target 是 ANNOTATION_TYPE 說明 IntDef 是作用在注解上的。

還有一個 value 是 int數組。

下面我們定義一個注解 MOBILE_TYPE , 并且用 IntDef 修飾,如下:

import androidx.annotation.IntDef; import kotlin.annotation.AnnotationRetention; import kotlin.annotation.Retention;/*** @author : zhaoyanjun* @time : 2021/7/29* @desc :*/ public class Util {public static final int TYPE_MI = 1;public static final int TYPE_MEIZU = 2;public static final int TYPE_HUAWEI = 3;@Retention(AnnotationRetention.SOURCE)@IntDef({TYPE_MI, TYPE_MEIZU, TYPE_HUAWEI})public @interface MOBILE_TYPE {}//使用public void doSomething(@MOBILE_TYPE int mobile){} }

使用方法很簡單,首先定義你需要的常量,然后用 @IntDef 包住這些常量,這樣別人在使用你的方法時如果輸入的值不在枚舉的范圍內,編譯器就會給出提示了。

同理,@StringDef 也是同樣的用法

import androidx.annotation.StringDef;import kotlin.annotation.AnnotationRetention; import kotlin.annotation.Retention;/*** @author : zhaoyanjun* @time : 2021/7/29* @desc :*/ public class Util {public static final String TYPE_HD = "720p";public static final String TYPE_SHD = "1080p";public static final String TYPE_FHD = "4k";@Retention(AnnotationRetention.SOURCE.SOURCE)@StringDef({TYPE_HD, TYPE_SHD, TYPE_FHD})public @interface DISPLAY_TYPE {}public void doSomething(@DISPLAY_TYPE String display) {} }

還有一個 @LongDef也是同樣的用法,這里就不舉例了。

總結 :IntDef @StringDef @LongDef 可以限制變量的類型,可以代替枚舉類型

我們來看一個系統例子,Toast 的源碼:

public static Toast makeText(Context context, CharSequence text, @Duration int duration) {return makeText(context, null, text, duration);}

@Duration 是一個自定義的注解:

/** @hide */@IntDef(prefix = { "LENGTH_" }, value = {LENGTH_SHORT,LENGTH_LONG})@Retention(RetentionPolicy.SOURCE)public @interface Duration {}

看到這里,我們已經很熟悉了,也是用的 @IntDef 注解,除此之外,我們還發現了一個細節 ,在 android 的注解包里,@IntDef 帶有 prefix 屬性,但是在 androidx 的注解包里卻沒有。

下面貼一下兩個的源碼,大家看看:

//android 的源碼,包名:android.annotation @Retention(SOURCE) @Target({ANNOTATION_TYPE}) public @interface IntDef {/** Defines the constant prefix for this element */String[] prefix() default {};/** Defines the constant suffix for this element */String[] suffix() default {};/** Defines the allowed constants for this element */int[] value() default {};/** Defines whether the constants can be used as a flag, or just as an enum (the default) */boolean flag() default false; }//androidx 的源碼,包名:androidx.annotation @Retention(SOURCE) @Target({ANNOTATION_TYPE}) public @interface IntDef {/** Defines the allowed constants for this element */int[] value() default {};/** Defines whether the constants can be used as a flag, or just as an enum (the default) */boolean flag() default false;/*** Whether any other values are allowed. Normally this is* not the case, but this allows you to specify a set of* expected constants, which helps code completion in the IDE* and documentation generation and so on, but without* flagging compilation warnings if other values are specified.*/boolean open() default false; }

這是兩個包下面的 IntDef 的差異,我想知道的是 prefix 有什么用?
其實也很簡單,規范 value 數組元素的命名前綴。

@StringRes

這個其實很好理解,限制字符的來源,必須是 R.string.xx , StringRes 源碼如下:

/*** Denotes that an integer parameter, field or method return value is expected* to be a String resource reference (e.g. {@code android.R.string.ok}).*/ @Documented @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) public @interface StringRes { }

舉個系統的例子:

public static Toast makeText(Context context, @StringRes int resId, @Duration int duration)throws Resources.NotFoundException {return makeText(context, context.getResources().getText(resId), duration);}

@ColorInt

限定顏色的取值范圍 R.color.xx , 源碼如下:

/*** Denotes that the annotated element represents a packed color* int, {@code AARRGGBB}. If applied to an int array, every element* in the array represents a color integer.* <p>* Example:* <pre>{@code* public abstract void setTextColor(@ColorInt int color);* }</pre>*/ @Documented @Retention(CLASS) @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD}) public @interface ColorInt { }

舉個系統的例子:

public void setTextColor(@ColorInt int color) {mTextColor = ColorStateList.valueOf(color);updateTextColors(); }

@IdRes

限制id 的取值范圍:R.id.xx , 源碼碼如下:

/*** Denotes that an integer parameter, field or method return value is expected* to be an id resource reference (e.g. {@code android.R.id.copy}).*/ @Documented @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) public @interface IdRes { }

舉個系統的例子:

@Overridepublic <T extends View> T findViewById(@IdRes int id) {return getDelegate().findViewById(id);}

@DrawableRes

限定資源的取值類型是一個 drawable 類型:android.R.attr.alertDialogIcon

@Documented @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) public @interface DrawableRes { }

舉個系統的例子:

public void setImageResource(@DrawableRes int resId) {... }

@NotNull

定義個變量不能為空, 如果真的為空,不會影響編譯,只是編譯器會報錯,提醒開發者注意。

public class Util {//參數不能為nullpublic void run(@NotNull String name) {} }

測試:

可以看到編譯器會自動檢查

@Nullable

限定一個參數,一個方法的返回值可以為null

public class Util {@Nullablepublic String aa() {return null;} }

使用:

編譯器會自動提示

@Keep

哪里不想被混淆就注解哪里。

@Keep public class Test { }public class TestA { }

開始混淆打包,查看混淆后的結果:


我們發現TestA不見了而Test保留了下來,說明我們的配置起作用了,下面我們在Test 類中增加點內容看看混淆后會變成什么樣子,修改后的類內容如下:

@Keep public class Test {int age = 20;protected String sex = "m";public String name = "CodingMaster"; }

查看混淆后的結果:


不幸的是雖然類名保留下來了,但是里面的內容卻被混淆了,如果我們想把name變量不被混淆怎么辦呢?

我們繼續修改Test類,這次我們多加了點東西,會在后面用到,內容如下:

@Keep public class Test {int age = 20;@Keepprotected String sex = "m";@Keeppublic String name = "CodingMaster";public int getAge() {return age;}public void setAge(int age) {this.age = age;}private void cry(){} }

重新混淆查看結果:

我們的name變量被成功的保留了,同理如何保留被sex變量呢?這里就不買關子了,直接給出答案,為sex添加@Keep注解就可以了,持懷疑態度的同學👨?🎓可以自己去驗證。
細心的同學可能已經發現,Test類里面的方法都被混淆了,怎樣指定某個方法不被混淆呢?

然后為cry()方法添加@Keep注解,重新混淆查看結果:


有沒有很簡單的感覺呢?哪里不混淆@Keep哪里,再也不用為混淆頭疼了!

@RequiresPermission

限定字段,方法需要某個權限,如果沒有,編譯器會提醒

public class Util1 {@RequiresPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) public String run() {return null; }

使用:

看到編譯器報錯,我們點擊一下 Add permission check , 編譯器會自動幫我們補全代碼

class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)if (ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {// TODO: Consider calling// ActivityCompat#requestPermissions// here to request the missing permissions, and then overriding// public void onRequestPermissionsResult(int requestCode, String[] permissions,// int[] grantResults)// to handle the case where the user grants the permission. See the documentation// for ActivityCompat#requestPermissions for more details.return}Util1().run()}}
  • @RequiresPermission(permision)
  • @RequiresPermission(allOf={permision1,perminsion2})
  • @RequiresPermission(anyOf={permision1,perminsion2})

@Deprecated

標記某個字段或者方法過時,舉例:

CallSuper

子類重寫某個方法時,要求調用super,可以使用該注解

@IntRange

//限定只能傳1-4fun run(@IntRange(from = 1, to = 4) num: Int) {}

使用:

@FloatRange

用法上和 IntRange 一樣,

//限定只能傳1-4fun run(@FloatRange(from = 1.0, to = 4.0) num: Float) {}

源碼如下:

其中,fromInclusive 是否包含 from ,toInclusive 是否包含 to , 其實就是左包含,右包含的意思。

@CheckResult

假設你定義了一個方法返回一個值,你期望調用者用這個值做些事情,那么你可以使用@CheckResult注解標注這個方法,強制用戶定義一個相應的返回值,使用它!

首先定義 CallSuperT ,定義一個retrunI方法返回一個int類型

public class CallSuperT {@CheckResultpublic int retrunI(){return 1;} }

正確調用:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);CallSuperT callSuperT = new CallSuperT();int returns = callSuperT.retrunI();} }

如果這里去掉返回類型的定義對象:int returns則會拋出異常

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);CallSuperT callSuperT = new CallSuperT();callSuperT.retrunI();} }

錯誤提示結果:

@size

定義長度大小,可選擇最小和最大長度使用

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);testDo("");testDo("111");testDo("1");}private void testDo(@Size(min = 1,max = 2)String s){Log.e("tag","-------->"+s);} }

錯誤提示結果:

這里size定了一個最小和最大長度,所以只有testDo(“1”)符合條件,其他調用都拋出了異常

總結:

注解的作用:

  • 提高我們的開發效率
  • 更早的發現程序的問題或者錯誤
  • 更好的增加代碼的描述能力
  • 更加利于我們的一些規范約束
  • 提供解決問題的更優解

總結

以上是生活随笔為你收集整理的Android Annotation注解详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。