13.Android之註解問題

目錄介紹

  • 13.0.0.1 什麼是註解?系統內置的標準註解有哪些?SuppressWarnings用過沒?Android中提供了哪些與線程相關的註解?
  • 13.0.0.2 什麼是apt?apt的難點和優點?什麼是註解處理器?抽象處理器中四個方法有何做用?annotationProcessor和apt區別?
  • 13.0.0.3 註解是怎麼分類的?自定義註解又是怎麼分類的?運行期註解原理是什麼?實際註解案例有哪些?
  • 13.0.0.4 在自定義註解中,Annotation裏面的方法爲什麼不能是private?Annotation裏面的方法參數有哪些?
  • 13.0.0.5 @Inherited是什麼意思?註解是不能夠繼承的,這是爲何?註解的繼承這個概念該如何理解?
  • 13.0.0.6 什麼是依賴注入?依賴注入案例舉例說明,有哪些方式,具有什麼優點?依賴查找和依賴注入有什麼區別?
  • 13.0.0.7 路由框架爲什麼須要依賴注入,不用的話行不行?路由用什麼方式注入,這些注入方式各具何特色,爲什麼選擇註解注入?
  • 13.0.0.8 實際開發中使用到註解有哪些,使用註解替代枚舉?如何經過註解限定傳入的類型?爲什麼說枚舉損耗性能?

註解基礎系列博客

  • 01.Annotation註解詳細介紹
    1.Annotation庫的簡單介紹
    2.@Nullable和@NonNull
    3.資源類型註釋
    4.類型定義註釋
    5.線程註釋
    6.RGB顏色紙註釋
    7.值範圍註釋
    8.權限註釋
    9.重寫函數註釋
    10.返回值註釋
    11.@Keep註釋
    12.@SuppressWarnings註解
    13.其餘
    複製代碼
  • 02.Dagger2深刻分析,待更新
  • 03.註解詳細介紹
    • 什麼是註解,註解分類有哪些?自定義註解分類?運行註解案例展現分析,以一個最簡單的案例理解註解……使用註解替代枚舉,使用註解限定類型
  • 04.APT技術詳解
    • 什麼是apt?理解註解處理器的做用和用途……android-apt被替代?annotationProcessor和apt區別? 什麼是jack編譯方式?
  • 06.自定義annotation註解
    • @Retention的做用?@Target(ElementType.TYPE)的解釋,@Inherited註解能夠被繼承嗎?Annotation裏面的方法爲什麼不能是private?
  • 07.註解之兼容kotlin
    • 後期更新
  • 08.註解之處理器類Processor
    • 處理器類Processor介紹,重要方法,Element的做用,修飾方法的註解和ExecutableElement,瞭解修飾屬性、類成員的註解和VariableElement……
  • 10.註解遇到問題和解決方案
    • 沒法引入javax包下的類庫,成功運行一次,修改代碼後再運行就報錯
  • 11.註解代替枚舉
    • 在作內存優化時,推薦使用註解代替枚舉,由於枚舉佔用的內存更高,如何說明枚舉佔用內存高呢?這是爲何呢?
  • 12.註解練習案例開源代碼
    • 註解學習小案例,比較系統性學習註解而且應用實踐。簡單應用了運行期註解,經過註解實現了setContentView功能;簡單應用了編譯器註解,經過註解實現了防暴力點擊的功能,同時支持設置時間間隔;使用註解替代枚舉;使用註解一步步搭建簡單路由案例。結合相應的博客,在來一些小案例,今後應該對註解有更加深刻的理解……

好消息

  • 博客筆記大彙總【16年3月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請註明出處,謝謝!
  • 連接地址:github.com/yangchong21…
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議或者問題,萬事起於忽微,量變引發質變!

13.0.0.1 什麼是註解?系統內置的標準註解有哪些?SuppressWarnings用過沒?Android中提供了哪些與線程相關的註解?

  • 什麼是註解?
    • Annotation(註解)就是Java提供了一種元程序中的元素關聯任何信息或者任何元數據(metadata)的途徑和方法。基本規則:Annotation(註解)不能影響程序代碼的執行,不管增長,刪除Annotation(註解),代碼都始終如一的執行。
  • 系統內置的標準註解有哪些?
    • @Override表示子類對父類方法的重寫所帶的註解;@Deprecated表示已通過時,不建議使用,可是依然可使用;@SuppressWarnings("all")用來抑制編譯時的警告信息,編譯器在你寫代碼的時候,不免會出現不少的警告,有強迫症的程序猿會感到極其不爽,那麼腫麼辦呢?@SuppressWarnings註解就能夠告訴編譯器,別警告啦,代碼不會有問題的。
  • SuppressWarnings用過沒?
    • @SuppressWarnings註解必定要傳遞一個參數給它,來表示過濾掉哪些類型的警告,筆者添加了」all」表示過濾掉全部類型的警告,很好理解吧!那麼還能夠傳遞什麼參數來過濾警告呢?看看下面的表格你就會知道啦:
    • image
  • Android中提供了哪些與線程相關的註解?
    • @UiThread,一般能夠等同於主線程,標註方法須要在UIThread執行,好比View類就使用這個註解
    • @MainThread 主線程,常常啓動後建立的第一個線程
    • @WorkerThread 工做者線程,通常爲一些後臺的線程,好比AsyncTask裏面的doInBackground就是這樣的
    • @BinderThread 註解方法必需要在BinderThread線程中執行,通常使用較少

13.0.0.2 什麼是apt?apt的難點和優點?什麼是註解處理器?抽象處理器中四個方法有何做用?annotationProcessor和apt區別?

  • 什麼是apt?
    • APT(Annotation Processing Tool的簡稱),能夠在代碼編譯期解析註解,而且生成新的Java文件,減小手動的代碼輸入。如今有不少主流庫都用上了 APT,好比 Dagger2, ButterKnife等
  • apt的難點和優點?
    • 難點:就apt自己來講沒有任何難點可言,難點一在於設計模式和解耦思想的靈活應用,二在與代碼生成的繁瑣,你能夠手動字符串拼接,固然有更高級的玩法用squareup的javapoet,用建造者的模式構建出任何你想要的源代碼
    • 優勢:它的強大之處無需多言,看錶明框架的源碼,你能夠學到不少新姿式。總的一句話:它能夠作任何你不想作的繁雜的工做,它能夠幫你寫任何你不想重複代碼。
  • 什麼是註解處理器?
    • 註解處理器是一個在javac中的,用來編譯時掃描和處理的註解的工具。你能夠爲特定的註解,註冊你本身的註解處理器。
    • 註解處理器能夠生成Java代碼,這些生成的Java代碼會組成 .java 文件,但不能修改已經存在的Java類(即不能向已有的類中添加方法)。而這些生成的Java文件,會同時與其餘普通的手寫Java源代碼一塊兒被javac編譯。
  • 抽象處理器中四個方法有何做用?
    • 每個註解處理器都要繼承於AbstractProcessor,以下所示:
      public class MyProcessor extends AbstractProcessor{
          @Override
          public synchronized void init(ProcessingEnvironment processingEnvironment){
              super.init(processingEnvironment);
          }
      
          @Override
          public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment){
              return false;
          }
      
          @Override
          public Set<String> getSupportedAnnotationTypes(){
              return super.getSupportedAnnotationTypes();
          }
      
          @Override
          public SourceVersion getSupportedSourceVersion(){
              return super.getSupportedSourceVersion();
          }
      }
      複製代碼
    • 這幾個方法以下
      • init(ProcessingEnvironment processingEnvironment): 每個註解處理器類都必須有一個空的構造函數。然而,這裏有一個特殊的init()方法,它會被註解處理工具調用,並輸入ProcessingEnviroment參數。ProcessingEnviroment提供不少有用的工具類Elements,Types和Filer。後面咱們將看到詳細的內容。
      • process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment): 這至關於每一個處理器的主函數main()。你在這裏寫你的掃描、評估和處理註解的代碼,以及生成Java文件。輸入參數RoundEnviroment,可讓你查詢出包含特定註解的被註解元素。後面咱們將看到詳細的內容。
      • getSupportedAnnotationTypes(): 這裏你必須指定,這個註解處理器是註冊給哪一個註解的。注意,它的返回值是一個字符串的集合,包含本處理器想要處理的註解類型的合法全稱。換句話說,你在這裏定義你的註解處理器註冊到哪些註解上。
      • getSupportedSourceVersion(): 用來指定你使用的Java版本。一般這裏返回SourceVersion.latestSupported()。然而,若是你有足夠的理由只支持Java 7的話,你也能夠返回SourceVersion.RELEASE_7。我推薦你使用前者。
  • annotationProcessor和apt區別?
    • Android 官方的 annotationProcessor 同時支持 javac 和 jack 編譯方式,而 android-apt 只支持 javac 方式。固然,目前 android-apt 在 Android Gradle 插件 2.2 版本上面仍然能夠正常運行,若是你沒有想支持 jack 編譯方式的話,能夠繼續使用 android-apt。
  • 什麼是jack編譯方式?
    • Jack (Java Android Compiler Kit)是新的Android 編譯工具,從Android 6.0 開始加入,替換原有的編譯工具,例如javac, ProGuard, jarjar和 dx。它主要負責將java代碼編譯成dex包,並支持代碼壓縮,混淆等。
  • Jack工具的主要優點
    • 徹底開放源碼,源碼均在AOSP中,合做夥伴可貢獻源碼
    • 加快編譯源碼,Jack 提供特殊的配置,減小編譯時間:pre-dexing, 增量編譯和Jack編譯服務器.
    • 支持代碼壓縮,混淆,重打包和multidex,不在使用額外單獨的包,例如ProGuard。

13.0.0.3 註解是怎麼分類的?自定義註解又是怎麼分類的?運行期註解原理是什麼?

  • 註解是怎麼分類的?
    • 標準 Annotation
      • 包括 Override, Deprecated, SuppressWarnings,是java自帶的幾個註解,他們由編譯器來識別,不會進行編譯, 不影響代碼運行,至於他們的含義不是這篇博客的重點,這裏再也不講述。
    • 元 Annotation
      • @Retention, @Target, @Inherited, @Documented,它們是用來定義 Annotation 的 Annotation。也就是當咱們要自定義註解時,須要使用它們。
    • 自定義 Annotation
      • 根據須要,自定義的Annotation。而自定義的方式,下面咱們會講到。
  • 自定義註解又是怎麼分類的?
    • 一樣,自定義的註解也分爲三類,經過元Annotation - @Retention 定義:
    • @Retention(RetentionPolicy.SOURCE)
      • 源碼時註解,通常用來做爲編譯器標記。如Override, Deprecated, SuppressWarnings。
    • @Retention(RetentionPolicy.RUNTIME)
      • 運行時註解,在運行時經過反射去識別的註解。
      • 定義運行時註解,只須要在聲明註解時指定@Retention(RetentionPolicy.RUNTIME)便可。
      • 運行時註解通常和反射機制配合使用,相比編譯時註解性能比較低,但靈活性好,實現起來比較簡答。
    • @Retention(RetentionPolicy.CLASS)
      • 編譯時註解,在編譯時被識別並處理的註解,這是本章重點。
      • 編譯時註解可以自動處理Java源文件並生成更多的源碼、配置文件、腳本或其餘可能想要生成的東西。
  • 運行期註解原理是什麼?
  • 實際註解案例有哪些?
    • 運行時註解:retrofit
    • 編譯時註解:Dagger2, ButterKnife, EventBus3

13.0.0.4 在自定義註解中,Annotation裏面的方法爲什麼不能是private?Annotation裏面的方法參數有哪些?

  • Annotation裏面的方法爲什麼不能是private?
    • 只能用public或默認(default)這兩個訪問權修飾.例如,String value();不能是private;由於它是提供給外部使用的。
    • image
  • Annotation裏面的方法參數有哪些
    • 參數只能使用基本類型byte,short,char,int,long,float,double,boolean八種基本數據類型和 String,Enum,Class,annotations等數據類型,以及這一些類型的數組.例如,String value();這裏的參數類型就爲String; 

13.0.0.5 @Inherited是什麼意思?註解是不能夠繼承的,這是爲何?註解的繼承這個概念該如何理解?

  • @Inherited是什麼意思?
    • 該註解的字面意識是繼承,但你要知道註解是不能夠繼承的。@Inherited是在繼承結構中使用的註解。
    • 若是你的註解是這樣定義的:
      • 當你的註解定義到類A上,此時,有個B類繼承A,且沒使用該註解。可是掃描的時候,會把A類設置的註解,掃描到B類上。
      @Inherited
      @Retention(RetentionPolicy.CLASS)
      @Target(ElementType.TYPE)
      public @interface Test {
      	//...
      }
      複製代碼
  • 註解是不能夠繼承的,這是爲何?
    • 註解不能繼承
  • 註解的繼承這個概念該如何理解?
    • 這裏講的繼承並非經過@Inherited修飾的註解。這個「繼承」是一個註解的使用技巧,使用上的感受相似於依賴倒置,來自於ButterKnife源碼。
    • 這是ButterKnife的OnClick 註解。特殊的地方在於**@OnClick修飾了註解@ListenerClass**,而且設置了一些只屬於@OnClick的屬性。
    • 那這樣的做用是什麼呢?凡是修飾了@OnClick的地方,也就自動修飾了@ListenerClass。相似於@OnClick是@ListenerClass的子類。而ButterKnife有不少的監聽註解@OnItemClick、@OnLongClick等等。這樣在作代碼生成時,不須要再單獨考慮每個監聽註解,只須要處理@ListenerClass就OK。
    @Target(METHOD)
    @Retention(CLASS)
    @ListenerClass(
        targetType = "android.view.View",
    	setter = "setOnClickListener",
    	type = "butterknife.internal.DebouncingOnClickListener",
    	method = @ListenerMethod(
        	name = "doClick",
        	parameters = "android.view.View"
    	)
    )
    public @interface OnClick {
    	/** View IDs to which the method will be bound. */
    	int[] value() default { View.NO_ID };
    }
    複製代碼

13.0.0.6 什麼是依賴注入?依賴注入案例舉例說明,有哪些方式,具有什麼優點?依賴查找和依賴注入有什麼區別?

  • 什麼是依賴注入
    • 在面向對象編程中,常常處理處理的問題就是解耦,程序的耦合性越低代表這個程序的可讀性以及可維護性越高。控制反轉(Inversion of Control或IoC)就是經常使用的面向對象編程的設計原則,使用這個原則咱們能夠下降耦合性。其中依賴注入是控制反轉最經常使用的實現。
  • 依賴注入案例舉例說明,有哪些方式,具有什麼優點?
    • 依賴是程序中常見的現象,好比類Car中用到了GasEnergy類的實例energy,一般的作法就是在Car類中顯式地建立GasEnergy類的實例,並賦值給energy。以下面的代碼
      interface Energy {
            
      }
        
      class GasEnergy implements Energy {
            
      }
        
      class Car {
          Energy energy = new GasEnergy();
      }
      複製代碼
    • 存在問題
      • 類Car承擔了多餘的責任,負責energy對象的建立,這必然存在了嚴重的耦合性。舉一個現實中的例子,一輛汽車使用哪一種能源不是由汽車來決定,而是由汽車製造商(CarMaker)來決定,這是汽車製造商的責任。
      • 可擴展性,假設咱們想修改能源爲電動力,那麼咱們必然要修改Car這個類,明顯不符合開放閉合原則。
      • 不利於單元測試。
    • 有哪些方式解耦
      • 依賴注入是這樣的一種行爲,在類Car中不主動建立GasEnergy的對象,而是經過外部傳入GasEnergy對象形式來設置依賴。 經常使用的依賴注入有以下三種方式
    • 構造器注入
      • 將須要的依賴做爲構造方法的參數傳遞完成依賴注入。
      class Car {
        Energy mEnergy;
        public Car(Energy energy) {
            mEnergy = energy;
        }
      }
      複製代碼
    • Setter方法注入
      • 增長setter方法,參數爲須要注入的依賴亦可完成依賴注入。
      class Car {
        Energy mEnergy;
            
        public void setEnergy(Energy energy) {
            mEnergy  = energy;
        }
      }
      複製代碼
    • 接口注入
      • 接口注入,聞其名不言而喻,就是爲依賴注入建立一套接口,依賴做爲參數傳入,經過調用統一的接口完成對具體實現的依賴注入。
      • 接口注入和setter方法注入相似,不一樣的是接口注入使用了統一的方法來完成注入,而setter方法注入的方法名稱相對比較隨意。
      interface EnergyConsumerInterface {
        public void setEnergy(Energy energy);
      }
        
      class Car implements EnergyConsumerInterface {
        Energy mEnergy;
            
        public void setEnergy(Energy energy) {
            mEnergy  = energy;
        }
      }
      複製代碼
  • 依賴查找和依賴注入
    • 依賴查找和依賴注入同樣屬於控制反轉原則的具體實現,不一樣於依賴注入的被動接受,依賴查找這是主動請求,在須要的時候經過調用框架提供的方法來獲取對象,獲取時須要提供相關的配置文件路徑、key等信息來肯定獲取對象的狀態。

13.0.0.7 路由框架爲什麼須要依賴注入,不用的話行不行?路由用什麼方式注入,這些注入方式各具何特色,爲什麼選擇註解注入?

  • 路由框架爲什麼須要依賴注入,不用的話行不行?
    • 路由的目的就是要實現跳轉,可是你有沒有想過,兩個Activity之間的跳轉確定免不了要傳入一些參數,若是咱們在跳轉後還要經過intent去獲取參數,這樣豈不是很麻煩,若是能夠自動把參數賦給屬性多好啊!
    • 組件化中兩個module之間可能有一些功能並不須要跳轉頁面,如支付模塊要獲取用戶模塊的用戶id,並不須要跳轉頁面,那麼咱們就要持有用戶模塊含有獲取用戶id功能的類的引用,若是咱們在支付模塊建立一個用戶模塊的功能引用,顯然就違背瞭解耦的規則。這兩個問題顯然用依賴注入的方式會更好些,若是你用過ARouter,你會發現ARouter中的服務(IProvider)就是經過依賴注入實現的。
  • 路由用什麼方式注入,這些注入方式各具何特色,爲什麼選擇註解注入?
    • 可使用註解的方式,至於爲啥,接下來我分析一下……
      • 爲何要用註解實現依賴注入,由於咱們用了apt啊,那豈不是天生實現依賴注入的利器。若是你去寫配置文件或構造方法等等,未免太複雜。
    • 沒法經過構造方法注入【構造器注入】
      • 在多組件並行開發過程當中,由於兩個module沒有引用關係,因此就不能經過構造方法傳入要依賴的類,這個時候怎麼辦呢?連對方的引用都得不到,如何進行依賴呢?
    • 不要經過反射方式注入
      • 可能你會想到反射,咱們先pass這個方案,能不用反射就能作好的前提下,咱們最好不要用反射。
    • 不建議經過接口方式注入【也能夠,但如果開源成lib,則不建議】
      • 可能有人會想到,在基類下沉一個接口功能標準,好比在基類庫base模塊定義獲取用戶id的接口,而後在用戶模塊實現接口的方法。那麼當支付模塊須要用到這個功能,就聲明這個接口,而後一行註解經過框架爲你建立實例,這樣用戶模塊只須要提供功能,並不須要關心誰在用這個功能,這樣豈不是大大減少了耦合。
      • 可是有一點,組件化實踐中,有不少個模塊,那豈不是全部的都須要依賴base基類庫,才能使用到接口注入,這樣不易轉接。

13.0.0.8 實際開發中使用到註解有哪些,使用註解替代枚舉?如何經過註解限定傳入的類型?爲什麼說枚舉損耗性能?

  • 實際開發中使用到註解有哪些,使用註解替代枚舉?
    /**
     * 播放模式
     * -1               播放錯誤
     * 0                播放未開始
     * 1                播放準備中
     * 2                播放準備就緒
     * 3                正在播放
     * 4                暫停播放
     * 5                正在緩衝(播放器正在播放時,緩衝區數據不足,進行緩衝,緩衝區數據足夠後恢復播放)
     * 6                正在緩衝(播放器正在播放時,緩衝區數據不足,進行緩衝,此時暫停播放器,繼續緩衝,緩衝區數據足夠後恢復暫停
     * 7                播放完成
     */
    public @interface CurrentState{
        int STATE_ERROR = -1;
        int STATE_IDLE = 0;
        int STATE_PREPARING = 1;
        int STATE_PREPARED = 2;
        int STATE_PLAYING = 3;
        int STATE_PAUSED = 4;
        int STATE_BUFFERING_PLAYING = 5;
        int STATE_BUFFERING_PAUSED = 6;
        int STATE_COMPLETED = 7;
    }
    複製代碼
  • 如何經過註解限定傳入的類型?
    • 代碼以下所示
    • 具體的案例,能夠看我視頻播放器開源庫:github.com/yangchong21…
    • 枚舉最大的做用是提供了類型安全。爲了彌補Android平臺不建議使用枚舉的缺陷,官方推出了兩個註解,IntDef和StringDef,用來提供編譯期的類型檢查。
    • 假若,傳入的值不是IjkPlayerType中的類型,則會致使編譯提醒和警告。
    /**
     * 經過註解限定類型
     * TYPE_IJK                 IjkPlayer,基於IjkPlayer封裝播放器
     * TYPE_NATIVE              MediaPlayer,基於原生自帶的播放器控件
     */
    @Retention(RetentionPolicy.SOURCE)
    public @interface IjkPlayerType {
        int TYPE_IJK = 111;
        int TYPE_NATIVE = 222;
    }
    @IntDef({IjkPlayerType.TYPE_IJK,IjkPlayerType.TYPE_NATIVE})
    public @interface PlayerType{}
    
    
    //使用
    /**
     * 設置播放器類型,必須設置
     * 注意:感謝某人建議,這裏限定了傳入值類型
     * 輸入值:ConstantKeys.IjkPlayerType.TYPE_IJK   或者  ConstantKeys.IjkPlayerType.TYPE_NATIVE
     * @param playerType IjkPlayer or MediaPlayer.
     */
    public void setPlayerType(@ConstantKeys.PlayerType int playerType) {
        mPlayerType = playerType;
    }
    複製代碼

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

03.12.註解練習案例開源代碼

相關文章
相關標籤/搜索