摘要:楊充一定時間內該點擊事件只能執行一次用來修飾這是一個什么類型的注解。楊充自定義編譯器獲取遍歷,并生成代碼配置文件文件配置的作用是向系統注冊自定義注解處理器,執行編譯時使用進行處理。
目錄介紹
01.創建項目步驟
1.1 項目搭建
1.2 項目功能
02.自定義注解
03.創建Processor
04.compiler配置文件
05.編譯jar
06.如何使用
07.編譯生成代碼
08.部分源碼說明
8.1 Process類-process方法
8.2 OnceProxyInfo代理類
8.3 OnceMethod類
好消息博客筆記大匯總【16年3月到至今】,包括Java基礎及深入知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug匯總,當然也在工作之余收集了大量的面試題,長期更新維護并且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計N篇[近100萬字,陸續搬到網上],轉載請注明出處,謝謝!
鏈接地址:https://github.com/yangchong2...
如果覺得好,可以star一下,謝謝!當然也歡迎提出建議或者問題,萬事起于忽微,量變引起質變!
關于apt實踐與總結開源庫地址 https://github.com/yangchong2... 00.注解系列博客匯總 0.1 注解基礎系列博客01.Annotation注解詳細介紹
[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功能;簡單應用了編譯器注解,通過注解實現了防暴力點擊的功能,同時支持設置時間間隔;使用注解替代枚舉;使用注解一步步搭建簡單路由案例。結合相應的博客,在來一些小案例,從此應該對注解有更加深入的理解……
13 ARouter路由解析
比較詳細地分析了阿里路由庫
14 搭建路由條件
為何需要路由?實現路由方式有哪些,這些方式各有何優缺點?使用注解實現路由需要具備的條件以及簡單原理分析……
15 通過注解去實現路由跳轉
自定義Router注解,Router注解里有path和group,這便是仿照ARouter對路由進行分組。然后看看注解生成的代碼,手寫路由跳轉代碼。
16 自定義路由Processor編譯器
Processor介紹,重要方法,Element的作用,修飾方法的注解和ExecutableElement
17 利用apt生成路由映射文件
在Activity類上加上@Router注解之后,便可通過apt來生成對應的路由表,那么究竟是如何生成的代碼呢?
在組件化開發中,有多個module,為何要在build.gradle配置moduleName,又是如何通過代碼拿到module名稱?
process處理方法如何生成代碼的,又是如何寫入具體的路徑,寫入文件的?
看完這篇文章,應該就能夠理解上面這些問題呢!
18 路由框架的設計和初始化
編譯期是在你的項目編譯的時候,這個時候還沒有開始打包,也就是你沒有生成apk呢!路由框架在這個時期根據注解去掃描所有文件,然后生成路由映射文件。這些文件都會統一打包到apk里,app運行時期做的東西也不少,但總而言之都是對映射信息的處理,如執行執行路由跳轉等。那么如何設計框架呢?
生成的注解代碼,又是如何把這些路由映射關系拿到手,或者說在什么時候拿到手比較合適?為何注解需要進行初始化操作?
如何得到得到路由表的類名,如何得到所有的routerAddress---activityClass映射關系?
[19 路由框架設計注意要點]()
需要注意哪些要點?
20 為何需要依賴注入
有哪些注入的方式可以解耦,你能想到多少?路由框架為何需要依賴注入?路由為何用注解進行依賴注入,而不是用反射方式注入,或者通過構造方法注入,或者通過接口方式注入?
21 Activity屬性注入
在跳轉頁面時,如何傳遞intent參數,或者如何實現跳轉回調處理邏輯?
01.創建項目步驟 1.1 項目搭建首先創建一個Android項目。然后給我們的項目增加一個module,一定要記得是Java Library。因為APT需要用到jdk下的 【 javax.~ 】包下的類,這在AndroidSdk中是沒有的。
一定要注意:需要說明的是:我們的目的是寫一個Android庫,APT Moudle是java Library,不能使用Android API。所以還需要創建一個Android Library,負責框架主體部分. 然后由Android Library引用APT jar包。
項目目錄結構如圖:
app:Demo
AptAnnotation:java Library主要放一些項目中需要用到的自定義注解及相關代碼
AptApi:Android Library. OnceClick是我們真正對外發布并交由第三方使用的庫,它引用了apt-jar包
AptCompiler:java Library主要是應用apt技術處理注解,生成相關代碼或者相關源文件,是核心所在。
1.2 項目功能在一定時間內,按鈕點擊事件只能執行一次。未到指定時間,不執行點擊事件。
02.自定義注解創建Annotation Module,需要創建一個Java Library,名稱可為annotation,主要放一些項目中需要用到的自定義注解及相關代碼
新建一個類,OnceClick。就是我們自定義的注解。
/** ** @author 楊充 * blog : https://github.com/yangchong211 * time : 2017/06/21 * desc : 一定time時間內該點擊事件只能執行一次 * revise:*/ //@Retention用來修飾這是一個什么類型的注解。這里表示該注解是一個編譯時注解。 @Retention(RetentionPolicy.CLASS) //@Target用來表示這個注解可以使用在哪些地方。 // 比如:類、方法、屬性、接口等等。這里ElementType.METHOD 表示這個注解可以用來修飾:方法 @Target(ElementType.METHOD) //這里的interface并不是說OnceClick是一個接口。就像申明類用關鍵字class。申明注解用的就是@interface。 public @interface OnceClick { //返回值表示這個注解里可以存放什么類型值 int value(); } ```03.創建Processor創建Compiler Module,需要再創建一個Java Library,名稱可為compiler,主要是應用apt技術處理注解,生成相關代碼或者相關源文件,是核心所在。
Processor是用來處理Annotation的類。繼承自AbstractProcessor。
/** ** @author 楊充 * blog : https://github.com/yangchong211 * time : 2017/06/21 * desc : 自定義Processor編譯器 * revise:*/ @SupportedSourceVersion(SourceVersion.RELEASE_7) public class OnceClickProcessor extends AbstractProcessor { private Messager messager; private Elements elementUtils; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); messager = processingEnv.getMessager(); elementUtils = processingEnv.getElementUtils(); } @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { //獲取proxyMap Map04.compiler配置文件proxyMap = getProxyMap(roundEnv); //遍歷proxyMap,并生成代碼 for (String key : proxyMap.keySet()) { OnceProxyInfo proxyInfo = proxyMap.get(key); writeCode(proxyInfo); } return true; } @Override public Set getSupportedAnnotationTypes() { Set types = new LinkedHashSet<>(); types.add(OnceClick.class.getCanonicalName()); return types; } @Override public SourceVersion getSupportedSourceVersion() { return super.getSupportedSourceVersion(); } } ```
build.gradle文件配置
auto-service的作用是向系統注冊processor(自定義注解處理器),執行編譯時使用processor進行處理。
javapoet提供了一套生成java代碼的api,利用這些api處理注解,生成新的代碼或源文件。
OnceClickAnnotation是上文創建的注解module。
apply plugin: "java-library" dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation "com.google.auto.service:auto-service:1.0-rc3" implementation "com.squareup:javapoet:1.10.0" implementation project(":OnceClickAnnotation") } sourceCompatibility = "7" targetCompatibility = "7"05.編譯jar
這里有一個坑,主Module是不可以直接引用這個java Module的。(直接引用,可以成功運行一次~修改代碼以后就不能運行了)而如何多帶帶編譯這個java Module呢?在編譯器Gradle視圖里,找到Module apt下的build目錄下的Build按鈕。雙擊運行。
代碼沒有問題編譯通過的話,會有BUILD SUCCESS提示。生成的jar包在 apt 下的build目錄下的libs下。將apt.jar拷貝到app下的libs目錄,右鍵該jar,點擊Add as Library,添加Library
06.如何使用
代碼如下所示
public class MainActivity extends AppCompatActivity { @Override 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"); } @OnceClick(R.id.tv_2) public void Click2(View v){ Log.d("tag--------------------","tv_2"); } }07.編譯生成代碼
編譯之后生成的代碼路徑,在項目中的build文件夾,如圖所示
編譯之后生成的代碼
// 編譯生成的代碼,不要修改 // 更多內容: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_Proxy08.部分源碼說明 8.1 Process類-process方法implements AbstractInjector { 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); } }}); } } } 當某個類Activity使用了@OnceClick注解之后,我們就應該為其生成一個對應的代理類,代理類實現我們框架的功能:為某個View設置點擊事件,并且這個點擊事件一定時間內只能執行一次。所以,一個代理類可能有多個需要處理的View。
先看process代碼:
ProxyInfo對象:存放生成代理類的必要信息,并生成代碼。
getProxyMap方法:使用參數roundEnv,遍歷所有@OnceClick注解,并生成代理類ProxyInfo的Map。
writeCode方法:真正生成代碼的方法。
總結一下:編譯時,取得所有需要生成的代理類信息。遍歷代理類集合,根據代理類信息,生成代碼。
@Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { //獲取proxyMap Map8.2 OnceProxyInfo代理類proxyMap = getProxyMap(roundEnv); //遍歷proxyMap,并生成代碼 for (String key : proxyMap.keySet()) { OnceProxyInfo proxyInfo = proxyMap.get(key); //寫入代碼 writeCode(proxyInfo); } return true; }
其實這個類,才是這個框架的重中之重,因為生成什么代碼,全靠這個類說了算。這個類也沒什么好講的,就是用StringBuidler拼出一個類來。ProxyInfo保存的是類信息,方法信息我們用List methods保存。然后根據這些信息生成類。
public class OnceProxyInfo { private String packageName; private String targetClassName; private String proxyClassName; private TypeElement typeElement; private List8.3 OnceMethod類methods; private static final String PROXY = "_Once_Proxy"; OnceProxyInfo(String packageName, String className) { this.packageName = packageName; this.targetClassName = className; this.proxyClassName = className + "$$" + PROXY; } String getProxyClassFullName() { return packageName + "." + proxyClassName; } String generateJavaCode() throws OnceClickException { StringBuilder builder = new StringBuilder(); builder.append("http:// 編譯生成的代碼,不要修改 "); builder.append("http:// 更多內容:https://github.com/yangchong211 "); builder.append("package ").append(packageName).append("; "); //寫入導包 builder.append("import android.view.View; "); builder.append("import com.ycbjie.api.Finder; "); builder.append("import com.ycbjie.api.AbstractInjector; "); builder.append(" "); builder.append("public class ").append(proxyClassName) .append(" ") .append(" implements AbstractInjector ").append(" { "); builder.append(" "); generateInjectMethod(builder); builder.append(" "); builder.append("} "); return builder.toString(); } private String getTargetClassName() { return targetClassName.replace("$", "."); } private void generateInjectMethod(StringBuilder builder) throws OnceClickException { builder.append(" public long intervalTime; "); builder.append(" "); builder.append(" @Override ") .append(" public void setIntervalTime(long time) { ") .append(" intervalTime = time; } "); builder.append(" "); builder.append(" @Override ") .append(" public void inject(final Finder finder, final T target, Object source) { "); builder.append(" View view;"); builder.append(" "); //這一步是遍歷所有的方法 for (OnceMethod method : getMethods()) { builder.append(" view = ") .append("finder.findViewById(source, ") .append(method.getId()) .append("); "); builder.append(" if(view != null){ ") .append(" view.setOnClickListener(new View.OnClickListener() { ") .append(" long time = 0L; "); builder.append(" @Override ") .append(" public void onClick(View v) { "); builder.append(" long temp = System.currentTimeMillis(); ") .append(" if (temp - time >= intervalTime) { " + " time = temp; "); if (method.getMethodParametersSize() == 1) { if (method.getMethodParameters().get(0).equals("android.view.View")) { builder.append(" target.") .append(method.getMethodName()).append("(v);"); } else { throw new OnceClickException("Parameters must be android.view.View"); } } else if (method.getMethodParametersSize() == 0) { builder.append(" target.") .append(method.getMethodName()).append("();"); } else { throw new OnceClickException("Does not support more than one parameter"); } builder.append(" } ") .append(" }") .append("}); } "); } builder.append(" } "); } TypeElement getTypeElement() { return typeElement; } void setTypeElement(TypeElement typeElement) { this.typeElement = typeElement; } List getMethods() { return methods == null ? new ArrayList () : methods; } void addMethod(OnceMethod onceMethod) { if (methods == null) { methods = new ArrayList<>(); } methods.add(onceMethod); } }
需要講的一點是,每一個使用了@OnceClick注解的Activity或View,都會為其生成一個代理類,而一個代理中有可能有很多個@OnceClick修飾的方法,所以我們專門為每個方法有創建了一個javaBean用于保存方法信息:
public class OnceMethod { private int id; private String methodName; private List關于其他內容介紹 01.關于博客匯總鏈接methodParameters; OnceMethod(int id, String methodName, List methodParameters) { this.id = id; this.methodName = methodName; this.methodParameters = methodParameters; } int getMethodParametersSize() { return methodParameters == null ? 0 : methodParameters.size(); } int getId() { return id; } String getMethodName() { return methodName; } List getMethodParameters() { return methodParameters; } } 1.技術博客匯總
2.開源項目匯總
3.生活博客匯總
4.喜馬拉雅音頻匯總
5.其他匯總
02.關于我的博客我的個人站點:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/...
簡書:http://www.jianshu.com/u/b7b2...
csdn:http://my.csdn.net/m0_37700275
喜馬拉雅聽書:http://www.ximalaya.com/zhubo...
開源中國:https://my.oschina.net/zbj161...
泡在網上的日子:http://www.jcodecraeer.com/me...
郵箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a... 239.headeruserinfo.3.dT4bcV
segmentfault頭條:https://segmentfault.com/u/xi...
掘金:https://juejin.im/user/593943...
關于apt實踐與總結開源庫地址 https://github.com/yangchong2...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73464.html
摘要:使用實現功能運行期注解案例使用簡單的注解,便可以設置布局,等效于使用實現路由綜合型案例比較全面的介紹從零起步,一步一步封裝簡易的路由開源庫。申明注解用的就是。返回值表示這個注解里可以存放什么類型值。 YCApt關于apt方案實踐與總結 目錄介紹 00.注解系列博客匯總 01.什么是apt 02.annotationProcessor和apt區別 03.項目目錄結構 04.該案例作用 ...
摘要:本文將分享軟件基本用法及文件進程注冊表查看,這是一款微軟推薦的系統監視工具,功能非常強大可用來檢測惡意軟件。可以幫助使用者對系統中的任何文件注冊表操作進行監視和記錄,通過注冊表和文件讀寫的變化,有效幫助診斷系統故障或發現惡意軟件病毒及木馬。 ...
摘要:網絡黑白一書所抄襲的文章列表這本書實在是垃圾,一是因為它的互聯網上的文章拼湊而成的,二是因為拼湊水平太差,連表述都一模一樣,還抄得前言不搭后語,三是因為內容全都是大量的科普,不涉及技術也沒有干貨。 《網絡黑白》一書所抄襲的文章列表 這本書實在是垃圾,一是因為它的互聯網上的文章拼湊而成的,二是因為拼湊水平太差,連表述都一模一樣,還抄得前言不搭后語,三是因為內容全都是大量的科普,不涉及技術...
摘要:是一個基于的命令行工具,可使用和來制作精美的電子書,并非關于的教程。使用制作電子書,必備兩個文件和。今天,我就教大家如何使用快速制作應用,該應用是基于鏡像制作,可以隨意運行在任何一個安裝有的主機上。首先安裝安裝完成后,地址欄輸入即可訪問。 GitBook 是一個基于 Node.js 的命令行工具,可使用 Github/Git 和 Markdown 來制作精美的電子書,GitBook 并...
閱讀 1129·2021-10-27 14:13
閱讀 2636·2021-10-09 09:54
閱讀 897·2021-09-30 09:46
閱讀 2424·2021-07-30 15:30
閱讀 2166·2019-08-30 15:55
閱讀 3409·2019-08-30 15:54
閱讀 2847·2019-08-29 14:14
閱讀 2771·2019-08-29 13:12