电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

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

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

瀏覽:3日期: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
相關文章:
主站蜘蛛池模板: 锂辉石检测仪器,水泥成分快速分析仪-湘潭宇科分析仪器有限公司 | 袋式过滤器,自清洗过滤器,保安过滤器,篮式过滤器,气体过滤器,全自动过滤器,反冲洗过滤器,管道过滤器,无锡驰业环保科技有限公司 | 重庆中专|职高|技校招生-重庆中专招生网 | 超声波电磁流量计-液位计-孔板流量计-料位计-江苏信仪自动化仪表有限公司 | 滚筒烘干机_转筒烘干机_滚筒干燥机_转筒干燥机_回转烘干机_回转干燥机-设备生产厂家 | 合肥角钢_合肥槽钢_安徽镀锌管厂家-昆瑟商贸有限公司 | 通用磨耗试验机-QUV耐候试验机|久宏实业百科 | 儿童乐园|游乐场|淘气堡招商加盟|室内儿童游乐园配套设备|生产厂家|开心哈乐儿童乐园 | 校园文化空间设计-数字化|中医文化空间设计-党建|法治廉政主题文化空间施工-山东锐尚文化传播公司 | 合肥制氮机_合肥空压机厂家_安徽真空泵-凯圣精机 | 北京租车公司_汽车/客车/班车/大巴车租赁_商务会议/展会用车/旅游大巴出租_北京桐顺创业租车公司 | 恒压供水控制柜|无负压|一体化泵站控制柜|PLC远程调试|MCGS触摸屏|自动控制方案-联致自控设备 | 阻垢剂,反渗透阻垢剂,缓蚀阻垢剂-山东普尼奥水处理科技有限公司 真空粉体取样阀,电动楔式闸阀,电动针型阀-耐苛尔(上海)自动化仪表有限公司 | 优宝-汽车润滑脂-轴承润滑脂-高温齿轮润滑油脂厂家 | 沙盘模型公司_沙盘模型制作公司_建筑模型公司_工业机械模型制作厂家 | 铁艺,仿竹,竹节,护栏,围栏,篱笆,栅栏,栏杆,护栏网,网围栏,厂家 - 河北稳重金属丝网制品有限公司 山东太阳能路灯厂家-庭院灯生产厂家-济南晟启灯饰有限公司 | AGV无人叉车_激光叉车AGV_仓储AGV小车_AGV无人搬运车-南昌IKV机器人有限公司[官网] | 宝鸡市人民医院| 下水道疏通_管道疏通_马桶疏通_附近疏通电话- 立刻通 | 粘度计维修,在线粘度计,二手博勒飞粘度计维修|收购-天津市祥睿科技有限公司 | 信阳网站建设专家-信阳时代网联-【信阳网站建设百度推广优质服务提供商】信阳网站建设|信阳网络公司|信阳网络营销推广 | 除湿机|工业除湿机|抽湿器|大型地下室车间仓库吊顶防爆除湿机|抽湿烘干房|新风除湿机|调温/降温除湿机|恒温恒湿机|加湿机-杭州川田电器有限公司 | 龙门加工中心-数控龙门加工中心厂家价格-山东海特数控机床有限公司_龙门加工中心-数控龙门加工中心厂家价格-山东海特数控机床有限公司 | 防水试验机_防水测试设备_防水试验装置_淋雨试验箱-广州岳信试验设备有限公司 | 上海单片机培训|重庆曙海培训分支机构—CortexM3+uC/OS培训班,北京linux培训,Windows驱动开发培训|上海IC版图设计,西安linux培训,北京汽车电子EMC培训,ARM培训,MTK培训,Android培训 | 上海橡胶接头_弹簧减震器_金属软接头厂家-上海淞江集团 | 蒸压釜_蒸养釜_蒸压釜厂家-山东鑫泰鑫智能装备有限公司 | 产业规划_产业园区规划-产业投资选址及规划招商托管一体化服务商-中机院产业园区规划网 | 石栏杆_青石栏杆_汉白玉栏杆_花岗岩栏杆 - 【石雕之乡】点石石雕石材厂 | 河南生物显微镜,全自动冰冻切片机-河南荣程联合科技有限公司 | 铝镁锰板厂家_进口钛锌板_铝镁锰波浪板_铝镁锰墙面板_铝镁锰屋面-杭州军晟金属建筑材料 | 福建珂朗雅装饰材料有限公司「官方网站」 | 1000帧高速摄像机|工业高速相机厂家|科天健光电技术 | 杭州厂房降温,车间降温设备,车间通风降温,厂房降温方案,杭州嘉友实业爽风品牌 | 春腾云财 - 为企业提供专业财税咨询、代理记账服务 | 软装设计-提供软装装饰和软装配饰及软装陈设的软装设计公司 | 百度网站优化,关键词排名,SEO优化-搜索引擎营销推广 | 苹果售后维修点查询,苹果iPhone授权售后维修服务中心 – 修果网 拼装地板,悬浮地板厂家,悬浮式拼装运动地板-石家庄博超地板科技有限公司 | 预制直埋蒸汽保温管-直埋管道-聚氨酯发泡保温管厂家 - 唐山市吉祥保温工贸有限公司 | 圆窗水平仪|伊莉莎冈特elesa+ganter| 亚克力制品定制,上海嘉定有机玻璃加工制作生产厂家—官网 |