程序员人生 网站导航

Android中的自定义注解

栏目:综合技术时间:2016-08-26 09:45:52

转载请标明出处:
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:

   @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是1个标记注解,没有成员。

@Inherited:

  @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 注解名 {定义体}

注解参数的可支持数据类型:

  • 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  • String类型
  • Class类型
  • enum类型
  • Annotation类型
  • 以上所有类型的数组

栗子

栗子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个抛砖迎玉的作用,大家可以定义出合适当前业务场景注解框架。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐