Android学会注釈フレームワークを書く

15205 ワード

ButterknifeとRetrofitを使用すると、注釈という概念に遭遇します.しかし、以前は使うために使われていたので、知っていましたが、その理由が分かりませんでした.
本文ではfindViewById(int)プロセスを簡略化する注釈を書くことで学習する.
まず注釈のフレームワークを書く前に反射と注釈が必要な知識を学び、ここではまず私たちが使う必要があることを簡単に紹介し、後で詳しく研究してから補充します.
反射(Reflection)
定義:反射メカニズムとは、実行状態でどのクラスに対しても、このクラスのすべての属性と方法を知ることができることを意味します.どのオブジェクトに対しても、そのメソッドとプロパティを呼び出すことができます.このように新しいオブジェクトメソッドとダイナミック呼び出しオブジェクトメソッドを動的に取得する機能を反射と呼ぶ.
一般的に、反射によってクラスの情報を取得するには、主に次の方法があります.
  • Class
  • を取得
    // String   
    //   :
    Class c = Class.forName("java.lang.String");  //           
    //   :
    Class c1=String.class;
    //   :
    String str = new String();
    Class c2=str.getClass();

    ここで取得したc,c 1,c 2はいずれも等しい.最初の書き方はよくあります.
  • クラスの属性(メンバー変数)
  • を取得
    Field[] fields = c.getDeclaredFields();

    ここでは、すべてのプロパティを含む配列を返します.取得した各プロパティFiledには、一連の方法でコンテンツを取得および変更できる方法が含まれています.
  • クラスを取得する方法
  • //        
    Method[] mt = c.getDeclaredMethods();

    などの例では、以上の3つの方法が用いられます.
    注記(Annotation)
    一、メタ注記:
    メタ注釈の役割は、他の注釈を注釈することです.Java 5.0は、他のannotationタイプについて説明するために使用される4つの標準的なmeta-annotationタイプを定義します.Java 5.0定義のメタ注記:
    @Target:
    役割:注釈を記述するための使用範囲(すなわち、記述された注釈がどこで使用できるか)の値(ElementType)は、次のとおりです.
    ElementType.CONSTRUCTOR
    エネルギー修飾コンストラクタ
    ElementType.LOCAL_VARIABLE
    ローカル変数を修飾できる
    ElementType.ANNOTATION_TYPE
    注釈を修飾できる
    ElementType.PACKAGE
    修飾できるバッグ
    ElementType.PARAMETER
    エネルギー修飾パラメータ
    ElementType.METHOD
    エネルギー修飾方法
    ElementType.FIELD
    修飾可能メンバー変数
    ElementType.TYPE
    クラス、インタフェース、または列挙タイプを修飾できます.
    @Retention:
    役割:注釈情報を保存するレベルを示します.注釈のライフサイクル(説明された注釈がどの範囲で有効か)を記述するには、次の3つの値があります.
    RetentionPolicy.SOURCE
    ソースコードにのみ保持され、一般的にはコードの理解性を高めたり、コードチェックを助けたりするために使用されます.例えば、私たちのOverrideなどです.
    RetentionPolicy.CLASS
    デフォルトの選択では、コンパイル後のバイトコードclassファイルに注釈を残すことができ、バイトコードファイルにのみ、実行時には得られません.
    RetentionPolicy.RUNTIME
    注記はclassバイトコードファイルに保持されるだけでなく、実行中に反射によって取得されることもよくあります.
    @Documented
    @Documentedは、他のタイプのannotationを記述するために、注釈されたプログラムメンバーの共通APIとして使用されるべきであり、javadocのようなツールでドキュメント化することができる.Documentedはタグ注記で、メンバーはいません.
    @Inherited
    @Inheritedメタ注釈はタグ注釈であり、@Inheritedはある注釈のタイプが継承されていることを述べている.@Inherited修飾を使用したannotationタイプがclassに使用される場合、このannotationはclassのサブクラスに使用されます.これらのタイプとサポートされているクラスはjava.lang.annotationパッケージで見つかります.
    二、カスタム注釈:
    @interfaceを使用して注釈をカスタマイズすると、java.lang.annotation.Annotationインタフェースが自動的に継承され、コンパイラによって他の詳細が自動的に完了します.注記を定義するときは、他の注記やインタフェースを継承することはできません.@interfaceは注釈を宣言するために使用され、各メソッドは実際に構成パラメータを宣言します.メソッドの名前はパラメータの名前で、戻り値タイプはパラメータのタイプです(戻り値タイプは基本タイプ、Class、String、enumのみ).パラメータのデフォルト値はdefaultで宣言できます.
          :
      public @interface     {   }

    注記パラメータのサポート可能なデータ型:
    1.すべての基本データ型(int,float,boolean,byte,double,char,long,short)2.String型3.Class型4.enum型5.Annotation型6.以上のすべての型の配列
    反射と注釈を大まかに理解した後、一つの例によって理解を深める.
    ≪インスタンス|Instance|emdw≫
    まず、一般的なレイアウトファイルactivity_main.xmlを作成します.buttonは次のとおりです.
    
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">
    
        <Button
            android:id="@+id/btn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="  "/>
    
    LinearLayout>

    注記フレームワークを使用する前に、次のように使用します.
    public class MainActivity extends Activity implements OnClickListener{
        private Button btn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //     
            bt = (Button) findViewById(R.id.btn);
            //       
            bt.setOnClickListener(this);
        }
    
        @Override
        public void onClick(View v) {
            Toast.makeText(this, "  ", Toast.LENGTH_LONG).show();
        }
    }

    注釈の書き方を使って、こうなりました.
    public class MainActivity extends Activity {
    
        @BindView(value = R.id.btn, click = "clickview")
        private Button btn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Butterfly.bind(this);
        }
    
        public void clickview() {
             Log.d("MainActivity : ", "click");
        }
    
    }

    注釈フレームワークを使用するとコードが簡潔になります.主にfindViewById(int)とsetOnClickListener(this)のこれらの操作を省いたのではないでしょうか.Butterknifeのような感じがしますか.急いではいけません.次のように書きます.
    まず、フレームワークはfindViewById(int)とsetOnClickListener(this)のプロセスを完了するのに役立ちます.
    上記のコード@BindView(value = R.id.btn, click = "clickview")のR.id.btnはvalueの値であり、我々が伝達する必要があるメソッド名clickviewは反射取得メソッドによって得ることができる.
    必要な情報が伝達されると、フレームワークの情報を読み出してフレームワークを有効にする必要があります.ここではButterfly.bind(this);によって実現されます.
    コード時刻:まず注釈を作成します
    /**
     * author: zhuzhou on 2017/3/22
     * email: [email protected]
     */
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface BindView {
        int value();
    
        String click() default "";
    }
    

    @Target(ElementType.FIELD)は、私たちが宣言した注釈がフィールドに作用していることを示しています.ここのButton@Retention(RetentionPolicy.RUNTIME)です.これは、私たちが宣言した注釈が実行時に有効であることを示しています.ここでは、value intタイプテーブルがコントロールを示すid click Stringタイプクリックイベントの方法名の2つの属性を定義しています.
    では、上記の定義が終わったら、MainActivityのフィールドで定義して使用することができますが、有効になりません.
    注釈を有効にするには、次に、注釈を読み込む方法のコア部分を作成し、Butterfly.javaのクラスを新たに定義します.
    /**
     * author: zhuzhou on 2017/3/22
     * email: [email protected]
     */
    
    public class Butterfly {
    
        public static void bind(Activity act) {
            Class c = act.getClass();
            //     activity      
            Field[] fields = c.getDeclaredFields();
    
            for (Field field : fields) {
                //          
                if (field.isAnnotationPresent(BindView.class)) { //             
                    //       
                    BindView b = field.getAnnotation(BindView.class);
                    int value = b.value();
                    field.setAccessible(true);  //             
                    Object view = null;
                    try {
                        view = act.findViewById(value);
                        //        
                        field.set(act, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                        Log.i("TAG", "      :" + field.getClass().getName() + ":" + field.getName());
                    }
    
                    try {
                        if (view instanceof View) {
                            View v = (View) view;
                            String methodName = b.click(); //               
                            EventListener eventListener = null;
                            if (!TextUtils.isEmpty(methodName)) {
                                eventListener = new EventListener(act);
                                v.setOnClickListener(eventListener);
                                eventListener.setClickMethodName(methodName);
                            }
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    以上から,まず反射act.getClass()を用いてクラスのインスタンスを取得し,c.getDeclaredFields()を介してクラス内のすべてのフィールドを取得し,次いでforループは各属性がBindView.class注釈をフィルタリングしたフィールドを取得し,idの情報(すなわちvalueの値)を取得してfindViewById(int)を呼び出す.コントロールを見つけて、反射を利用して値を割り当てます.最後にクリックイベントを設定します.ここではEventListenerを定義したクラスの下に与えられます.ここでは主にEventListenerオブジェクトを作成し、クリックイベントのメソッド名を保存し、最後に反射取得方法で処理します.これらはEventListenerで完了します.
    次にEventListener.javaクラスを定義します.
    public class EventListener implements View.OnClickListener {
        private String TAG = "EventListener";
    
        private Object receiver = null;
    
        private String clickMethodName = "";
    
        public EventListener(Object receiver) {
            this.receiver = receiver;
        }
    
        public void setClickMethodName(String clickMethodName) {
            this.clickMethodName = clickMethodName;
        }
    
        @Override
        public void onClick(View v) {
            Method method = null;
            try {
                method = receiver.getClass().getMethod(clickMethodName);
                if (method != null) {
                    method.invoke(receiver);
                }
            } catch (Exception e) {
                e.printStackTrace();
                Log.e(TAG, "   :" + clickMethodName + "  ");
            }
    
    
                try {
                    if (method == null) {
                        method = receiver.getClass().getMethod(clickMethodName, View.class);
                        if (method != null) {
                            method.invoke(receiver, v);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    Log.e(TAG, "    view     :" + clickMethodName + "  ");
                }
        }
    }

    EventListenerに渡されたメソッド名が表示され、反射receiver.getClass().getMethod()によってメソッドが取得されます.EventListenerはすでにView.OnClickListenerインタフェースを実現しています.ここでviewのクリックイベントは完了します.
    最後に注釈付きのMainActivity.javaで楽しく注釈を使用できます.
    はい、これで簡単な注釈フレームワークが完成しました.もちろん、ここではviewのクリックイベントを提供しただけで、他のイベントも同様に実現できます.すべてのコードが上に表示されているので、ここではdemoを提供していません.必要なメッセージがあります.