カスタムAndroid注解Part 1:注解変数


Androidの注釈には多少の接触がありますが、多くの人は他の依存ライブラリを使う時に接触していると信じています.いくつかの倉庫が使いたいなら、提供された注釈を使わなければならないからです.例えばButterKnife、Dagger 2、Roomなどです.
なぜ注を使うのですか?使ったことがあるものは全部知っています.一番明らかなのは便利で簡潔です.注釈を使うことによって、プロジェクトのコンパイル段階で、私達が自動的にいくつかの重複コードを生成するのを助けて、私達の負担を軽減します.典型的なButterKnifeの本質はAndroidの注釈を使っています.注釈を通して、view.findView ByIdに対する編纂を減らして、私達の開発効率を高めます.前のシリーズ(AAC)のRoomも同じです.簡単に振り返ってみます.
@Entity(tableName = "contacts")
data class ContactsModel(
        @PrimaryKey
        @ColumnInfo(name = "contacts_id")
        val id: Int,
        @ColumnInfo(name = "name")
        val name: String,
        @ColumnInfo(name = "phone")
        val phone: String
)
注を使用して、本体表を定義します.つまり、10行ぐらいのコードです.全部自分で書くなら、コードは二百行も必要です.その中で間違えたり、バグを変えたりするかもしれません.効率がひどく下がる.依存ライブラリにこんなに迷惑がかかると誰も使いません.
では、依存ライブラリはどうやって注釈を使う必要があるかどうかを判断しますか?実は簡単です.次の2点を覚えておけばいいです.
  • 生成が必要なコードは、プロジェクトロジックに関連していません.
  • Androidの注釈はコードしか生成できません.コードを変更することはできません.
    ここでは、Androidの注釈の本質はJavaの反射機構を使用しています.後で詳しく説明します.
    プロジェクトアーキテクチャ
    ButterKnifeは接触したことがあるべきでしょう.ないのも大丈夫です.今はちょうどその時です.以下はBindViewとOnClickの注釈を自分で実現し、ButterKnifeの対応注釈機能を実現します.まず全体のプロジェクト構造を見てみます.
    プロジェクト図を通して、私達ははっきりと見られます.主に三つの部分に分けられます.
  • butterknife-annotations:注釈ライブラリ、BindViewとOnClickなどのカスタム注釈
  • を含みます.
  • butterknife-bind:バインディングライブラリ、カスタム注釈と声明の種類バインディング
  • butterknife-compler:解析コンパイル生成ライブラリでは、ステートメントクラスの注釈を解析し、コンパイル時に自動的に該当コードを生成します.
  • Androidの注釈をより簡単に理解するために、今日の主な分析はbutterknife-annotationsという注釈庫です.みんなを連れて、注解変数を宣言します.
    BindView
    オープンソースライブラリbutterknifeのようなバインディングID効果を実現するために、ここでまずBindViewの注釈を定義します.具体的には以下の通りです.
    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.FIELD)
    public @interface BindView {
        @IdRes int[] value();
    }
    ええ、やはり簡単ですよね.5行のコードについてBindViewの注釈の定義を解決します.
    では、この5行のコードを詳しく分析します.
    Retension
    まず第一行コードのRetensionです.使い方を見れば、これも声明の注釈です.
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Retention {
        /**
         * Returns the retention policy.
         * @return the retention policy
         */
        RetentionPolicy value();
    }
    ソースコードを通して、私達はこの注釈がただ一つのパラメータを受け取ることを見抜きます.このパラメータはRetension Policyタイプです.我々はさらに深くRetenspolicyに入っています.
    public enum RetentionPolicy {
        /**
         * Annotations are to be discarded by the compiler.
         */
        SOURCE,
     
        /**
         * Annotations are to be recorded in the class file by the compiler
         * but need not be retained by the VM at run time.  This is the default
         * behavior.
         */
        CLASS,
     
        /**
         * Annotations are to be recorded in the class file by the compiler and
         * retained by the VM at run time, so they may be read reflectively.
         *
         * @see java.lang.reflect.AnnotatedElement
         */
        RUNTIME
    }
    ここで私達はそれが実は1つの列挙なことを発見して、列挙の中で3つの定数を支持して、それぞれSOURCE、CLASSとRUNTIMEです.それらの違いは主に作用の周期範囲です.次にこの三つの役割を翻訳します.
  • SOURCE:この表記を使った注釈はコンパイル段階で破棄されます.
  • CLASS:この表記を使った注釈はコンパイル段階で作成したクラスファイルに記録されますが、運行段階では時又はVMによって破棄されます.デフォルトはこのモードです.
  • RUNTIME:この表記を用いた注釈は、コンパイル段階で生成されたクラスファイルに保存され、同時に運転段階でVMに保存されます.したがって、この注釈は常に存在し、自然にjavaの反射機構によって読み取ることができる.
  • だから彼らの存在は常にSOURCE<CLASS<RUNTIME>である.その作用範囲を知ったら、私達はユーザー定義の注釈の時に、できるだけ小さい注釈の作用範囲を求めて、プロジェクトのコンパイルと運行速度を高めます.
    私たちのBindViewの注釈はViweのバインディングを行うためだけですので、コンパイル後は存在しないので、ここではCLASSを使って表記します.
    Target
    次は第二行コードを見に来ました.ここでもう一つの注釈Targetを使っています.やはりソースを見に来ました.
    @Documented
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.ANNOTATION_TYPE)
    public @interface Target {
        /**
         * Returns an array of the kinds of elements an annotation type
         * can be applied to.
         * @return an array of the kinds of elements an annotation type
         * can be applied to
         */
        ElementType[] value();
    }
    注のソースコードはとても簡単です.ここでElemenntType配列パラメータを受け取りました.ElementTypeはそのタイプも列挙です.
    public enum ElementType {
        /** Class, interface (including annotation type), or enum declaration */
        TYPE,
     
        /** Field declaration (includes enum constants) */
        FIELD,
     
        /** Method declaration */
        METHOD,
     
        /** Formal parameter declaration */
        PARAMETER,
     
        /** Constructor declaration */
        CONSTRUCTOR,
     
        /** Local variable declaration */
        LOCAL_VARIABLE,
     
        /** Annotation type declaration */
        ANNOTATION_TYPE,
     
        /** Package declaration */
        PACKAGE,
     
        /**
         * Type parameter declaration
         *
         * @since 1.8
         */
        TYPE_PARAMETER,
     
        /**
         * Use of a type
         *
         * @since 1.8
         */
        TYPE_USE
    }
    E lementTypeには10定数がありますが、実際に使っているのは前の8種類です.これらは、カスタマイズされた注釈が機能するオブジェクトを表します.それぞれ:
  • TYPE:クラス、インターフェースまたはエニュメレーション
  • に作用します.
  • FIELD:クラス内で宣言されたフィールドまたは列挙中の定数
  • METHOD:方法に作用する声明文
  • PARDER:パラメータ宣言文に作用する
  • CONSTRUCTOR:構造関数に作用する声明文の中の
  • LOCAL_VRIABLE:局所変数に作用する声明文
  • ANNOTATION_TYPE:注釈に作用する声明文の中で
  • PACKAGE:パケットに作用する声明文
  • TYPE_PAAMETER:java 1.8の後、タイプ宣言の文に作用する
  • TYPE_USE:java 1.8の後、使用タイプの任意の語句において、
  • 私たちのBindViewの役割を組み合わせると、Viewに対してidバインディングを行うことになります.だからBindViewでFIELDを使用しました.
    第四行コードを見てください.
    @IdRes int[] value()
    上記の注釈接触があって、BindViewが1つのintタイプを受信することを示す配列パラメータであることが分かります.オープンソースライブラリbutterknifeのBindViewはバインディングが必要なViewのIDを受信します.ここでは改版をして、Stringのidを受信して、バインディングのViewのためにデフォルト値を設定します.これで私たちがカスタマイズしたBindViewのコメントが完成しました.
    OClick
    次にOnClickでクリックしたコメントをカスタマイズします.上の分析を通して、RetentとTargetのそれぞれの値を頭の中で考えられますか?
    考えてから来ました.
    @Retention(RetentionPolicy.SOURCE)
    @Target(ElementType.METHOD)
    public @interface OnClick {
        @IdRes int value();
    }
    Retensionの役割範囲はBindViewと同じトップページのSOURCEです.コンパイル後は必要ありません.Targetの作用対象はBindViewと違って、クリックイベントのクリック操作である以上、当然操作ロジックの方法に作用するので、ここでMETHODを使用します.
    keep
    文章の冒頭で、本質は注釈によって自動的にコードを生成し、必要なクラスを作成すると述べました.実際の開発において、私達のプロジェクトが混同されると、自動的に作成されたクラスが失効し、ユーザー定義の注釈が失効することになります.そこで、故障を防ぐために、ここでもう一つの注釈keepを定義します.
    @Retention(RetentionPolicy.CLASS)
    @Target(ElementType.TYPE)
    public @interface Keep {
    }
    Retensの役割範囲はクラスファイルで他のクラスに呼び出されるので、ここでCLASSを使います.ターゲットは自動的に生成されるクラスですので、TYPEを使います.パラメータは必要ありません.クラスを明示するためだけに、混同されないようにします.
    締め括りをつける
    butterknife-annotationsライブラリのカスタムコメントが完成しました.上の分析を通して、私達の注意点は主に以下の3点にまとめられています.
  • Retension:注解作用を明確にする生命は長い間、早めに
  • を除去する.
  • Target:注釈の役割を明確にするオブジェクト
  • Keep:後続の自動生成を防ぐクラスが混同されている
  • 注解変数の定義はこれで終わります.文章中のコードは全部Githubで取得できます.使う時は枝をfeat_に切り替えてください.アンノテーションprocessing
    いい感じがしたら、いい思い出を作ってください.これは私の最大の支持です.ありがとうございます.
    関連記事
    カスタムAndroid注解Part 2:コード自動生成
    カスタムAndroid注解Part 3:バインディング
    関心を持つ
    個人ブログ