轉載請標明出處:
http://blog.csdn.net/hai_qing_xu_kong/article/details/51779695
本文出自:【顧林海的博客】
目前注解的使用頻率還是挺高,像第3方butterknife、數據庫ActiveAndroid等等,通過注解,我們的開發效力得到了明顯提高。因此理解注解并熟練使用注解是非常重要的,下面分為兩部份,第1部份是注解的介紹,資料來源于網上;第2部份是兩個小例子,利用注解+反射分別完成網絡要求的封裝和數據庫操作案例。
注解是1種元數據, 可以添加到java代碼中. 類、方法、變量、參數、包都可以被注解,注解對注解的代碼沒有直接影響.
ava內置的注解有Override, Deprecated, SuppressWarnings等.
現在查看Override注解的源碼
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.SOURCE)
public @interface Override {
}
發現Override注解上面有兩個注解, 這就是元注解. 元注解就是用來定義注解的注解.其作用就是定義注解的作用范圍, 使用在甚么元素上等等, 下面來詳細介紹.
元注解共有4種@Retention, @Target, @Inherited, @Documented
@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定義了該Annotation被保存的時間長短:某些Annotation僅出現在源代碼中,而被編譯器拋棄;而另外一些卻被編譯在class文件中;編譯在class文件中的Annotation可能會被虛擬機疏忽,而另外一些在class被裝載時將被讀?。ㄕ堊⒁馄鋵嵅挥绊慶lass的履行,由于Annotation與class在使用上是被分離的)。使用這個meta-Annotation可以對 Annotation的“生命周期”限制。
作用:表示需要在甚么級別保存該注釋信息,用于描寫注解的生命周期(即:被描寫的注解在甚么范圍內有效)
取值(RetentionPoicy)有:
1.SOURCE:在源文件中有效(即源文件保存)
2.CLASS:在class文件中有效(即class保存)
3.RUNTIME:在運行時有效(即運行時保存)
@Documented用于描寫其它類型的annotation應當被作為被標注的程序成員的公共API,因此可以被例如javadoc此類的工具文檔化。Documented是1個標記注解,沒有成員。
@Inherited 元注解是1個標記注解,@Inherited論述了某個被標注的類型是被繼承的。如果1個使用了@Inherited修飾的annotation類型被用于1個class,則這個annotation將被用于該class的子類。
注意:@Inherited annotation類型是被標注過的class的子類所繼承。類其實不從它所實現的接口繼承annotation,方法其實不從它所重載的方法繼承annotation。
當@Inherited annotation類型標注的annotation的Retention是RetentionPolicy.RUNTIME,則反射API增強了這類繼承性。如果我們使用java.lang.reflect去查詢1個@Inherited annotation類型的annotation時,反射代碼檢查將展開工作:檢查class和其父類,直到發現指定的annotation類型被發現,或到達類繼承結構的頂層。
其中, @Retention是定義保存策略, 直接決定了我們用何種方式解析. SOUCE級別的注解是用來標記的, 比如Override, SuppressWarnings. 我們真正使用的類型是CLASS(編譯時)和RUNTIME(運行時)
使用@interface自定義注解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義注解時,不能繼承其他的注解或接口。@interface用來聲明1個注解,其中的每個方法實際上是聲明了1個配置參數。方法的名稱就是參數的名稱,返回值類型就是參數的類型(返回值類型只能是基本類型、Class、String、enum)??梢酝ㄟ^default來聲明參數的默許值。
定義注解格式:
public @interface 注解名 {定義體}
注解參數的可支持數據類型:
栗子1:
在要求網絡數據時,會提供接口地址、要求數據等等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 注解用于要求的地址、端口和1些配置參數。
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<String> clumsList = new ArrayList<String>();
/**
* 打開數據庫
*
* @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 是1個抽象類,用于數據庫的操作。
以下是建表操作:
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;
}
創建了1個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個拋磚迎玉的作用,大家可以定義出合適當前業務場景注解框架。
下一篇 自定義LoadingView大全