日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Android APT 實現控件注入框架SqInject的示例

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

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

本人17年畢業于中山大學,于2018年7月加入37手游安卓團隊,曾經就職于久邦數碼擔任安卓開發工程師;

目前是37手游安卓團隊的海外負責人,負責相關業務開發;同時兼顧一些基礎建設相關工作。

背景

在游戲發行中,經常需要切包,如果直接使用R.id.xxx,在回編譯時,由于resources.arsc會重新編譯,R類中的id值和resources.arsc中的對應關系會異常,導致程序異常。我們有兩種解決方法。

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

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

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

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

一、APT技術簡介1、APT定義

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

2、注解定義

1、注解是一種能被添加到java代碼中的元數據,類、方法、變量、參數和包都可以用注解修飾。

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

3、APT原理簡介

Android APT 實現控件注入框架SqInject的示例

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

二、APT實戰使用1、SqInject框架來源

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

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

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()); } //綁定點擊事件 @OnClick(SqR.id.tv) public void click(View view) { Intent intent = new Intent(MainActivity.this, TestActivity.class); startActivity(intent); }}2、SqInject的實現原理2.1、注解處理器模塊實現

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

Android APT 實現控件注入框架SqInject的示例

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

在SqInject代碼中,實現如下:

@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; } }

在生成控件注入相關代碼之前,框架中會先檢測資源id的合法性,本框架中在使用注解時,傳入的是字符串。可資源名稱是有可能不存在對應資源的,框架會做相應的檢測

2.2、資源檢測

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

/** * 檢測資源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 + '不存在,請檢查是否包名有誤,或者類型錯誤'); } 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生成代碼,篇幅有限,下面僅列出獲取參數和生成代碼的一小部分

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(); //檢測名稱是否合法 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); } }

小小總結一下,在注解處理器中,最重要的兩個環節,一個是解析注解,獲取參數,然后是利用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是我們日常工作中會使用到的SqInject框架,該框架以開源,地址是: github.com/37sy/SqInje… 有興趣的歡迎star哈

以上就是Android APT 實現控件注入框架SqInject的示例的詳細內容,更多關于Android APT控件注入框架SqInject的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
欧美激情亚洲| 国产亚洲字幕| 日韩免费视频| 亚洲美女久久精品| 偷拍精品精品一区二区三区| 国产一区二区三区四区二区| 国产精品4hu.www| 亚洲精品一级| 久久国产人妖系列| 久久久精品国产**网站| 精品国产午夜肉伦伦影院| 国模大尺度视频一区二区| 日韩免费av| 先锋影音国产一区| 国产精品美女久久久| 亚洲深深色噜噜狠狠爱网站| 亚洲精品美女| 69堂免费精品视频在线播放| 国产欧美日韩综合一区在线播放| 精品一二三区| 欧美99久久| 亚洲制服欧美另类| 亚洲免费成人av在线| 美女久久久久久 | 亚洲深夜福利在线观看| 国产乱论精品| 久久蜜桃av| 亚洲一级大片| 9999国产精品| 中文字幕日韩高清在线| 精品午夜久久| 午夜一级在线看亚洲| 国产麻豆一区| 欧美va天堂| 日韩福利视频网| 国产精品高颜值在线观看| 91久久中文| 国产日韩欧美一区二区三区 | av一区二区高清| 国产欧美一区二区三区国产幕精品 | 久久久久久婷| 在线观看视频免费一区二区三区| 黄色aa久久| 日本国产欧美| 亚洲综合国产| 久久久久久美女精品| 国产欧美高清视频在线| 亚洲作爱视频| 久久精品青草| 久久久久免费| 日本va欧美va精品发布| 中文国产一区| 国产一区二区精品久| 天堂俺去俺来也www久久婷婷| 五月天综合网站| 久久uomeier| 国产精品啊v在线| 日本不卡高清| 首页国产欧美日韩丝袜| 国内激情久久| 国产欧美一区二区三区精品酒店| 国产美女亚洲精品7777| 日韩精品a在线观看91| 97精品资源在线观看| 91p九色成人| 国产福利一区二区三区在线播放| 成人在线黄色| 欧美日韩国产一区二区三区不卡| 亚洲a在线视频| 亚洲一区二区三区高清不卡| 中文字幕日韩欧美精品高清在线| 91精品啪在线观看国产爱臀| 欧美www视频在线观看| 香蕉人人精品| 日本一区中文字幕| 老色鬼精品视频在线观看播放| 在线中文字幕播放| 亚洲精品极品少妇16p| 日韩二区三区在线观看| 成人福利视频| 石原莉奈在线亚洲二区| 国产精品欧美日韩一区| 久久婷婷激情| 亚洲自拍另类| 麻豆久久一区二区| 蜜桃视频欧美| 国产欧美欧美| 激情综合网站| 国产日韩欧美一区在线| 91精品精品| 91精品一区| 欧美日韩一二| 精品三级国产| 日韩国产在线观看| 久久中文视频| 麻豆91在线播放| 免费一级片91| 日韩欧美精品| 亚洲精品女人| 免费久久久久久久久| 国产日韩一区二区三区在线播放| 欧美精品一区二区久久| 精品三级久久久| 欧美一级一区| 日韩一区精品字幕| 日韩欧美视频专区| 国产精品va视频| 亚洲2区在线| aa国产精品| 日韩精品影视| 国产不卡av一区二区| 日韩高清成人在线| 男人的天堂亚洲一区| 久久久久久色 | 首页国产欧美日韩丝袜| 国产网站在线| 国产精品久久久久久久久久白浆| 首页亚洲欧美制服丝腿| 另类专区亚洲| 麻豆精品久久久| 欧美在线91| 久久精品99国产精品| 日韩精品免费视频一区二区三区| 日韩影院免费视频| 欧美资源在线| 蜜桃一区二区三区在线观看| 伊人久久亚洲热| 日韩午夜精品| 亚洲自啪免费| 男女男精品网站| 一区二区高清| 亚洲人成毛片在线播放女女| 美女日韩在线中文字幕| 欧美日韩国产免费观看| 国产亚洲亚洲| 一区二区国产精品| 亚洲精品视频一二三区| 日韩国产在线观看| 国产精品一级在线观看| 精品一区视频| 丝袜诱惑一区二区| 亚洲欧美一区在线| 久久亚洲图片| 7m精品国产导航在线| 国产精品a久久久久| 亚洲黄色中文字幕| 欧美一级精品| 亚洲视频电影在线| 亚洲一二三区视频| 免费在线观看一区| 九色porny丨国产首页在线| 91精品福利| 欧美日一区二区在线观看| 国产亚洲一区二区手机在线观看 | 亚洲综合婷婷| 丁香婷婷久久| 亚洲国产不卡| 国产精品久久久久久模特 | 亚洲三级毛片| 成人小电影网站| 亚洲一级淫片| 欧美羞羞视频| 欧美一级一区| 影音先锋久久| 精品久久视频| 中文字幕日本一区| 国产一区二区三区亚洲| 一本一道久久a久久精品蜜桃| 日本不卡中文字幕| 欧美日韩精品在线一区| 久久国产人妖系列| jiujiure精品视频播放| 日本va欧美va精品发布| 在线日韩一区| 国产精品网站在线看| 中文国产一区| 99精品电影| 成人台湾亚洲精品一区二区| 日韩激情一区二区| 蜜臀av一区二区在线免费观看| 精品久久久网| 日本国产一区| 亚洲少妇自拍| 久久婷婷av| 精品国产乱码久久久| 欧美偷窥清纯综合图区| 欧美在线资源| 国产v综合v| 日韩一区二区中文| 久久xxx视频| 麻豆国产欧美一区二区三区| 国产午夜久久av| 欧美精品国产白浆久久久久| 日韩1区2区日韩1区2区| 亚洲精品成a人ⅴ香蕉片| 蜜桃久久久久久| 亚洲精品福利| 国产日产精品_国产精品毛片 | 每日更新成人在线视频| 视频一区视频二区在线观看|