編輯:關於Android編程
今天講下注解吧,現在遇到的用注解的開源庫越來越多,雖然知道怎麼用,但是其原理,怎麼寫都還不清楚。
注解的分類方式有很多:
標准的Annotation:override、deprecated、SuppressWarnings等(這些為Java自帶)。 元Annotation:這些注解是用來修飾注解的,例如Target(說明該注解是用來形容哪些程序元素的,例如Method、Field、Class等)、Retention(指明注解的生命周期,後面會詳述)、Documented(是否保存到Javadoc中)、Inherited(能否被繼承)。 其他Annotation:包括Android已經提供的Annotation(support-annotation包中)和自定義的Annotation。當然還有其他的分類方式,根據注解的Retention來區分,剛剛說到Retention是注解的生命周期,那分為Source(源碼時注解)、Class(編譯時注解)、Runtime(運行時注解)。@Retention(RetentionPolicy.SOURCE)這個注解的意思是讓注解只在java源文件中存在,編譯成.class文件後注解就不存在了。@Retention(RetentionPolicy.CLASS)這個注解的意思是讓注解在java源文件(.java文件)中存在,編譯成.class文件後注解也還存在,被注解類標識的類被類加載器加載到內存中後MyAnnotation注解就不存在了。如果是Runtime的話,則加載到內存中,該注解還存在,下面是詳細解釋:
當在Java源程序上加了一個注解,這個Java源程序要由javac去編譯,javac把java源文件編譯成.class文件,在編譯成class時可能會把Java源程序上的一些注解給去掉,java編譯器(javac)在處理java源程序時,可能會認為這個注解沒有用了,於是就把這個注解去掉了,那麼此時在編譯好的class中就找不到注解了, 這是編譯器編譯java源程序時對注解進行處理的第一種可能情況,假設java編譯器在把java源程序編譯成class時,沒有把java源程序中的注解去掉,那麼此時在編譯好的class中就可以找到注解,當程序使用編譯好的class文件時,需要用類加載器把class文件加載到內存中,class文件中的東西不是字節碼,class文件裡面的東西由類加載器加載到內存中去,類加載器在加載class文件時,會對class文件裡面的東西進行處理,如安全檢查,處理完以後得到的最終在內存中的二進制的東西才是字節碼,類加載器在把class文件加載到內存中時也有轉換,轉換時是否把class文件中的注解保留下來,這也有說法,所以說一個注解的生命周期有三個階段:java源文件是一個階段,class文件是一個階段,內存中的字節碼是一個階段,javac把java源文件編譯成.class文件時,有可能去掉裡面的注解,類加載器把.class文件加載到內存時也有可能去掉裡面的注解,因此在自定義注解時就可以使用Retention注解指明自定義注解的生命周期,自定義注解的生命周期是在RetentionPolicy.SOURCE階段(java源文件階段),還是在RetentionPolicy.CLASS階段(class文件階段),或者是在RetentionPolicy.RUNTIME階段(內存中的字節碼運行時階段),根據JDK提供的API可以知道默認是在RetentionPolicy.CLASS階段 (JDK的API寫到:the retention policy defaults to RetentionPolicy.CLASS.)
如果是Source和Class的話,即使引起Android Studio報錯,也不會影響運行,依舊可以運行。舉個簡單的例子,EventBus庫(最新的版本onEvent函數使用了@Subscribe注解),我們知道其原理是當對象注冊了EventBus後,EventBus會記錄該類onEvent方法,當檢測到有消息post出來後,會調用有subscribe注解的方法,試想一下,如果這歌subscribe是source或者class的話,那麼加載到內存的時候,就已經沒有了,那麼Eventbus找不到任何方法來處理事件,所以subscribe一定是運行時,看下代碼:
package org.greenrobot.eventbus; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { ThreadMode threadMode() default ThreadMode.POSTING; /** * If true, delivers the most recent sticky event (posted with * {@link EventBus#postSticky(Object)}) to this subscriber (if event available). */ boolean sticky() default false; /** Subscriber priority to influence the order of event delivery. * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of * delivery among subscribers with different {@link ThreadMode}s! */ int priority() default 0; }
果然沒錯,驗證了我們的猜想。
安卓中的注解分為八大類:
Nullness注解:@Nullable、@NoNull(為方法的形參加上這個,可以減少判空的代碼) 資源類型注解:以Res結尾,例如@StringRes、@ColorRes、@IdRes等 權限注解:@requestPermission,這個注解在PermissionDispatcher這個開源庫中用到 CallSuper注解:被@CallSuper修飾的方法,一定要調用super方法 枚舉注解:@IntDef和@StringDef,具體用法可以看Toast下Duration 線程注解:@MainThread、@WorkThread等,被這些注解修飾的方法只能在該線程內調用 變量限制注解:@Size、@IntRange、@FloatRange等 結果檢查注解:@CheckResult,被該注解修飾的方法,需要對方法的返回值進行處理。例如Context.checkPermission(@NonNull String permission, int pid, int uid)方法,防止別人誤解該方法就已經算是請求權限了,調用該方法一定要判斷返回值,權限是否被賦予,如果不判斷,調用該方法無用。如果只是簡單調該函數,並未判斷返回值,則會提示是否使用另外一個函數。@CheckResult(suggest="#enforcePermission(String,int,int,String)") @PackageManager.PermissionResult public abstract int checkPermission(@NonNull String permission, int pid, int uid);
講到現在都還沒舉一個具體的例子,那就讓我們來看看常見的ColorRes:
@Documented @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) public @interface ColorRes { }
解釋下上述代碼:該注解將會保存到Javadoc中;該注解是編譯時注解,在class文件中還會存在,但是在內存中就沒了;該注解用來修飾方法、參數、屬性、局部變量。
還有一些比較好用的注解,例如@Keep,該注解表示被該注解修飾的元素將不會被混淆。
下面講下編譯時解析:由apt(Annotation Processing Tool)自動解析
自定義類繼承自AbstractProcessor 重寫該類的Process函數今天給大家帶來的是僅僅使用一個TextView實現一個高仿京東、淘寶、唯品會等各種電商APP的活動倒計時。最近公司一直加班也沒來得及時間去整理,今天難得休息想把這個分享給
Android通訊錄開發之實現全選、反選功能 2014年1月15日 實現全選、反選不是什麼難的事情,就只是用另外一個數據結構來存儲被選中的狀態,通過刷新列表
我們在做Android應用尤其是商業應用的時候,很多時候都需要後期版本升級,如果我們的數據庫文件非常大,比如游戲之類的,這時候就不應該每次版本更新都去重新復制數據庫。將數
/** WebServcie 概念 多個系統數據交換: 跨平台語言的相互通信; 如:java 的客戶端 和dotnet的服務器端的接口調用: 得到接口和方法 : 基於