av一区二区在线观看_亚洲男人的天堂网站_日韩亚洲视频_在线成人免费_欧美日韩精品免费观看视频_久草视

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

Android APT 實(shí)現(xiàn)控件注入框架SqInject的示例

瀏覽:4日期:2022-09-20 15:52:27
作者

大家好,我叫小鑫,也可以叫我蠟筆小鑫😊;

本人17年畢業(yè)于中山大學(xué),于2018年7月加入37手游安卓團(tuán)隊(duì),曾經(jīng)就職于久邦數(shù)碼擔(dān)任安卓開發(fā)工程師;

目前是37手游安卓團(tuán)隊(duì)的海外負(fù)責(zé)人,負(fù)責(zé)相關(guān)業(yè)務(wù)開發(fā);同時(shí)兼顧一些基礎(chǔ)建設(shè)相關(guān)工作。

背景

在游戲發(fā)行中,經(jīng)常需要切包,如果直接使用R.id.xxx,在回編譯時(shí),由于resources.arsc會(huì)重新編譯,R類中的id值和resources.arsc中的對(duì)應(yīng)關(guān)系會(huì)異常,導(dǎo)致程序異常。我們有兩種解決方法。

一種是在切包過程中糾正R類的值,實(shí)現(xiàn)方案具體介紹可以查看

游戲發(fā)行切包資源索引沖突解決方案,鏈接如下:

//www.jb51.net/article/207579.htm

另一種解決方案則是使用getIdentifier獲取資源ID,放棄使用R類,這種方式中,編碼較為麻煩,并且getIdentifier中需要寫的是字符串,容易寫錯(cuò),并且編譯過程中是發(fā)現(xiàn)不了的。基于這種情況,我們開發(fā)了基于getIdentifier的控件注入框架

一、APT技術(shù)簡(jiǎn)介1、APT定義

APT(Annotation Processing Tool)即注解處理器,是一種處理注解的工具,確切的說它是javac的一個(gè)工具,它用來在編譯時(shí)掃描和處理注解。注解處理器以Java代碼作為輸入,生成.java文件作為輸出

2、注解定義

1、注解是一種能被添加到j(luò)ava代碼中的元數(shù)據(jù),類、方法、變量、參數(shù)和包都可以用注解修飾。

2、注解對(duì)于它所修飾的代碼沒有直接的影響

3、APT原理簡(jiǎn)介

Android APT 實(shí)現(xiàn)控件注入框架SqInject的示例

Annotation processing是在編譯階段執(zhí)行的,它的原理就是讀入Java源代碼,解析注解,然后生成新的Java代碼。新生成的Java代碼最后被編譯成Java字節(jié)碼,注解解析器(Annotation Processor)不能改變讀入的Java 類,比如不能加入或刪除Java方法

二、APT實(shí)戰(zhàn)使用1、SqInject框架來源

在手游發(fā)行中,經(jīng)常需要切包,將游戲接完SDK1的包,通過反編譯,替換smali文件及其他資源文件的方式,替換為渠道SDK2的渠道包。在這個(gè)反編譯回編譯的過程中,資源索引ID(即R類和resources.arsc中的ID映射關(guān)系)會(huì)發(fā)生沖突導(dǎo)致程序異常,即不做特殊處理的話,渠道SDK及發(fā)行SDK是不能直接使用R類的,要使用getIdentifier獲取資源ID

要求在程序中使用getIdentifier,在開發(fā)過程中是比較麻煩的事情。在這樣的條件下,我們也無法使用如butterknife這樣的框架。因此,我們模仿butterknife開發(fā)了一套基于getIdentifier的控件注入框架SqInject。下面介紹SqInject的實(shí)現(xiàn),先來看下簡(jiǎn)單使用哈

public class MainActivity extends AppCompatActivity { //綁定ID @BindView(SqR.id.tv) TextView hello; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SqInject.bind(this); Log.e('SqInject', hello.getText().toString()); } //綁定點(diǎn)擊事件 @OnClick(SqR.id.tv) public void click(View view) { Intent intent = new Intent(MainActivity.this, TestActivity.class); startActivity(intent); }}2、SqInject的實(shí)現(xiàn)原理2.1、注解處理器模塊實(shí)現(xiàn)

上文說到APT常用于生成代碼,在SqInject中APT注解處理環(huán)節(jié)中,流程如下圖所示:

Android APT 實(shí)現(xiàn)控件注入框架SqInject的示例

在編譯過程中掃描注解,生成Java代碼,而后再次編譯

在SqInject代碼中,實(shí)現(xiàn)如下:

@AutoService(Processor.class)@SupportedSourceVersion(SourceVersion.RELEASE_7)public class SqInjectProcessor extends AbstractProcessor { ... //核心方法 @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) { //控件類注解解析,ResChecker檢查資源id合法性,合法則生成'類名+$ViewBinder類,否則編譯失敗 BindViewBuilder bindViewBuilder = new BindViewBuilder(roundEnvironment, mResChecker, mElementUtils, mTypeUtils, mFiler, mMessager); bindViewBuilder.build(); //id類注解解析,ResChecker檢查資源id合法性,合法則生成'類名+$IdBinder類',否則編譯失敗 BindIdsBuilder bindIdsBuilder = new BindIdsBuilder(roundEnvironment, mResChecker, mElementUtils, mTypeUtils, mFiler, mMessager); bindIdsBuilder.build(); return false; } }

在生成控件注入相關(guān)代碼之前,框架中會(huì)先檢測(cè)資源id的合法性,本框架中在使用注解時(shí),傳入的是字符串。可資源名稱是有可能不存在對(duì)應(yīng)資源的,框架會(huì)做相應(yīng)的檢測(cè)

2.2、資源檢測(cè)

Android編譯資源過程中,會(huì)生成R類,也就是說只有在R類中存在的,用getIdentifier才能夠獲取到,那么我們可以用R類來檢測(cè)傳入的參數(shù)是否合理,代碼如下:

/** * 檢測(cè)資源id在R文件中是否存在 * @param name * @param type * @return */ public boolean isIdValid(String name, String type) { String RClassName = mPackageNeme + '.R.' + type; TypeElement RClassType = mElementUtils.getTypeElement(RClassName); if (RClassType == null) { mMessager.printMessage(Diagnostic.Kind.ERROR, RClassName + '不存在,請(qǐng)檢查是否包名有誤,或者類型錯(cuò)誤'); } else { //遍歷屬性 for (Element element : RClassType.getEnclosedElements()) { String fieldName = element.getSimpleName().toString(); if (name.equals(fieldName)) { return true; } } } return false; }2.3、解析注解,生成代碼

以解析BindView為例,代碼如下:

/** * 解析BindView注解 * @return */ private Map<TypeElement, List<VariableElement>> parseBindView(){ Set<Element> elements = (Set<Element>) mRoundEnvironment.getElementsAnnotatedWith(BindView.class); if (!Utils.isEmpty(elements)) { Map<TypeElement, List<VariableElement>> map = new HashMap<>(); for (Element element : elements) { if (element instanceof VariableElement) { //獲取該屬性所在類 TypeElement targetElement = (TypeElement) element.getEnclosingElement(); mTargetSet.add(targetElement); if (map.get(targetElement) == null) { List<VariableElement> targetStringLists = new ArrayList<>(); targetStringLists.add((VariableElement) element); map.put(targetElement, targetStringLists); } else { map.get(targetElement).add((VariableElement) element); } } } return map; } return null; }

解析完BindView注解后,使用javapoet生成代碼,篇幅有限,下面僅列出獲取參數(shù)和生成代碼的一小部分

if (mBindViewIdTargetMap != null && mBindViewIdTargetMap.get(targetElement) != null) { List<VariableElement> viewElements = mBindViewIdTargetMap.get(targetElement); //方法體 for (VariableElement viewElement : viewElements) { //獲取屬性名 String fieldName = viewElement.getSimpleName().toString(); //獲取類型 TypeMirror typeMirror = viewElement.asType(); TypeElement fieldClassElement = (TypeElement) mTypeUtils.asElement(typeMirror); mMessager.printMessage(Diagnostic.Kind.NOTE, '注解的字段類型為: ' + fieldClassElement.getQualifiedName().toString()); TypeElement fieldType = mElementUtils.getTypeElement(fieldClassElement.getQualifiedName()); ClassName fieldClassName = ClassName.get(fieldType); //獲取@BindView注解的值,即名稱 String name = viewElement.getAnnotation(BindView.class).value(); //檢測(cè)名稱是否合法 if(!mResChecker.isIdValid(name, 'id')){ mMessager.printMessage(Diagnostic.Kind.ERROR, 'R文件中不存在id為' + name + '的值'); } methodBuilder.addStatement('target.$N = $T.findRequiredViewAsType(source, $S, $S, $T.class)', fieldName, JptConstants.UTILS, name, 'field ' + fieldName,fieldClassName); } }

小小總結(jié)一下,在注解處理器中,最重要的兩個(gè)環(huán)節(jié),一個(gè)是解析注解,獲取參數(shù),然后是利用javapoet框架生成代碼

下面看下生成的代碼

public class MainActivity$ViewBinder implements ViewBinder<MainActivity> { @Override public void bindView(final MainActivity target, final View source) { //這里就是上面的代碼生成的 target.hello = ViewUtils.findRequiredViewAsType(source, 'tv', 'field hello', TextView.class); IdUtils.findViewByName('tv', source).setOnClickListener(new DebouncingOnClickListener() { public void doClick(View v) { target.click(v); } } ); }}

到這里,就介紹完了使用APT生成代碼了哈。本文中提到的框架SqInject是我們?nèi)粘9ぷ髦袝?huì)使用到的SqInject框架,該框架以開源,地址是: github.com/37sy/SqInje… 有興趣的歡迎star哈

以上就是Android APT 實(shí)現(xiàn)控件注入框架SqInject的示例的詳細(xì)內(nèi)容,更多關(guān)于Android APT控件注入框架SqInject的資料請(qǐng)關(guān)注好吧啦網(wǎng)其它相關(guān)文章!

標(biāo)簽: Android
相關(guān)文章:
主站蜘蛛池模板: 成人免费片 | 国产黄色大片 | 国产成人精品视频 | a天堂在线视频 | 精品一区二区免费视频 | 另类一区二区 | 男女插插插视频 | 天天操免费视频 | 国产一区福利 | 黄色激情视频网站 | 少妇搡bbbb搡bbb搡澳门 | 51成人做爰www免费看网站 | 超碰在线人人 | 亚洲精品欧美 | 伊人国产女| 青青青国产 | 欧美理论片在线观看 | 中文在线观看视频 | 美女毛片视频 | 欧美日在线| 国产欧美一区二区精品性色超碰 | 国产小精品 | 一区二区三区黄色 | 四虎在线免费视频 | 亚洲色妞 | 国产免费一区二区三区在线观看 | 成人手机在线视频 | 久久久久久久久国产精品 | 亚洲视频一区在线观看 | 成人免费看片视频 | 欧美高清在线 | 欧美午夜在线观看 | 国产黄色一区二区 | 久久国语 | 能看毛片的网站 | 99这里有精品 | 97精品久久 | 精品国产一区二区三 | 一区二区三区在线免费观看 | 五月婷婷激情网 | 99在线免费视频 |