安卓to鴻蒙系列:ButterKnife(一)
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
本文是關(guān)于ButterKnife的移植的第一篇:
先介紹基礎(chǔ)知識(shí),理解apt是什么,最終輸出一個(gè)乞丐版BufferKnife注入工具。
這是讀懂BufferKnife源碼的基礎(chǔ)。
第二篇占坑:具體講解BufferKnife的移植。
基礎(chǔ)知識(shí)
apt技術(shù)的實(shí)際應(yīng)用:
- Android Databinding綁定view
- ButterKnife綁定view
- Dagger2注入變量
- ARouter生成路由表
真的好多,很重要。apt就是生產(chǎn)力工具!
什么是apt?
通俗講:apt就是javac對(duì)外開放的一個(gè)插件,使javac在編譯期間獲取注解(Annotation),并做出相應(yīng)的處理(多數(shù)都是生成一些java代碼)。
從上圖可以看出apt處理的是 java源文件 ,在編譯期介入。
與之對(duì)比的是asm之類的工具,處理的是字節(jié)碼文件,在編譯后期介入。
apt與javac的約定
apt與javac約定在META-INF/services/javax.annotation.processing.Processor文件中注冊(cè)apt插件。這樣apt就參與到j(luò)avac的編譯過程中了。
寫一個(gè)乞丐版BufferKnife
不想看文字,直接看源碼,點(diǎn)擊 https://gitee.com/andych008/aptDemo
初始代碼fork自 https://github.com/LiMubai2017/aptDemo ,先對(duì)作者表示感謝。
乞丐版BufferKnife作為一款view注入工具,主要干了3件事,
解析注解
處理注解(生成模板類文件)
通過模板類注入view對(duì)象
第0步:準(zhǔn)備工作
先定義注解BindView,被@BindView標(biāo)記的變量會(huì)被注入。
一般注解都定義在一個(gè)單獨(dú)的module(如取名apt-annotation),因?yàn)樗鼤?huì)被apt-compiler和apt-api都依賴,屬于公共代碼。
apt-compiler是apt的主要代碼所在,完成注解的解析、模板文件的生成。
apt-api則是對(duì)外的工具類,供用戶使用,完成注入操作。
app是demo,其中定義了
- @BindView(value = ResourceTable.Id_text_helloworld)
- public Text testTextView;
第一步:解析注解
在apt-compiler中定義類BindViewProcessor繼承javax.annotation.processing.AbstractProcessor,實(shí)現(xiàn)其中的getSupportedAnnotationTypes()該方法注冊(cè)要解析的注解。
第二步:處理注解(生成模板文件)
在BindViewProcessor中實(shí)現(xiàn)process()方法,處理注解。
先理解javax.lang.model.element.Element和javax.lang.model.type.TypeMirror,參考這里 有詳細(xì)的解釋。
- 簡(jiǎn)單講:
- Element是描述java語言元素的類,比如包、類、變量、參數(shù)等。
- TypeMirror是描述Element類型的類,比如各種基本類型、數(shù)組、類等。
很繞,只有多用才能真正理解。比如:demo中testTextView就是VariableElement元素類型
- TypeElement enclosingElement = (TypeElement) variableElement.getEnclosingElement();//獲取代表MainAbility的TypeElement
- String field = variableElement.getSimpleName().toString();//testTextView
- TypeMirror typeMirror = variableElement.asType();//ohos.agp.components.Text
通過log()方法,可以使用Messager打日志,驗(yàn)證我們的理解。
- log(String.format("element : (%s) %s ", element.getKind(), element));
- log(String.format("bind : (%s) %s <--> id = %d", typeMirror, field, id));
- 輸出日志:
- 注: element : (FIELD) testTextView
- 注: bind : (ohos.agp.components.Text) testTextView <--> id = 16777222
generateCodeByPoet()方法中,使用javapoet生成模板代碼MainAbility$$Autobind.java(文件路徑app/build/generated/source/annotation/debug/com/example/apt_demo/MainAbility$$Autobind.java)
關(guān)于javapoet的使用,直接看官方文檔吧:https://github.com/square/javapoet
解釋一下下面這段代碼,讓大家對(duì)javapoet有一直觀的認(rèn)識(shí)
- MethodSpec.Builder injectMethod = MethodSpec.methodBuilder("inject")//生成一個(gè)方法,方法名是inject
- .addAnnotation(Override.class)//給方法加上"Override.class"注解
- .addModifiers(Modifier.PUBLIC)//給方法加上訪問控制符
- .addParameter(Object.class, "target")//給方法加上參數(shù)
- .addStatement("$T substitute = ($T)target", className, className);//在方法體內(nèi)定義一條語名
上面的代碼生成下面的代碼(我用java代碼生成java代碼,這就是javapoet干的事情):
- @Override
- public void inject(Object target) {
- MainAbility substitute = (MainAbility)target;
- }
看完上面這一坨,你如果覺得難。請(qǐng)用JavaWriter生成java文件。你就會(huì)覺得javapoet真香。
第三步:通過模板類注入view對(duì)象
在apt-api中,我們定義一個(gè)AutoBind.java類封裝對(duì)模板類MainAbility$$Autobind.java的操作。
按照模板類的命名規(guī)則xxx$$Autobind,通過反射實(shí)例化出MainAbility$$Autobind.java,調(diào)用 其中的inject方法,完成view的注入。
總結(jié)
apt只是一個(gè)工具,在這套工具框架下,怎么處理注解才是難點(diǎn)。
BufferKnife和我們的“乞丐版BufferKnife”本質(zhì)上沒有區(qū)別。除了注入view,還支持事件綁定、增量編譯。
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)