自定义依赖注解无效_关于Apt注解实践与总结【包含20篇博客】
超詳細!安卓巴士開發者大會嘉賓及主題介紹
目錄介紹
00.注解系列博客匯總
01.什么是apt
02.annotationProcessor和apt區別
03.項目目錄結構
04.該案例作用
05.使用說明
06.編譯期注解生成代碼[點擊事件案例]
07.運行期注解案例[setContentView案例]
08.使用注解替代枚舉
09.使用注解搭建路由[綜合案例]
9.1 搭建路由條件
9.2 通過注解去實現路由跳轉
9.3 自定義路由Processor編譯器
9.4 利用apt生成路由映射文件
9.5 路由框架的設計
9.6 路由參數的傳遞和接收
9.7 為何需要依賴注入
9.8 Activity屬性注入
9.9 路由開源庫的使用
關于apt實踐與總結開源庫地址:
https://github.com/yangchong211/YCApt
文中含有眾多超鏈,可點擊左下角“閱讀原文”相應位置查看。
00.注解系列博客匯總
0.1 注解基礎系列博客
01.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功能;簡單應用了編譯器注解,通過注解實現了防暴力點擊的功能,同時支持設置時間間隔;使用注解替代枚舉;使用注解一步步搭建簡單路由案例。結合相應的博客,在來一些小案例,從此應該對注解有更加深入的理解……
0.2 注解系列博客問題答疑
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.什么是apt
什么是apt
APT,就是Annotation Processing Tool的簡稱,就是可以在代碼編譯期間對注解進行處理,并且生成Java文件,減少手動的代碼輸入。注解我們平時用到的比較多的可能會是運行時注解,比如大名鼎鼎的retrofit就是用運行時注解,通過動態代理來生成網絡請求。編譯時注解平時開發中可能會涉及的比較少,但并不是說不常用,比如我們經常用的輪子Dagger2, ButterKnife, EventBus3 都在用,所以要緊跟潮流來看看APT技術的來龍去脈。
編譯時注解。
也有人叫它代碼生成,其實他們還是有些區別的,在編譯時對注解做處理,通過注解,獲取必要信息,在項目中生成代碼,運行時調用,和直接運行手寫代碼沒有任何區別。而更準確的叫法:APT - Annotation Processing Tool
大概原理
Java API 已經提供了掃描源碼并解析注解的框架,開發者可以通過繼承 AbstractProcessor 類來實現自己的注解解析邏輯。APT 的原理就是在注解了某些代碼元素(如字段、函數、類等)后,在編譯時編譯器會檢查 AbstractProcessor 的子類,并且自動調用其 process() 方法,然后將添加了指定注解的所有代碼元素作為參數傳遞給該方法,開發者再根據注解元素在編譯期輸出對應的 Java 代碼
02.annotationProcessor和apt區別
annotationProcessor和apt區別
Android 官方的 annotationProcessor 同時支持 javac 和 jack 編譯方式,而 android-apt 只支持 javac 方式。當然,目前 android-apt 在 Android Gradle 插件 2.2 版本上面仍然可以正常運行,如果你沒有想支持 jack 編譯方式的話,可以繼續使用 android-apt。
目前比如一些常用框架dagger2,butterKnife,ARouter等,都支持annotationProcessor
什么是jack編譯方式?
Jack (Java Android Compiler Kit)是新的Android 編譯工具,從Android 6.0 開始加入,替換原有的編譯工具,例如javac, ProGuard, jarjar和 dx。它主要負責將java代碼編譯成dex包,并支持代碼壓縮,混淆等。
Jack工具的主要優勢
完全開放源碼,源碼均在AOSP中,合作伙伴可貢獻源碼
加快編譯源碼,Jack 提供特殊的配置,減少編譯時間:pre-dexing, 增量編譯和Jack編譯服務器.
支持代碼壓縮,混淆,重打包和multidex,不在使用額外單獨的包,例如ProGuard。
03.項目目錄結構
項目目錄結構如圖:
app:Demo
AptAnnotation:java Library主要放一些項目中需要用到的自定義注解及相關代碼
AptApi:Android Library. 是我們真正對外發布并交由第三方使用的庫,它引用了apt-jar包
AptCompiler:java Library主要是應用apt技術處理注解,生成相關代碼或者相關源文件,是核心所在。
04.該案例作用
前期僅僅是為了學習,同時先讓demo運行起來,雖然網上很多講解apt的博客寫的很詳細,但是還是有必要結合實際案例練習一下。
使用apt實現點擊事件【編譯期注解生成代碼】
在一定時間內,按鈕點擊事件只能執行一次。未到指定時間,不執行點擊事件。
使用apt實現setContentView功能【運行期注解案例】
使用簡單的注解,便可以設置布局,等效于setContentView(R.layout.activity_main)
使用apt實現路由【綜合型案例】
比較全面的介紹從零起步,一步一步封裝簡易的路由開源庫。一共用10篇博客記錄了大部分的過程,想要更加深入了解,歡迎clone該項目。
05.使用說明
如下所示
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.activity_main);
????????//初始化OnceClick,并設置點擊事件間隔是2秒
????????OnceInit.once(this,2000);
????}
????@OnceClick(R.id.tv_1)
????public?void?Click1(){
????????Log.d("tag--------------------","tv_1");
????}
06.編譯期注解生成代碼
如下所示,在app/build/generated/source/apt/debug/MainActivity_Once_Proxy目錄下
????//?更多內容:https://github.com/yangchong211
????package?com.ycbjie.ycapt;
????import?android.view.View;
????import?com.ycbjie.api.Finder;
????import?com.ycbjie.api.AbstractInjector;
????public?class?MainActivity$$_Once_Proxy<T?extends?MainActivity>?implements?AbstractInjector<T>?{
????????public?long?intervalTime;
????????@Override
????????public?void?setIntervalTime(long?time)?{
????????????intervalTime?=?time;
????????}
????????@Override
????????public?void?inject(final?Finder?finder,?final?T?target,?Object?source)?{
????????????View?view;
????????????view?=?finder.findViewById(source,?2131165325);
????????????if(view?!=?null){
????????????????view.setOnClickListener(new?View.OnClickListener()?{
????????????????long?time?=?0L;
????????????????@Override
????????????????public?void?onClick(View?v)?{
????????????????????long?temp?=?System.currentTimeMillis();
????????????????????if?(temp?-?time?>=?intervalTime)?{
????????????????????????time?=?temp;
????????????????????????target.Click1();
????????????????????}
????????????????}});
????????????}
????????????view?=?finder.findViewById(source,?2131165326);
????????????if(view?!=?null){
????????????????view.setOnClickListener(new?View.OnClickListener()?{
????????????????long?time?=?0L;
????????????????@Override
????????????????public?void?onClick(View?v)?{
????????????????????long?temp?=?System.currentTimeMillis();
????????????????????if?(temp?-?time?>=?intervalTime)?{
????????????????????????time?=?temp;
????????????????????????target.Click2(v);
????????????????????}
????????????????}});
????????????}
??????}
????}
07.運行期注解案例
首先先定義自定義注解
????@Retention(RetentionPolicy.RUNTIME)
????//@Target用來表示這個注解可以使用在哪些地方。比如:類、方法、屬性、接口等等。
????//這里ElementType.TYPE?表示這個注解可以用來修飾:Class,?interface?or?enum?declaration。
????//當你用ContentView修飾一個方法時,編譯器會提示錯誤。
????@Target({ElementType.TYPE})
????//這里的interface并不是說ContentView是一個接口。
????//就像申明類用關鍵字class。申明枚舉用enum。申明注解用的就是@interface。
????public?@interface?ContentView?{
????????//返回值表示這個注解里可以存放什么類型值。
????????int?value();
????}
然后需要在activity中做注解解析
????public?class?ContentActivity?extends?AppCompatActivity?{
????????@Override
????????protected?void?onCreate(Bundle?savedInstanceState)?{
????????????super.onCreate(savedInstanceState);
????????????//注解解析
????????????//遍歷所有的子類
????????????for?(Class?c?=?this.getClass();?c?!=?Context.class;?c?=?c.getSuperclass())?{
????????????????assert?c?!=?null;
????????????????//找到修飾了注解ContentView的類
????????????????ContentView?annotation?=?(ContentView)?c.getAnnotation(ContentView.class);
????????????????if?(annotation?!=?null)?{
????????????????????try?{
????????????????????????//獲取ContentView的屬性值
????????????????????????int?value?=?annotation.value();
????????????????????????//調用setContentView方法設置view
????????????????????????this.setContentView(value);
????????????????????}?catch?(RuntimeException?e)?{
????????????????????????e.printStackTrace();
????????????????????}
????????????????????return;
????????????????}
????????????}
????????}
????}
關于如何使用,注意你寫的Activity需要實現ContentActivity,才能讓注解生效
????public?class?FourActivity?extends?ContentActivity?{
????????@Override
????????protected?void?onCreate(Bundle?savedInstanceState)?{
????????????super.onCreate(savedInstanceState);
????????????findViewById(R.id.tv_1).setOnClickListener(new?View.OnClickListener()?{
????????????????@Override
????????????????public?void?onClick(View?v)?{
????????????????????Toast.makeText(FourActivity.this,"運行期注解",Toast.LENGTH_SHORT).show();
????????????????}
????????????});
????????}
????}
09.使用注解搭建路由[綜合案例]
9.1 ARouter路由解析
比較詳細地分析了阿里路由庫
9.1 搭建路由條件
為何需要路由?實現路由方式有哪些,這些方式各有何優缺點?使用注解實現路由需要具備的條件以及簡單原理分析……
9.2 通過注解去實現路由跳轉
自定義Router注解,Router注解里有path和group,這便是仿照ARouter對路由進行分組。然后看看注解生成的代碼,手寫路由跳轉代碼。
9.3 自定義路由Processor編譯器
Processor介紹,重要方法,Element的作用,修飾方法的注解和ExecutableElement
9.4 利用apt生成路由映射文件
在Activity類上加上@Router注解之后,便可通過apt來生成對應的路由表,那么究竟是如何生成的代碼呢?
在組件化開發中,有多個module,為何要在build.gradle配置moduleName,又是如何通過代碼拿到module名稱?
process處理方法如何生成代碼的,又是如何寫入具體的路徑,寫入文件的?
看完這篇文章,應該就能夠理解上面這些問題呢!
9.5 路由框架的設計和初始化
編譯期是在你的項目編譯的時候,這個時候還沒有開始打包,也就是你沒有生成apk呢!路由框架在這個時期根據注解去掃描所有文件,然后生成路由映射文件。這些文件都會統一打包到apk里,app運行時期做的東西也不少,但總而言之都是對映射信息的處理,如執行執行路由跳轉等。那么如何設計框架呢?
生成的注解代碼,又是如何把這些路由映射關系拿到手,或者說在什么時候拿到手比較合適?為何注解需要進行初始化操作?
如何得到得到路由表的類名,如何得到所有的routerAddress---activityClass映射關系?
[9.6 路由框架設計注意要點]()
需要注意哪些要點?
9.7 為何需要依賴注入
有哪些注入的方式可以解耦,你能想到多少?路由框架為何需要依賴注入?路由為何用注解進行依賴注入,而不是用反射方式注入,或者通過構造方法注入,或者通過接口方式注入?
9.8 Activity屬性注入
在跳轉頁面時,如何傳遞intent參數,或者如何實現跳轉回調處理邏輯?
9.9 路由開源庫的使用
不帶參數直接跳轉
????????public?class?SixActivity?extends?AppCompatActivity?{
????????????@Override
????????????protected?void?onCreate(@Nullable?Bundle?savedInstanceState)?{
????????????????super.onCreate(savedInstanceState);
????????????????setContentView(R.layout.activity_six);
????????????}
????????}
????????ARouter.getsInstance().build(Path.six)
????????????????????.navigation(MainActivity.this,?new?NavigationCallback()?{
????????????????@Override
????????????????public?void?onFound(Postcard?postcard)?{
????????????????????Log.e("NavigationCallback","找到跳轉頁面");
????????????????}
????????????????@Override
????????????????public?void?onLost(Postcard?postcard)?{
????????????????????Log.e("NavigationCallback","未找到");
????????????????}
????????????????@Override
????????????????public?void?onArrival(Postcard?postcard)?{
????????????????????Log.e("NavigationCallback","成功跳轉");
????????????????}
????????????});
帶參數跳轉
????????public?class?FiveActivity?extends?AppCompatActivity?{
????????????@Extra
????????????String?title;
????????????@Override
????????????protected?void?onCreate(@Nullable?Bundle?savedInstanceState)?{
????????????????super.onCreate(savedInstanceState);
????????????????setContentView(R.layout.activity_five);
????????????????//添加這行代碼,實際上就是自動生成了下面獲取參數值的代碼
????????????????ARouter.getsInstance().inject(this);
????????????????//如果不添加插入注解,則可以直接用下面的代碼。
????????????????//Intent?intent?=?getIntent();
????????????????//String?title?=?intent.getStringExtra("title");
????????????????Toast.makeText(this,?"title="?+?title,?Toast.LENGTH_SHORT).show();
????????????}
????????}
????????Bundle?bundle?=?new?Bundle();
????????bundle.putString("title","標題-------------");
????????ARouter.getsInstance()
????????????????.build(Path.five)
????????????????.withBundle(bundle)
????????????????.navigation();
路由注解生成的代碼位置
關于apt實踐與總結開源庫地址:
https://github.com/yangchong211/YCApt
大家都在看
線上活動 | 解救深陷暗戀的Mob福利菌!
從零開始跨平臺開發 Flutter 1.0 環境搭建
23種設計模式及案例整理分享
一篇文章告訴你MVC、MVP、MVVM
歡迎前往安卓巴士博客區投稿,技術成長于分享
期待巴友留言,共同探討學習
總結
以上是生活随笔為你收集整理的自定义依赖注解无效_关于Apt注解实践与总结【包含20篇博客】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 活用内核链表解决约瑟夫斯问题
- 下一篇: 程序员面试系列——大小端