Android注解小結(jié)

本篇學(xué)習的目的是擼一個編譯時注解框架:

1、本篇只是學(xué)習小結(jié),需要詳細的講解可參考:

2、基礎(chǔ)小結(jié)

  • 元注解:
    元注解是由java提供的基礎(chǔ)注解,負責注解其它注解。
    元注解有:

    @Retention:注解保留的生命周期
    @Target:注解對象的作用范圍。
    @Inherited:@Inherited標明所修飾的注解,在所作用的類上,是否可以被繼承。
    @Documented:如其名,javadoc的工具文檔化,一般不關(guān)心。
    
  • @Retention:
    Retention說標明了注解被生命周期,對應(yīng)RetentionPolicy的枚舉,表示注解在何時生效:

    SOURCE:只在源碼中有效,編譯時拋棄,如@Override。
    CLASS:編譯class文件時生效。
    RUNTIME:運行時才生效。
    
  • @Target:
    Target標明了注解的適用范圍,對應(yīng)ElementType枚舉,明確了注解的有效范圍。

    TYPE:類、接口、枚舉、注解類型。
    FIELD:類成員(構(gòu)造方法、方法、成員變量)。
    METHOD:方法。
    PARAMETER:參數(shù)。
    CONSTRUCTOR:構(gòu)造器。
    LOCAL_VARIABLE:局部變量。
    ANNOTATION_TYPE:注解。
    PACKAGE:包聲明。
    TYPE_PARAMETER:類型參數(shù)。
    TYPE_USE:類型使用聲明。
    
  • @Inherited
    注解所作用的類,在繼承時默認無法繼承父類的注解。除非注解聲明了 @Inherited。同時Inherited聲明出來的注解,只對類有效,對方法/屬性無效。

3、先擼一個運行時注解,直接上代碼:
注解類:

  @Target({ElementType.FIELD})     
  @Retention(RetentionPolicy.RUNTIME)
  public @interface BindView {
      int value() default -1;
  }

運用例子:

  public class MainActivity extends AppCompatActivity {

      @BindView(R.id.tv)
      private TextView mView;

      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          getAllAnnotationView();
          mView.setText("注解成功");
      }
      /**
       * 解析注解,獲取控件
       */
      private void getAllAnnotationView() {
          //獲得成員變量
          Field[] fields = this.getClass().getDeclaredFields();

          for (Field field : fields) {
              try {
                  //判斷注解
                  if (field.getAnnotations() != null) {
                      //確定注解類型
                      if (field.isAnnotationPresent(BindView.class)) {
                          //允許修改反射屬性
                          field.setAccessible(true);
                          BindView getViewTo = field.getAnnotation(BindView.class);
                          //findViewById將注解的id,找到View注入成員變量中
                          field.set(this, findViewById(getViewTo.value()));
                      }
                  }
              } catch (Exception e) {
            }
          }
      }
  }

4、編譯時注解:
運行時注解大多使用反射,這會影響效率,BufferKnife不會這樣做,BufferKnife使用的是編譯時注解,在編譯時生成對應(yīng)的java代碼,實現(xiàn)注入。下面就一步步擼一個編譯時注解:

  • 首先,準備三個Module:

    bindView-annotation:用于存放注解等,Java模塊(創(chuàng)建時選javalib)
    bindView-compiler:用于編寫注解處理器,Java模塊
    bindView-annotation:用于給用戶提供使用的API,Android模塊
    
  • 注解模塊的實現(xiàn)(bindView-annotation):

    @Target({ElementType.FIELD})
    @Retention(RetentionPolicy.CLASS)
    public @interface BindView {
        int value() default -1;
    }
    
  • 注解處理器的實現(xiàn)(bindView-compiler):
    先添加依賴:compile 'com.google.auto.service:auto-service:1.0-rc2'

  • 然后創(chuàng)建類繼承AbstractProcessor,找不到這個類的看Module是不是javalib:

    public class BindViewProcessor extends AbstractProcessor {
    
        private Filer mFileUtils;
        private Elements mElementUtils;
        private Messager mMessager;
    
        /**
         *  初始化一些工具。Elements幾個子類:VariableElement //一般代表成員變量
         *                                ExecutableElement //一般代表類中的方法
         *                                TypeElement //一般代表代表類
         *                                PackageElement //一般代表Package
         *   固定的寫法
         */
        @Override
        public synchronized void init(ProcessingEnvironment processingEnvironment) {
            super.init(processingEnvironment);
            mFileUtils = processingEnvironment.getFiler();    //跟文件相關(guān)的輔助類,生成JavaSourceCode。
            mElementUtils = processingEnvironment.getElementUtils();  //跟元素相關(guān)的輔助類,幫助我們?nèi)カ@取一些元素相關(guān)的信息。
            mMessager = processingEnvironment.getMessager();  //跟日志相關(guān)的輔助類。
        }
        /**
         *  返回注解類型, 固定的寫法
         */
        @Override
        public Set<String> getSupportedAnnotationTypes(){
            Set<String> annotationTypes = new LinkedHashSet<String>();
            annotationTypes.add(BindView.class.getCanonicalName());
            return annotationTypes;
        }
        /**
         *  返回支持的源碼版本,固定的寫法
         */
        @Override
        public SourceVersion getSupportedSourceVersion(){
           return SourceVersion.latestSupported();
        }
    
        /**
         *  核心的方法
         */
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
            return false;
        }
     }
    
  • process先不寫,先寫一個代理類ProxyInfo:

    /**
     * 用來保存每個類的所有注解信息
     */
    public class ProxyInfo {
    
        private String packageName;       //包名
        private String proxyClassName;    //代理類名
        private TypeElement typeElement;    //代表被代理的類
    
        public Map<Integer, VariableElement> injectVariables = new HashMap<>();  //存被注解的成員變量
    
        public static final String PROXY = "OnBindView";  //接口名,對應(yīng)API模塊
    
        public ProxyInfo(Elements elementUtils, TypeElement typeElement) {
            this.typeElement = typeElement;
            PackageElement packageElement =           elementUtils.getPackageOf(typeElement);   //通過元素工具從類元素中拿到包元素
            String packageName = packageElement.getQualifiedName().toString();  //通過包元素拿到包名
            int packageLen = packageName.length()+1;  //包名長度加1
            String className   //getQualifiedName()拿到的是com.test.MainActivity,這一步就拿到MainActivity,后面的replace是內(nèi)部類的情況
              = typeElement.getQualifiedName().toString().substring(packageLen).replace(".","$");
            this.packageName = packageName;
            this.proxyClassName = className + "$$" + PROXY;   //代理類名
        }
    
        /**
         * 寫代理類
         * @return
         */
        public String generateJavaCode() {
            StringBuilder builder = new StringBuilder();
            builder.append("http:// Generated code. Do not modify!\n");
            builder.append("package ").append(packageName).append(";\n\n");
            builder.append("import com.bindview.*;\n");
            builder.append('\n');
    
            builder.append("public class ").append(proxyClassName).append(" implements " + ProxyInfo.PROXY + "<" + typeElement.getQualifiedName() + ">");
            builder.append(" {\n");
    
            generateMethods(builder);
            builder.append('\n');
    
            builder.append("}\n");
            return builder.toString();
        }
    
        /**
         * 寫findviewbyID
         * @return
         */
        private void generateMethods(StringBuilder builder) {
    
            builder.append("@Override\n ");
            builder.append("public void inject(" + typeElement.getQualifiedName() + " host, Object source ) {\n");
    
            for (int id : injectVariables.keySet()) {
                VariableElement element = injectVariables.get(id);
                String name = element.getSimpleName().toString();
                String type = element.asType().toString();
                builder.append(" if(source instanceof android.app.Activity){\n");
                builder.append("host." + name).append(" = ");
                builder.append("(" + type + ")(((android.app.Activity)source).findViewById( " + id + "));\n");
                builder.append("\n}else{\n");
                builder.append("host." + name).append(" = ");
                builder.append("(" + type + ")(((android.view.View)source).findViewById( " + id + "));\n");
                builder.append("\n};");
            }
            builder.append("  }\n");
        }
    
        public String getProxyClassFullName() {
            return packageName + "." + proxyClassName;
        }
    
        public TypeElement getTypeElement() {
            return typeElement;
        }
    }
    
  • 現(xiàn)在看process的代碼:

      private Map<String, ProxyInfo> mProxyMap = new HashMap<String, ProxyInfo>();
    
        @Override
        public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
      //------------------------收集信息----------------------------
            mProxyMap.clear();
            Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);  //收集被注解的元素
            for (Element element : elements) {
                checkAnnotationValid(element, BindView.class);  //檢查element類型,比如只能修飾成員變量
                VariableElement variableElement = (VariableElement) element;
                //class type
                TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
                //full class name
                String fqClassName = classElement.getQualifiedName().toString();
    
                ProxyInfo proxyInfo = mProxyMap.get(fqClassName);
                if (proxyInfo == null) {
                    proxyInfo = new ProxyInfo(mElementUtils, classElement);
                    mProxyMap.put(fqClassName, proxyInfo);
                }
    
                BindView bindAnnotation = variableElement.getAnnotation(BindView.class);
                int id = bindAnnotation.value();
                proxyInfo.injectVariables.put(id, variableElement);
            }
    
            //-------------寫代理類--------------------
            for (String key : mProxyMap.keySet()) {
                ProxyInfo proxyInfo = mProxyMap.get(key);
                try {
                    JavaFileObject jfo = processingEnv.getFiler().createSourceFile(
                            proxyInfo.getProxyClassFullName(),
                            proxyInfo.getTypeElement());
                    Writer writer = jfo.openWriter();
                    writer.write(proxyInfo.generateJavaCode());
                    writer.flush();
                    writer.close();
                } catch (IOException e) {
                    error(proxyInfo.getTypeElement(),
                            "Unable to write injector for type %s: %s",
                            proxyInfo.getTypeElement(), e.getMessage());
                }
    
            }
            return true;
        }
    
        private boolean checkAnnotationValid(Element annotatedElement, Class clazz) {
            if (annotatedElement.getKind() != ElementKind.FIELD) {
                error(annotatedElement, "%s must be declared on field.", clazz.getSimpleName());
                return false;
            }
            if (annotatedElement.getModifiers().contains(Modifier.PRIVATE)) {
                error(annotatedElement, "%s() must can not be private.", annotatedElement.getSimpleName());
                return false;
            }
    
            return true;
        }
    
     private void error(Element element, String message, Object... args) {
            if (args.length > 0) {
                message = String.format(message, args);
            }
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, message, element);
        }
    
  • compiler模塊寫完,輪到API模塊了:

      public interface OnBindView<T> {
          void inject(T t, Object source);
      }
    
      public class BindView {
          private static final String SUFFIX = "$$OnBindView";
    
          public static void injectView(Activity activity) {
              OnBindView proxyActivity = findProxyActivity(activity);
              proxyActivity.inject(activity, activity);
          }
    
          public static void injectView(Object object, View view) {
              OnBindView proxyActivity = findProxyActivity(object);
              proxyActivity.inject(object, view);
          }
    
          private static OnBindView findProxyActivity(Object activity) {
              try {
                  Class clazz = activity.getClass();
                  Class injectorClazz = Class.forName(clazz.getName() + SUFFIX);
                  return (OnBindView) injectorClazz.newInstance();
              } catch (ClassNotFoundException e) {
                  e.printStackTrace();
              } catch (InstantiationException e) {
                  e.printStackTrace();
              } catch (IllegalAccessException e) {
                e.printStackTrace();
              }
              throw new RuntimeException(String.format("can not find %s , something when compiler.", activity.getClass().getSimpleName() + SUFFIX));
          }
      }
    
  • 最后,使用:

      public class MainActivity extends AppCompatActivity {
          @Bind(R.id.id_textview)
           TextView mTv;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_fragment);
           ViewInjector.injectView(this);
      }
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容