溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

3000行代碼怎樣簡(jiǎn)化成300行?來,一文來教你!

發(fā)布時(shí)間:2020-08-04 15:03:02 來源:ITPUB博客 閱讀:166 作者:yilian 欄目:移動(dòng)開發(fā)

前言

APT(Annotation Processor Tool)是用來處理注解的,即注解處理器。 APT 在編譯器會(huì)掃描處理源代碼中的注解,我們可以使用這些注解,然后利用  APT自動(dòng)生成  Java代碼,減少模板代碼,提升編碼效率,使源碼更加簡(jiǎn)潔,可讀性更高。

1、具體場(chǎng)景

下面我將會(huì)以項(xiàng)目中常見的 intent 頁面跳轉(zhuǎn)為例,給大家演示一下,如何自動(dòng)生成  intent代碼,以及對(duì) getIntent的參數(shù)自動(dòng)賦值。

要實(shí)現(xiàn)上面這個(gè)功能我們需要了解  APT、以及 JavaPoet。如果不太了解的同學(xué)可以先去了解一下。

常用寫法

  Intent intent = new Intent(this,OtherActivity.class);
  intent.putExtra("name",name);
  intent.putExtra("gender",gender);
  startActivity(intent);

數(shù)據(jù)獲取

  String name = getIntent().getStringExtra("name",name);  String gender = getIntent().getStringExtra("gender",gender);

上述代碼很必要但重復(fù)性又很高,寫多了會(huì)煩,又浪費(fèi)時(shí)間。并且在數(shù)據(jù)傳遞與獲取時(shí)  key 值都需要保持一致,這又需要我們新建很多的常量。所以,這里我們希望上述的數(shù)據(jù)傳遞與獲取可以自動(dòng)生成。

為了實(shí)現(xiàn)這個(gè)需求,我們需要實(shí)現(xiàn)如下功能:
1)自動(dòng)為  OtherActivity類生成一個(gè)叫做  OtherActivityAutoBundle 的類
2)使用建造者模式為變量賦值
3)支持  startActivity 或  startActivityForResult 跳轉(zhuǎn)
4)支持調(diào)用一個(gè)方法即可解析  Intent 傳遞的數(shù)據(jù),并賦值給跳轉(zhuǎn)的  Activity 中的變量

我們需要自動(dòng)化如下代碼:

  new OtherActivityAutoBundle()
          .name("小明")
          .gender("男")
          .start(this);//或 startActivityForResult(this,requestCode)

在 OtherActivity 中,自動(dòng)為變量賦值:

  new OtherActivityAutoBundle().bindIntentData(this,getIntent());

2、搭建 APT 項(xiàng)目

a、創(chuàng)建一個(gè) Java Library,并創(chuàng)建注解類
例如:

  @Target(ElementType.FIELD)  @Retention(RetentionPolicy.CLASS)  public @interface AutoBundle {      boolean exclude() default false;//不參與 intent、bundle 傳值
      boolean addFlags() default false;//添加 activity 啟動(dòng)方式
      boolean isCloseFromActivity() default false;//是否關(guān)閉 FromActivity
      boolean isBundle() default false;//是否使用 Bundle 對(duì)象傳值
      boolean isSerializable() default false;//是否是 Serializable 類型
      boolean isParcelable() default false;//是否是 Parcelable 類型
      boolean isParcelableArray() default false;//是否是 ParcelableArray 類型
      boolean isParcelableArrayList() default false;//是否是 ParcelableArrayList 類型
  }

b、再創(chuàng)建一個(gè) Java Library,并將上一步 Java Library 添加進(jìn)來

此時(shí),我們還需要在該 Library 中創(chuàng)建  resources 文件夾;接著在  resources 中創(chuàng)建  META-INF 和  services 兩個(gè)文件夾;然后在  services 中創(chuàng)建一個(gè)名為  javax.annotation.processing.Processor 的文件。最后在該文件中寫入我們注解處理器的全路徑。

這里我們也可以使用自動(dòng)化工具  implementation 'com.google.auto.service:auto-service:1.0-rc2' 感興趣的去搜一下具體用法

3000行代碼怎樣簡(jiǎn)化成300行?來,一文來教你!

3、創(chuàng)建自己的處理類,繼承 AbstractProcessor

  public class AutoBundleProcessor extends AbstractProcessor {
  }

在創(chuàng)建 AutoBundleProcessor 后,我們需要重寫幾個(gè)方法

   @Override
   public synchronized void init(ProcessingEnvironment ev) {
   }

在編譯開始時(shí)首先會(huì)回調(diào)此方法,在這里,我們可以獲取一些實(shí)例為后面做準(zhǔn)備。

  @Override
  public boolean process(Set<? extends TypeElement> set, RoundEnvironment rev) {
  }

在該方法中,我們能夠獲取需要的類、變量、注解等相關(guān)信息,后面我們會(huì)利用這些來生成代碼

  @Override
  public Set<String> getSupportedAnnotationTypes() {
  }

該方法中我們可以指定具體需要處理哪些注解

接著我們需要使用到  Elements、  Filer、 Name、 TypeMirror 對(duì)象
Elements:對(duì)  Element 對(duì)象進(jìn)行操作
Filer:文件操作接口,它可以創(chuàng)建  Java 文件
Name:表示類名、方法名
TypeMirror:表示數(shù)據(jù)類型。如  intString、以及自定義數(shù)據(jù)類型
下面我們可以獲取被  @AutoBundle 注解元素的相關(guān)信息:

  Set<? extends Element> elementsAnnotatedWith = 
  rev.getElementsAnnotatedWith(AutoBundle.class);  for (Element element : elementsAnnotatedWith) {       if (element.getKind() == ElementKind.FIELD) {
                  VariableElement variableElement = (VariableElement) element;
                  TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();                  //類名
                  String className = typeElement.getSimpleName().toString();                  //包名
                  String packageName = 
  mElementUtils.getPackageOf(typeElement).getQualifiedName().toString();
                  AutoBundle autoBundle = variableElement.getAnnotation(AutoBundle.class);                  //變量名
                  Name simpleName = variableElement.getSimpleName();                  //變量類型
                  TypeMirror typeMirror = variableElement.asType();
        }
   }

例如:

變量: gendertype:java.lang.String

其他變量亦是如此。

現(xiàn)在我們需要新建類來保存上面獲取的值。這里我們新建  FieldHolder 來保存變量類型、變量名以及其他信息。
FieldHolder

  public class FieldHolder {      private String variableName;//變量名
      private TypeMirror clazz;//字段類型(如:String)
      private String packageName;//包名
      private boolean addFlags;//是否是添加 activity 啟動(dòng)方式
      private boolean exclude;//是否參與 intent、bundle 傳值
      private boolean closeFromActivity;//是否關(guān)閉當(dāng)前 Activity
      private boolean isBundle;//是否使用 Bundle 傳值
      private boolean isSerializable;//是否實(shí)現(xiàn) Serializable 接口的類
      private boolean isParcelable;//是否是自定義類實(shí)現(xiàn) Parcelable 接口
      private boolean isParcelableArray;//是否是自定義類 ParcelableArray 類型
      private boolean isParcelableArrayList;//是否是自定義類 ParcelableArrayList 類型
  }

4、下面我們需要使用 JavaPoet 生成 Java 文件

簡(jiǎn)單介紹下需要用到的 API

A、TypeSpec.Builder

主要用于生成類,這里的類包括的范圍比較廣,可以是一個(gè)  class、一個(gè)  interface 等等。

方法 功能
classBuilder 生成類
interfaceBuilder 生成接口

B、MethodSpec.Builder

主要用于生成類

方法 功能
constructBuilder 生成構(gòu)造方法
methodBuilder 生成成員方法

C、FieldSpec.Builder

主要用于生成成員變量

方法 功能
builder 生成一個(gè)成員變量

D、JavaFile.Builder

主要用來生成 Java 文件

方法 功能
builder 生成一個(gè) JavaFile 對(duì)象
writeTo 將數(shù)據(jù)寫到 Java 文件中

E、其他方法

方法 功能 描述
addModifier 添加修飾符 比如:public、private、static 等等
addParameter 添加參數(shù) 向方法中添加參數(shù)。例:addParameter(ClassName.get("包名"),"類名")
addStatement 添加陳述 直接添加代碼。例:addStatement("return this")
addCode 添加代碼語句 直接添加代碼,自動(dòng)幫你導(dǎo)入需要的包,并在末尾自動(dòng)添加分號(hào)
returns 添加返回值 為方法添加返回值。例:returns(void.class)
addMethod 添加方法 將生成的方法添加到類中。例:addMethod(customMethod.build())
addField 添加變量 將生成的變量添加到類中。例:addField(customField.build())

生成成員變量以及變量的 set 方法

  TypeSpec.Builder typeClass = TypeSpec.classBuilder(clazzName + "AutoBundle");  for (FieldHolder fieldHolder : fieldHolders) {
         packageName = fieldHolder.getPackageName();
         FieldSpec builder = FieldSpec.builder(ClassName.get(fieldHolder.getClazz()), 
  fieldHolder.getVariableName(), Modifier.PRIVATE).build();
         typeClass.addField(builder);
         MethodSpec.Builder buildParamMethod = MethodSpec.methodBuilder(String.format("%s", 
  fieldHolder.getVariableName()));
         buildParamMethod.addParameter(ClassName.get(fieldHolder.getClazz()), 
  fieldHolder.getVariableName());
         buildParamMethod.addStatement(String.format("this.%s=%s", fieldHolder.getVariableName(), 
  fieldHolder.getVariableName()));
         buildParamMethod.addStatement(String.format("return %s", "this"));
         buildParamMethod.addModifiers(Modifier.PUBLIC);
         buildParamMethod.returns(ClassName.get(fieldHolder.getPackageName(), clazzName +  "AutoBundle"));
         typeClass.addMethod(buildParamMethod.build());
  }

生成的代碼:

  public class OtherActivityAutoBundle {
    private String name;    private String gender;    public OtherActivityAutoBundle name(String name) {        this.name = name;        return this;
    }    public OtherActivityAutoBundle gender(String gender) {        this.gender = gender;        return this;
    }
  }

生成 start 方法

  private void generateCommonStart(MethodSpec.Builder builderMethod, List<FieldHolder> 
  fieldHolders, String clazzName) {
          builderMethod.addStatement(String.format("Intent intent = new Intent(context,%s.class)", clazzName));          /** 生成頁面跳轉(zhuǎn)方法 */
          for (FieldHolder fieldHolder : fieldHolders) {
              String fieldType = fieldHolder.getClazz().toString();              if ("android.os.Bundle".equals(fieldType)) {
                  builderMethod.addStatement(String.format("Bundle %s = new Bundle()", fieldHolder.getVariableName()));
              builderMethod.addStatement(String.format("intent.putExtra(\"%s\",%s)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  mAutoBundleField = fieldHolder.getVariableName();
              } else if (fieldHolder.isBundle() && String.class.getName().equals(fieldType)) {
              builderMethod.addStatement(String.format("%s.putString(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((boolean.class.getName().equals(fieldType) || Boolean.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
            builderMethod.addStatement(String.format("%s.putBoolean(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((byte.class.getName().equals(fieldType) || Byte.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                  builderMethod.addStatement(String.format("%s.putByte(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((char.class.getName().equals(fieldType) || Character.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                  builderMethod.addStatement(String.format("%s.putChar(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((short.class.getName().equals(fieldType) || Short.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                 builderMethod.addStatement(String.format("%s.putShort(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((int.class.getName().equals(fieldType) || Integer.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                  builderMethod.addStatement(String.format("%s.putInt(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((long.class.getName().equals(fieldType) || Long.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                  builderMethod.addStatement(String.format("%s.putLong(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((float.class.getName().equals(fieldType) || Float.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                  builderMethod.addStatement(String.format("%s.putFloat(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } else if ((double.class.getName().equals(fieldType) || Double.class.getName().equals(fieldType)) && fieldHolder.isBundle()) {
                 builderMethod.addStatement(String.format("%s.putDouble(\"%s\",%s)", mAutoBundleField, fieldHolder.getVariableName(), fieldHolder.getVariableName()));
              } 
  }

結(jié)果

  public void start(Context context) {
          Intent intent = new Intent(context, OtherActivity.class);
          intent.putExtra("id", id);
          intent.putExtra("name", name);
          intent.putExtra("is", is);
          intent.putExtra("mByte", mByte);
          intent.putExtra("b", b);
          intent.putExtra("mShort", mShort);
          intent.putExtra("mLong", mLong);
          intent.putExtra("mFloat", mFloat);
          intent.putExtra("mDouble", mDouble);
          context.startActivity(intent);
    }

生成 bindIntentData

  for (FieldHolder fieldHolder : fieldHolders) {
                  packageName = fieldHolder.getPackageName();
                  TypeMirror clazz = fieldHolder.getClazz();
                  String fieldType = clazz.toString();                  if ((boolean.class.getName().equals(fieldType) || Boolean.class.getName().equals(fieldType)) && !fieldHolder.isBundle()&&!fieldHolder.isExclude()) {
                      bindIntentMethod.addStatement(String.format("target.%s = intent.getBooleanExtra(\"%s\",false)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((byte.class.getName().equals(fieldType) || Byte.class.getName().equals(fieldType)) && !fieldHolder.isBundle()) {
                      bindIntentMethod.addStatement(String.format("target.%s = intent.getByteExtra(\"%s\",(byte)0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((char.class.getName().equals(fieldType) || Character.class.getName().equals(fieldType)) && !fieldHolder.isBundle()) {
                      bindIntentMethod.addStatement(String.format("target.%s = intent.getCharExtra(\"%s\",(char)0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((short.class.getName().equals(fieldType) || Short.class.getName().equals(fieldType)) && !fieldHolder.isBundle()) {
                      bindIntentMethod.addStatement(String.format("target.%s = intent.getShortExtra(\"%s\",(short)0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((int.class.getName().equals(fieldType) || Integer.class.getName().equals(fieldType)) && !fieldHolder.isBundle()&&!fieldHolder.isExclude()) {
                      bindIntentMethod.addStatement(String.format("target.%s=intent.getIntExtra(\"%s\",0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((long.class.getName().equals(fieldType) || Long.class.getName().equals(fieldType)) && !fieldHolder.isBundle()) {
                     bindIntentMethod.addStatement(String.format("target.%s=intent.getLongExtra(\"%s\",0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((float.class.getName().equals(fieldType) || Float.class.getName().equals(fieldType)) && !fieldHolder.isBundle()) {
                    bindIntentMethod.addStatement(String.format("target.%s=intent.getFloatExtra(\"%s\",0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } else if ((double.class.getName().equals(fieldType) || Double.class.getName().equals(fieldType)) && !fieldHolder.isBundle()) {
                 bindIntentMethod.addStatement(String.format("target.%s=intent.getDoubleExtra(\"%s\",0)", fieldHolder.getVariableName(), fieldHolder.getVariableName()));
                  } 
  }

生成的結(jié)果

  public void bindIntentData(OtherActivity target, Intent intent) {
          target.id = intent.getIntExtra("id", 0);
          target.name = intent.getStringExtra("name");
          target.is = intent.getBooleanExtra("is", false);
          target.mByte = intent.getByteExtra("mByte", (byte) 0);
          target.b = intent.getCharExtra("b", (char) 0);
          target.mShort = intent.getShortExtra("mShort", (short) 0);
          target.mLong = intent.getLongExtra("mLong", 0);
          target.mFloat = intent.getFloatExtra("mFloat", 0);
          target.mDouble = intent.getDoubleExtra("mDouble", 0);
  }

最后將生成好的 Java 代碼寫入文件

   //與目標(biāo) Class 放在同一個(gè)包下,解決 Class 屬性的可訪問性
   JavaFile javaFile = JavaFile.builder(packageName, typeClass.build())
             .build();    try {           //生成 class 文件
           javaFile.writeTo(mFiler);
         } catch (IOException e) {
             e.printStackTrace();
       }

生成的文件在  app/build/generated/ap_generated_sources/debug/out/包名/xxx

3000行代碼怎樣簡(jiǎn)化成300行?來,一文來教你!

最后生成的代碼:

  /**
   * This codes are generated automatically. Do not modify! */
  public class ThirdActivityAutoBundle {    private int no;    private String address;    private boolean isChoose;    public ThirdActivityAutoBundle no(int no) {      this.no=no;      return this;
    }    public ThirdActivityAutoBundle address(String address) {      this.address=address;      return this;
    }    public ThirdActivityAutoBundle isChoose(boolean isChoose) {      this.isChoose=isChoose;      return this;
    }    public void start(Context context) {
      Intent intent = new Intent(context,ThirdActivity.class);
      intent.putExtra("no",no);
      intent.putExtra("address",address);
      intent.putExtra("isChoose",isChoose);
      context.startActivity(intent);
    }    public void startForResult(Context context, Integer requestCode) {
      Intent intent = new Intent(context,ThirdActivity.class);
      intent.putExtra("no",no);
      intent.putExtra("address",address);
      intent.putExtra("isChoose",isChoose);
      ((android.app.Activity)context).startActivityForResult(intent,requestCode);
    }    public void bindIntentData(ThirdActivity target, Intent intent) {
      target.no=intent.getIntExtra("no",0);
      target.address=intent.getStringExtra("address");
      target.isChoose = intent.getBooleanExtra("isChoose",false);
    }
  }
總結(jié)

好了,到這里就全部結(jié)束了,大家可以發(fā)揮想象力添加自己想要的功能。
喜歡的話,點(diǎn)個(gè)贊唄

作為一個(gè)程序員,要學(xué)的東西有很多,而學(xué)到的知識(shí)點(diǎn),都是錢(因?yàn)榧夹g(shù)人員大部分情況是根據(jù)你的能力來定級(jí)、來發(fā)薪水的),技多不壓身。

為了很好的生活,我們要多多學(xué)習(xí),增加我們手里的金錢。尤其經(jīng)歷了這一疫情,我深深的感受到金錢的重要,所以,我們一定不能停下學(xué)習(xí)的腳步!

附上我的Android核心技術(shù)學(xué)習(xí)大綱,獲取相關(guān)內(nèi)容來GitHub: https://github.com/Meng997998/AndroidJX
vx:xx13414521

3000行代碼怎樣簡(jiǎn)化成300行?來,一文來教你!
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI