Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android中的自定義注解

Android中的自定義注解

編輯:關於Android編程

前言

目前注解的使用頻率還是挺高,像第三方butterknife、數據庫ActiveAndroid等等,通過注解,我們的開發效率得到了明顯提高。因此理解注解並熟練使用注解是非常重要的,下面分為兩部分,第一部分是注解的介紹,資料來源於網上;第二部分是兩個小例子,利用注解+反射分別完成網絡請求的封裝和數據庫操作案例。

什麼是注解

注解是一種元數據, 可以添加到java代碼中. 類、方法、變量、參數、包都可以被注解,注解對注解的代碼沒有直接影響.

元注解

ava內置的注解有Override, Deprecated, SuppressWarnings等.
現在查看Override注解的源碼

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}

發現Override注解上面有兩個注解, 這就是元注解. 元注解就是用來定義注解的注解.其作用就是定義注解的作用范圍, 使用在什麼元素上等等, 下面來詳細介紹.

元注解共有四種@Retention, @Target, @Inherited, @Documented

@Target:

   @Target說明了Annotation所修飾的對象范圍:Annotation可被用於 packages、types(類、接口、枚舉、Annotation類型)、類型成員(方法、構造方法、成員變量、枚舉值)、方法參數和本地變量(如循環變量、catch參數)。在Annotation類型的聲明中使用了target可更加明晰其修飾的目標。
  作用:用於描述注解的使用范圍(即:被描述的注解可以用在什麼地方)
  取值(ElementType)有:
    1.CONSTRUCTOR:用於描述構造器
    2.FIELD:用於描述域
    3.LOCAL_VARIABLE:用於描述局部變量
    4.METHOD:用於描述方法
    5.PACKAGE:用於描述包
    6.PARAMETER:用於描述參數
    7.TYPE:用於描述類、接口(包括注解類型) 或enum聲明

@Retention:

  @Retention定義了該Annotation被保留的時間長短:某些Annotation僅出現在源代碼中,而被編譯器丟棄;而另一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機忽略,而另一些在class被裝載時將被讀取(請注意並不影響class的執行,因為Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
  作用:表示需要在什麼級別保存該注釋信息,用於描述注解的生命周期(即:被描述的注解在什麼范圍內有效)
  取值(RetentionPoicy)有:
    1.SOURCE:在源文件中有效(即源文件保留)
    2.CLASS:在class文件中有效(即class保留)
    3.RUNTIME:在運行時有效(即運行時保留)

@Documented:

  @Documented用於描述其它類型的annotation應該被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是一個標記注解,沒有成員。

@Inherited:

  @Inherited 元注解是一個標記注解,@Inherited闡述了某個被標注的類型是被繼承的。如果一個使用了@Inherited修飾的annotation類型被用於一個class,則這個annotation將被用於該class的子類。
  注意:@Inherited annotation類型是被標注過的class的子類所繼承。類並不從它所實現的接口繼承annotation,方法並不從它所重載的方法繼承annotation。
  當@Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這種繼承性。如果我們使用java.lang.reflect去查詢一個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或者到達類繼承結構的頂層。

其中, @Retention是定義保留策略, 直接決定了我們用何種方式解析. SOUCE級別的注解是用來標記的, 比如Override, SuppressWarnings. 我們真正使用的類型是CLASS(編譯時)和RUNTIME(運行時)

自定義注解:

  使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義注解時,不能繼承其他的注解或接口。@interface用來聲明一個注解,其中的每一個方法實際上是聲明了一個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)。可以通過default來聲明參數的默認值。
  
定義注解格式:

public @interface 注解名 {定義體}

注解參數的可支持數據類型:

所有基本數據類型(int,float,boolean,byte,double,char,long,short) String類型 Class類型 enum類型 Annotation類型 以上所有類型的數組

栗子

栗子1:

在請求網絡數據時,會提供接口地址、請求數據等等一些參數,接下來展示的時如何利用反射和注解來封裝我們的請求部分:

package demo.src.demo.request.host;

public enum Host {

    Host_1("https://L1"), Host_2("https://T2"), Host_3("https://R3");

    private String host;

    Host(String host) {
        this.host = host;
    }

    public String getHost() {
        return host;
    }
}

Host是我們的請求端口。

package demo.src.demo.request.params;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface RequestParamsKey {
    String key();
}
package demo.src.demo.request.params;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import demo.src.demo.request.host.Host;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RequestParamsUrl {
    //接口地址
    String url();

    //端口
    Host host() default Host.Host_1;

    //緩存
    boolean isCache() default false;
}

RequestParamsKey 注解用於請求的鍵值對,RequestParamsUrl 注解用於請求的地址、端口以及一些配置參數。

package demo.src.demo.request;

import java.lang.reflect.Field;

import demo.src.demo.request.params.RequestParamsKey;
import demo.src.demo.request.params.RequestParamsUrl;

/**
 * 拼接請求參數
 * 
 * @author glh
 * 
 */
public class RequestParam {

    private RequestParam() {
    }

    /**
     * 獲取request參數
     * @param _clazz
     * @param _object
     * @return
     */
    public static String getParam(Class _clazz, Object _object) {
        Class clazz = _clazz;
        Field[] fields = clazz.getDeclaredFields();
        try {
            return requestParam(fields, clazz, _object);
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 獲取請求路徑
     * 
     * @param fields
     * @param clazz
     * @param _object
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    private static String requestParam(Field[] fields, Class clazz, Object _object) throws IllegalArgumentException, IllegalAccessException {
        StringBuilder request = new StringBuilder();
        RequestParamsUrl requestParamsUrl = (RequestParamsUrl) clazz.getAnnotation(RequestParamsUrl.class);
        if (requestParamsUrl != null) {
            String url = requestParamsUrl.url();
            boolean isCache = requestParamsUrl.isCache();
            String host = requestParamsUrl.host().getHost();
            request.append(host);
            request.append(url);
            request.append("?");
            System.out.println("請求端口:" + host);
            System.out.println("請求地址:" + url);
            System.out.println("是否緩存:" + isCache);
        }
        for (Field field : fields) {
            RequestParamsKey requestParamsKey = field.getAnnotation(RequestParamsKey.class);
            if (requestParamsKey != null) {
                String key = requestParamsKey.key();
                String Value = (String) field.get(_object);
                request.append(key);
                request.append("=");
                request.append(Value);
                request.append("&");
            }
        }
        request.deleteCharAt(request.length() - 1);
        System.out.println("請求路徑:" + request.toString());
        return request.toString();
    }

}

RequestParam 類用於解析我們自定義的注解並獲取所需的參數。

接下來看如何定義我們的請求參數request:

package demo.src.demo.reqBody;

import demo.src.demo.request.host.Host;
import demo.src.demo.request.params.RequestParamsKey;
import demo.src.demo.request.params.RequestParamsUrl;

@RequestParamsUrl(url = "getLocation.php", isCache = true, host = Host.Host_2)
public class LocationReq {
    @RequestParamsKey(key = "lat_key")
    public String lat;
    @RequestParamsKey(key = "lan_key")
    public String lan;
}
package demo.src.demo;

import demo.src.demo.reqBody.LocationReq;
import demo.src.demo.request.RequestParam;

public class demo {

    public LocationReq mLocation;

    public static void main(String[] args) throws ClassNotFoundException {
        demo demo = new demo();
        demo.cofig();
        demo.getRequest();
    }

    /**
     * 設置請求參數
     */
    private void cofig() {
        mLocation = new LocationReq();
        mLocation.lan = "123.09";
        mLocation.lat = "232.34";

    }

    /**
     * 獲取請求路徑
     */
    private void getRequest(){
        System.out.println(RequestParam.getParam(mLocation.getClass(),mLocation));
    }


}

輸出結果:

請求端口:https://T2
請求地址:getLocation.php
是否緩存:true
請求路徑:https://T2getLocation.php?lat_key=232.34&lan_key=123.09
https://T2getLocation.php?lat_key=232.34&lan_key=123.09

栗子2:

下面是通過注解和反射定義數據庫的創建等操作:

package com.example.dbannotion.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * 數據庫
 * @author glh
 *
 */
public class SqliteHelper extends SQLiteOpenHelper{

    private String createTable;
    private String tableName;

  public SqliteHelper(Context context, String name, CursorFactory factory, int version,String createTable,String tableName) {
    super(context, name, factory, version);
    this.createTable=createTable;
    this.tableName=tableName;

  }
  /**
   * 創建表
   */
  @Override
  public void onCreate(SQLiteDatabase db) {
    if(createTable!=""){
        db.execSQL(createTable);
    }

  }
  /**
   * 更新表
   */
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      db.execSQL( "DROP TABLE IF EXISTS " + tableName );
      onCreate(db);
  }
  /**
   * 更新列
   * @param db
   * @param oldColumn
   * @param newColumn
   * @param typeColumn
   */
  public void updateColumn(SQLiteDatabase db, String oldColumn, String newColumn, String typeColumn){
      try{
          db.execSQL( "ALTER TABLE " +
                tableName + " CHANGE " +
                  oldColumn + " "+ newColumn +
                  " " + typeColumn
          );
      } catch(Exception e){
          e.printStackTrace();
      }
  }
}

SqliteHelper 用於創建、更新數據庫操作。

package com.example.dbannotion.db.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumDB {

    String column();

}
package com.example.dbannotion.db.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface TableDB {
    String table();//表名
    String dbName() default "demoDb.db";//數據庫名稱
    int version() default 1;//版本號
}

ColumDB 注解用於列數據(鍵值對),TableDB 注解用於表和數據庫的配置參數。

package com.example.dbannotion.db;

import java.lang.reflect.Field;
import java.util.ArrayList;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.example.dbannotion.db.annotation.ColumDB;
import com.example.dbannotion.db.annotation.TableDB;

public abstract class AbstractDB {

    public static final String TAG = "AbstractDB";

    private SQLiteDatabase db;
    private SqliteHelper dbHelper;
    // 版本
    private int version;
    // 數據庫名稱
    private String dbName;
    // 表名
    private String tableName;
    // 建表語句
    private StringBuilder createBuilder;

    // 插入的數據
    private ContentValues contentValues = new ContentValues();

    // 是否存在數據
    private boolean isColums;

    private ArrayList clumsList = new ArrayList();

    /**
     * 打開數據庫
     * 
     * @param _context
     * @param _clazz
     * @param _object
     * @return
     */
    public AbstractDB open(Context _context, Class _clazz, Object _object) {
        if (paramsDB(_clazz, _object)) {
            dbHelper = new SqliteHelper(_context, dbName, null, version, createBuilder.toString(), tableName);
            db = dbHelper.getWritableDatabase();
            Log.e(TAG, "-------------數據庫打開成功!----------");

        } else {
            Log.e(TAG, "-------------數據庫打開失敗!----------");
        }
        return this;
    }

    /**
     * 打開數據庫時是否進行插入操作
     * 
     * @param isInsert
     * @return
     */
    public AbstractDB isInsert(boolean isInsert) {
        if (isInsert && isColums) {
            insert();
        }
        return this;
    }

    /**
     * 創建並檢查數據庫
     * 
     * @param _object
     * @param _clazz
     * @return false:失敗 true:成功
     */
    public boolean paramsDB(Class _clazz, Object _object) {
        isColums = false;
        contentValues.clear();
        clumsList.clear();
        createBuilder = new StringBuilder();
        Class clazz = _clazz;
        Field[] fields = clazz.getDeclaredFields();
        createBuilder.append("CREATE TABLE IF NOT EXISTS ");
        /*
         * 表名
         */
        TableDB tDb = _clazz.getAnnotation(TableDB.class);
        if (tDb != null) {
            dbName = tDb.dbName();
            tableName = tDb.table();
            version = tDb.version();
            createBuilder.append(tableName);
            createBuilder.append("(");
        } else {
            return false;
        }

        /*
         * 列
         */
        for (Field field : fields) {
            ColumDB requestParamsKey = field.getAnnotation(ColumDB.class);
            if (requestParamsKey != null) {
                String key = requestParamsKey.column();
                String value;
                try {
                    value = (String) field.get(_object);

                    clumsList.add(key);
                    if (!value.isEmpty()) {
                        contentValues.put(key, value);
                    }
                    createBuilder.append(key);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                }
                createBuilder.append(" varchar,");
                isColums = true;
            }
        }
        if (!isColums) {
            return false;
        }
        createBuilder.deleteCharAt(createBuilder.length() - 1);
        createBuilder.append(")");
        return true;
    }

    public AbstractDB insert() {
        if (contentValues != null) {
            db.insert(tableName, null, contentValues);
            Log.e(TAG, "-------------數據添加成功!----------");
        } else {
            Log.e(TAG, "-------------數據添加失敗!----------");
        }
        return this;
    }

    /**
     * 獲取數據
     * 
     * @return
     */
    public String getDate() {
        StringBuilder dateBuilder = new StringBuilder();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select * from " + tableName, null);
        while (cursor.moveToNext()) {
            if (clumsList != null) {
                for (int i = 0, length = clumsList.size(); i < length; i++) {
                    String name = cursor.getString(cursor.getColumnIndex(clumsList.get(i)));
                    dateBuilder.append(clumsList.get(i));
                    dateBuilder.append("=");
                    dateBuilder.append(name);
                    dateBuilder.append(",");
                }
                dateBuilder.append("\n");
            }
        }
        cursor.close();
        if (dateBuilder.length() > 1) {
            dateBuilder.deleteCharAt(dateBuilder.length() - 1);
            return dateBuilder.toString();
        } else {
            Log.e(TAG, "-------------無數據解析!----------");
            return "";
        }
    }

    public void Close() {
        db.close();
        dbHelper.close();
    }

}

AbstractDB 是一個抽象類,用於數據庫的操作。

以下是建表操作:

package com.example.dbannotion.table;

import com.example.dbannotion.db.AbstractDB;
import com.example.dbannotion.db.annotation.ColumDB;
import com.example.dbannotion.db.annotation.TableDB;

@TableDB(table = "student",dbName="demo.db",version=1)
public class Student extends AbstractDB {
    @ColumDB(column = "name")
    public String name;
    @ColumDB(column = "age")
    public String age;
    @ColumDB(column = "sex")
    public String sex;
}

創建了一個demo.db數據庫,表面為student,版本號1,name,age,sex列。

package com.example.dbannotion;

import com.example.dbannotion.table.Student;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

    private Student student;

    public TextView tv_show;
    public Button btn_get;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initDate();
        initView();
        initEvent();
    }

    private void initView() {
        tv_show = (TextView) findViewById(R.id.tv_show);
        btn_get = (Button) findViewById(R.id.btn_get);
    }

    private void initEvent() {
        btn_get.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                new Handler().post(new Runnable() {

                    @Override
                    public void run() {
                        tv_show.setText(student.getDate());
                    }
                });
            }
        });
    }

    private void initDate() {
        new Thread(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    student = new Student();
                    student.age = i + "";
                    student.name = "name-" + i;
                    if (i % 2 == 0) {
                        student.sex = "男";
                    } else {
                        student.sex = "女";
                    }
                    student.open(MainActivity.this, student.getClass(), student).isInsert(true);
                }
            }
        }).start();
    }

}

運行結果:

這裡寫圖片描述

以上栗子只是起了一個拋磚迎玉的作用,大家可以定義出適合當前業務場景注解框架。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved