91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

Android中如何使用注解

發布時間:2021-06-29 14:35:11 來源:億速云 閱讀:186 作者:Leah 欄目:移動開發

Android中如何使用注解,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

首先什么是注解?@Override就是注解,它的作用是:

  • 檢查是否正確的重寫了父類中的方法。

  • 標明代碼,這是一個重寫的方法。

1、體現在于:檢查子類重寫的方法名與參數類型是否正確;檢查方法private/final/static等不能被重寫。實際上@Override對于應用程序并沒有實際影響,從它的源碼中可以出來。

Android中如何使用注解

2、主要是表現出代碼的可讀性。

Android中如何使用注解

作為Android開發中熟知的注解,Override只是注解的一種體現,更多時候,注解還有以下作用:

  • 降低項目的耦合度。

  • 自動完成一些規律性的代碼。

  • 自動生成java代碼,減輕開發者的工作量。

一、注解基礎快讀

1、元注解

元注解是由java提供的基礎注解,負責注解其它注解,如上圖Override被@Target和@Retention修飾,它們用來說明解釋其它注解,位于sdk/sources/android-25/java/lang/annotation路徑下。

元注解有:

  • @Retention:注解保留的生命周期

  • @Target:注解對象的作用范圍。

  • @Inherited:@Inherited標明所修飾的注解,在所作用的類上,是否可以被繼承。

  • @Documented:如其名,javadoc的工具文檔化,一般不關心。

@Retention

Retention說標明了注解被生命周期,對應RetentionPolicy的枚舉,表示注解在何時生效:

  • SOURCE:只在源碼中有效,編譯時拋棄,如上面的@Override。

  • CLASS:編譯class文件時生效。

  • RUNTIME:運行時才生效。

如下圖X1,com.android.support:support-annotations中的Nullable注解,會在編譯期判斷,被注解的參數是否會空,具體后續分析。

Android中如何使用注解

@Target

Target標明了注解的適用范圍,對應ElementType枚舉,明確了注解的有效范圍。

  • TYPE:類、接口、枚舉、注解類型。

  • FIELD:類成員(構造方法、方法、成員變量)。

  • METHOD:方法。

  • PARAMETER:參數。

  • CONSTRUCTOR:構造器。

  • LOCAL_VARIABLE:局部變量。

  • ANNOTATION_TYPE:注解。

  • PACKAGE:包聲明。

  • TYPE_PARAMETER:類型參數。

  • TYPE_USE:類型使用聲明。

如上圖X1所示,@Nullable可用于注解方法,參數,類成員,注解,包聲明中,常用例子如下所示:

/**   * Nullable表明   * bind方法的參數target和返回值Data可以為null   */  @Nullable   public static Data bind(@Nullable Context target) {    //do someThing and return    return bindXXX(target);  }

@Inherited

注解所作用的類,在繼承時默認無法繼承父類的注解。除非注解聲明了  @Inherited。同時Inherited聲明出來的注,只對類有效,對方法/屬性無效。

如下方代碼,注解類@AInherited聲明了Inherited ,而注解BNotInherited 沒有,所在在它們的修飾下:

  • 類Child繼承了父類Parent的@AInherited,不繼承@BNotInherited;

  • 重寫的方法testOverride()不繼承Parent的任何注解;

  • testNotOverride()因為沒有被重寫,所以注解依然生效。

@Retention(RetentionPolicy.RUNTIME)   @Inherited   public @interface AInherited {       String value();   }   @Retention(RetentionPolicy.RUNTIME)   public @interface BNotInherited {       String value();   }    @AInherited("Inherited")   @BNotInherited("沒Inherited")   public class Parent {        @AInherited("Inherited")       @BNotInherited("沒Inherited")       public void testOverride(){        }       @AInherited("Inherited")       @BNotInherited("沒Inherited")       public void testNotOverride(){     } }    /**   * Child繼承了Parent的AInherited注解   * BNotInherited因為沒有@Inherited聲明,不能被繼承   */ public class Child extends Parent {      /**    * 重寫的testOverride不繼承任何注解    * 因為Inherited不作用在方法上    */     @Override       public void testOverride() {       }      /**    * testNotOverride沒有被重寫    * 所以注解AInherited和BNotInherited依然生效。    */ }

2、自定義注解

2.1 運行時注解

了解了元注解后,看看如何實現和使用自定義注解。這里我們簡單介紹下運行時注解RUNTIME,編譯時注解CLASS留著后面分析。

首先,創建一個注解遵循: public @interface 注解名 {方法參數},如下方@getViewTo注解:

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

然后如下方所示,我們將注解描述在Activity的成員變量mTv和mBtn中,在App運行時,通過反射將findViewbyId得到的控件,注入到mTv和mBtn中。

是不是很熟悉,有點ButterKnife的味道?當然,ButterKnife比這個高級多,畢竟反射多了影響效率,不過我們明白了,可以通過注解來注入和創建對象,這樣可以在一定程度節省代碼量。

public class MainActivity extends AppCompatActivity {      @getViewTo(R.id.textview)     private TextView mTv;      @getViewTo(R.id.button)     private Button mBtn;      @Override     protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.activity_main);          //通過注解生成View;         getAllAnnotationView();     }      /**      * 解析注解,獲取控件      */     private void getAllAnnotationView() {         //獲得成員變量         Field[] fields = this.getClass().getDeclaredFields();          for (Field field : fields) {           try {             //判斷注解             if (field.getAnnotations() != null) {               //確定注解類型               if (field.isAnnotationPresent(GetViewTo.class)) {                 //允許修改反射屬性                 field.setAccessible(true);                 GetViewTo getViewTo = field.getAnnotation(GetViewTo.class);                 //findViewById將注解的id,找到View注入成員變量中                 field.set(this, findViewById(getViewTo.value()));               }             }           } catch (Exception e) {           }         }       }  }

2.2 編譯時注解

運行時注解RUNTIME如上2.1所示,大多數時候實在運行時使用反射來實現所需效果,這很大程度上影響效率,如果BufferKnife的每個View注入不可能如何實現。實際上,ButterKnife使用的是編譯時注解CLASS,如下圖X2.2,是ButterKnife的@BindView注解,它是一個編譯時注解,在編譯時生成對應java代碼,實現注入。

Android中如何使用注解

說到編譯時注解,就不得不說注解處理器  AbstractProcessor,如果你有注意,一般第三方注解相關的類庫,如bufferKnike、ARouter,都有一個Compiler命名的Module,如下圖X2.3,這里面一般都是注解處理器,用于編譯時處理對應的注解。

注解處理器(Annotation  Processor)是javac的一個工具,它用來在編譯時掃描和處理注解(Annotation)。你可以對自定義注解,并注冊相應的注解處理器,用于處理你的注解邏輯。

Android中如何使用注解

如下所示,實現一個自定義注解處理器,至少重寫四個方法,并且注冊你的自定義Processor,詳細可參考下方代碼CustomProcessor。

  • @AutoService(Processor.class),谷歌提供的自動注冊注解,為你生成注冊Processor所需要的格式文件(com.google.auto相關包)。

  • init(ProcessingEnvironment env),初始化處理器,一般在這里獲取我們需要的工具類。

  • getSupportedAnnotationTypes(),指定注解處理器是注冊給哪個注解的,返回指定支持的注解類集合。

  • getSupportedSourceVersion() ,指定java版本。

  • process(),處理器實際處理邏輯入口。

@AutoService(Processor.class) public class CustomProcessor extends AbstractProcessor {      /**      * 注解處理器的初始化      * 一般在這里獲取我們需要的工具類      * @param processingEnvironment 提供工具類Elements, Types和Filer      */     @Override     public synchronized void init(ProcessingEnvironment env){          super.init(env);         //Element代表程序的元素,例如包、類、方法。         mElementUtils = env.getElementUtils();          //處理TypeMirror的工具類,用于取類信息         mTypeUtils = env.getTypeUtils();           //Filer可以創建文件         mFiler = env.getFiler();          //錯誤處理工具         mMessages = env.getMessager();     }      /**      * 處理器實際處理邏輯入口      * @param set      * @param roundEnvironment 所有注解的集合      * @return       */     @Override     public boolean process(Set<? extends TypeElement> annoations,        RoundEnvironment env) {         //do someThing     }      //指定注解處理器是注冊給哪個注解的,返回指定支持的注解類集合。     @Override     public Set<String> getSupportedAnnotationTypes() {            Set<String> sets = new LinkedHashSet<String>();            //大部分class而已getName、getCanonicalNam這兩個方法沒有什么不同的。           //但是對于array或內部類等就不一樣了。           //getName返回的是[[Ljava.lang.String之類的表現形式,           //getCanonicalName返回的就是跟我們聲明類似的形式。           sets(BindView.class.getCanonicalName());            return sets;     }      //指定Java版本,一般返回***版本即可     @Override     public SourceVersion getSupportedSourceVersion() {         return SourceVersion.latestSupported();     }  }

首先,我們梳理下一般處理器處理邏輯:

  1. 遍歷得到源碼中,需要解析的元素列表。

  2. 判斷元素是否可見和符合要求。

  3. 組織數據結構得到輸出類參數。

  4. 輸入生成java文件。

  5. 錯誤處理。

然后,讓我們理解一個概念:Element,因為它是我們獲取注解的基礎。

Processor處理過程中,會掃描全部Java源碼,代碼的每一個部分都是一個特定類型的Element,它們像是XML一層的層級機構,比如類、變量、方法等,每個Element代表一個靜態的、語言級別的構件,如下方代碼所示。

package android.demo; // PackageElement  // TypeElement public class DemoClass {      // VariableElement     private boolean mVariableType;      // VariableElement     private VariableClassE m VariableClassE;      // ExecuteableElement     public DemoClass () {     }      // ExecuteableElement     public void resolveData (Demo data   //TypeElement ) {     } }

其中,Element代表的是源代碼,而TypeElement代表的是源代碼中的類型元素,例如類。然而,TypeElement并不包含類本身的信息。你可以從TypeElement中獲取類的名字,但是你獲取不到類的信息,例如它的父類。這種信息需要通過TypeMirror獲取。你可以通過調用elements.asType()獲取元素的TypeMirror。

1、知道了Element,我們就可以通過process  中的RoundEnvironment去獲取,掃描到的所有元素,如下圖X2.4,通過env.getElementsAnnotatedWith,我們可以獲取被@BindView注解的元素的列表,其中validateElement校驗元素是否可用。

Android中如何使用注解

2、因為env.getElementsAnnotatedWith返回的,是所有被注解了@  BindView的元素的列表。所以有時候我們還需要走一些額外的判斷,比如,檢查這些Element是否是一個類:

@Override   public boolean process(Set<? extends TypeElement> an, RoundEnvironment env) {     for (Element e : env.getElementsAnnotatedWith(BindView.class)) {       // 檢查元素是否是一個類       if (ae.getKind() != ElementKind.CLASS) {             ...       }    }    ... }

3、javapoet  (com.squareup:javapoet)是一個根據指定參數,生成java文件的開源庫,有興趣了解javapoet的可以看下javapoet&mdash;&mdash;讓你從重復無聊的代碼中解放出來,在處理器中,按照參數創建出  JavaFile之后,通Filer利用javaFile.writeTo(filer);就可以生成你需要的java文件。

4、錯誤處理,在處理器中,我們不能直接拋出一個異常,因為在process()中拋出一個異常,會導致運行注解處理器的JVM崩潰,導致跟蹤棧信息十分混亂。因此,注解處理器就有一個Messager類,一般通過messager.printMessage(  Diagnostic.Kind.ERROR, StringMessage, element)即可正常輸出錯誤信息。

至此,你的注解處理器完成了所有的邏輯。可以看出,編譯時注解實在編譯時生成java文件,然后將生產的java文件注入到源碼中,在運行時并不會像運行時注解一樣,影響效率和資源。

總結

我們就利用ButterKnife的流程,簡單舉例做個總結吧。

  1. @BindView在編譯時,根據Acitvity生產了XXXActivity$$ViewBinder.java。

  2. Activity中調用的ButterKnife.bind(this);,通過this的類名字,加$$ViewBinder,反射得到了ViewBinder,和編譯處理器生產的java文件關聯起來了,并將其存在map中緩存,然后調用ViewBinder.bind()。

  3. 在ViewBinder的bind方法中,通過id,利用ButterKnife的butterknife.internal.Utils工具類中的封裝方法,將findViewById()控件注入到Activity的參數中。

看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。

向AI問一下細節

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

AI

惠东县| 博客| 沭阳县| 永德县| 鹤壁市| 嘉祥县| 开原市| 昂仁县| 威海市| 宜君县| 友谊县| 会同县| 丹凤县| 张家口市| 灵宝市| 石泉县| 泾川县| 岢岚县| 巴青县| 廊坊市| 玉龙| 惠安县| 修水县| 宜章县| 兴山县| 枝江市| 奈曼旗| 前郭尔| 衡阳县| 周宁县| 且末县| 黄冈市| 龙江县| 布拖县| 调兵山市| 富川| 潮州市| 博湖县| 嘉祥县| 惠东县| 洱源县|