亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android APT如何實現控件注入框架SqInject的方法

發布時間:2021-03-15 11:09:33 來源:億速云 閱讀:280 作者:TREX 欄目:開發技術

本篇內容介紹了“Android APT如何實現控件注入框架SqInject的方法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

背景

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

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

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

//www.mlszssj.com/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);
 }
 } );
 }
}


“Android APT如何實現控件注入框架SqInject的方法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

敦煌市| 闵行区| 秭归县| 彩票| 页游| 囊谦县| 墨脱县| 绥阳县| 内黄县| 宿松县| 鲁甸县| 横峰县| 乌海市| 高州市| 北票市| 巴青县| 普陀区| 邵阳市| 得荣县| 紫云| 宁海县| 三台县| 天祝| 通城县| 花莲市| 灯塔市| 祁门县| 鄂托克旗| 芦溪县| 乌拉特中旗| 贵州省| 乳山市| 靖安县| 卓资县| 额敏县| 彭州市| 石台县| 科技| 荆州市| 广灵县| 永德县|