Android中注解的实际运用
来源:互联网 发布:网络上找不到打印机 编辑:程序博客网 时间:2024/06/05 00:17
- 什么是注解
- 注解有哪些优缺点
- 注解的讲解
- 元注解
- Target
- Retention
- Documented
- Inherited
- 自定义注解
- 案列1布局文件的注解
- 案列2字段注解
- 案列3字段注解
- 案列4事件的注解
- 元注解
首先先把源码添出来:源码下载链接
你的支持是我前进的动力,欢迎star.
接下来进入正题:在学习使用注解之前一定要理解注解的定义,明白注解的各部分构成。文章也主要从这几个方面讲解,后面附带几个小案例。
什么是注解:
- 百度百科 —— java.lang.annotation.Retention可以在您定义Annotation型态时,指示编译器如何对待您的自定义 Annotation,预设上编译器会将Annotation资讯留在class档案中,但不被虚拟机器读取,而仅用于编译器或工具程式运行时提供资讯。
- 个人理解:注解是一种元数据, 可以添加到java代码中. 类、方法、变量、参数、包都可以被注解,注解对注解的代码没有直接影响.
注解有哪些优缺点?
- 优点:
越大的项目,使用注解就越清晰,代码可读性越高,维护起来就越简单。简单来说,通过注解,可以使我们的开发更方便简- 缺点: 理解起来有一定的难度……
注解的讲解?
元注解
Java内置的注解有Override, Deprecated, SuppressWarnings等.
@Target(ElementType.METHOD)@Retention(RetentionPolicy.SOURCE)public @interface Override {}
Overrider上的两个注解我们称之为元注解。简单来说就是用来定义注解的注解我们叫做元注解。其作用就是定义注解的作用范围,使用在什么元素上面。
元注解一共有四种:
@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(布局文件的注解)
简单的Activity布局设置
注解
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface ContentView { int value();}
反射类:
public class ViewUtils { public static void injectContentView(Activity activity){ Class a = activity.getClass(); if (a.isAnnotationPresent(ContentView.class)) { // 得到activity这个类的ContentView注解 ContentView contentView = (ContentView) a.getAnnotation(ContentView.class); // 得到注解的值 int layoutId = contentView.value(); // 使用反射调用setContentView try { Method method = a.getMethod("setContentView", int.class); method.setAccessible(true); method.invoke(activity, layoutId); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }}
使用类:
@ContentView(R.layout.activity_annotation2)public class Annotation2Activity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewUtils.injectContentView(this); }}
案列2(字段注解)
Butterknife相信大家都用过,怎么实现的呢?下面我们来看看如何通过注解反射来实现一个简单的ButterKnife:
注解:
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject { int value();}
反射
public static void injectViews(Activity activity){ Class a = activity.getClass(); Field[] fields = a.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(ViewInject.class)) { ViewInject viewInject = field.getAnnotation(ViewInject.class); int viewId = viewInject.value(); try { Method method = a.getMethod("findViewById", int.class); method.setAccessible(true); Object invoke = method.invoke(activity, viewId); field.setAccessible(true); field.set(activity,invoke); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }
使用方法:
@ContentView(R.layout.activity_annotation2)public class Annotation2Activity extends AppCompatActivity { @ViewInject(R.id.annotation_btn1) Button mButton1; @ViewInject(R.id.annotation_btn2) Button mButton2; @ViewInject(R.id.annotation_text) TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewUtils.injectContentView(this); ViewUtils.injectViews(this); }}
案列3(字段注解)
在请求网络数据时,会提供接口地址、请求数据等等一些参数,接下来展示的时如何利用反射和注解来封装我们的请求部分:
枚举
public enum Host { Aserbao_1("https://One"), Aserbao_2("https://Two"), Aserbao_3("https://Three"); private String host; Host(String host){ this.host = host; } public String getHost(){ return host; }}
参数注解:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface RequestParamsKey { String key();}
类注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface RequestParamsUrl { //接口地址 String url(); //端口 Host host() default Host.Aserbao_1; //缓存 boolean isCache() default false;}
输入参数类:
@RequestParamsUrl(url = "getLocation.php", isCache = true, host = Host.Aserbao_2)public class LocationReq { @RequestParamsKey(key = "lat_key") public String lat; @RequestParamsKey(key = "lan_key") public String lan;}
反射解析类:
public class RequestParam { public RequestParam() { } 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(); }}
结果展示类:
public class AnnationActivity extends AppCompatActivity { public LocationReq mLocation; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_annation); } public void btn_one(View view) { config(); Toast.makeText(AnnationActivity.this, RequestParam.getParam(mLocation.getClass(),mLocation), Toast.LENGTH_SHORT).show(); } /** * 设置请求参数 */ private void config() { mLocation = new LocationReq(); mLocation.lan = "123.09"; mLocation.lat = "232.34"; }}
案列4(事件的注解)
注解:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface EventBase { Class listenerType(); String listenerSetter(); String methodName();}
@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@EventBase(listenerType = View.OnClickListener.class,listenerSetter = "setOnClickListener",methodName = "onClick")public @interface OnClick { int[] value();}
反射
public static void injectEvents(Activity activity){ Class<? extends Activity> aClass = activity.getClass(); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(OnClick.class)) { //得到该方法上的OnClick注解 OnClick onClick = method.getAnnotation(OnClick.class); //获取到OnClick注解的值 int[] viewIds = onClick.value(); //得到OnClick注解上的EventBase注解 EventBase eventBase = onClick.annotationType().getAnnotation(EventBase.class); String listenerSetter = eventBase.listenerSetter(); Class listenerType = eventBase.listenerType(); String methodName = eventBase.methodName(); //使用动态代理 DynamicHandler handler = new DynamicHandler(activity); Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[] { listenerType }, handler); handler.addMethod(methodName, method); // 为每个view设置点击事件 for (int viewId : viewIds) { try { Method findViewByIdMethod = aClass.getMethod("findViewById", int.class); findViewByIdMethod.setAccessible(true); View view = (View) findViewByIdMethod.invoke(activity, viewId); Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType); setEventListenerMethod.setAccessible(true); setEventListenerMethod.invoke(view, listener); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }
动态代理:
public class DynamicHandler implements InvocationHandler { private final HashMap<String,Method> methodMap = new HashMap<>(1); // 因为传进来的为activity,使用弱引用主要是为了防止内存泄漏 private WeakReference<Object> handlerRef; public DynamicHandler(Object object){ this.handlerRef = new WeakReference<Object>(object); } public void addMethod(String name,Method method){ methodMap.put(name,method); } // 当回到OnClickListener的OnClick方法的时候,它会调用这里的invoke方法 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 得到activity实例 Object handler = handlerRef.get(); if (handler != null) { // method对应的就是回调方法OnClick,得到方法名 String methodName = method.getName(); // 得到activtiy里面的clickBtnInvoked方法 method = methodMap.get(methodName); if (method != null) { // 回调clickBtnInvoked方法 return method.invoke(handler,args); } } return null; }}
使用方法:
@ContentView(R.layout.activity_annotation2)public class Annotation2Activity extends AppCompatActivity { @ViewInject(R.id.annotation_btn1) Button mButton1; @ViewInject(R.id.annotation_btn2) Button mButton2; @ViewInject(R.id.annotation_text) TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewUtils.injectContentView(this); ViewUtils.injectViews(this); ViewUtils.injectEvents(this); } @Override protected void onResume() { super.onResume(); init(); } private void init() { mTextView.setText("The second part is already done"); } @OnClick({R.id.annotation_btn1, R.id.annotation_btn2}) public void clickBtnInvoked(View view){ switch (view.getId()){ case R.id.annotation_btn1: Toast.makeText(this, "toast num one", Toast.LENGTH_SHORT).show(); break; case R.id.annotation_btn2: Toast.makeText(this, "toast num two", Toast.LENGTH_SHORT).show(); break; } }}
这里写一下ViewUtils的代码:
public class ViewUtils { public static void injectContentView(Activity activity){ Class a = activity.getClass(); if (a.isAnnotationPresent(ContentView.class)) { // 得到activity这个类的ContentView注解 ContentView contentView = (ContentView) a.getAnnotation(ContentView.class); // 得到注解的值 int layoutId = contentView.value(); // 使用反射调用setContentView try { Method method = a.getMethod("setContentView", int.class); method.setAccessible(true); method.invoke(activity, layoutId); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } } public static void injectViews(Activity activity){ Class a = activity.getClass(); Field[] fields = a.getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(ViewInject.class)) { ViewInject viewInject = field.getAnnotation(ViewInject.class); int viewId = viewInject.value(); try { Method method = a.getMethod("findViewById", int.class); method.setAccessible(true); Object invoke = method.invoke(activity, viewId); field.setAccessible(true); field.set(activity,invoke); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } public static void injectEvents(Activity activity){ Class<? extends Activity> aClass = activity.getClass(); Method[] methods = aClass.getDeclaredMethods(); for (Method method : methods) { if (method.isAnnotationPresent(OnClick.class)) { //得到该方法上的OnClick注解 OnClick onClick = method.getAnnotation(OnClick.class); //获取到OnClick注解的值 int[] viewIds = onClick.value(); //得到OnClick注解上的EventBase注解 EventBase eventBase = onClick.annotationType().getAnnotation(EventBase.class); String listenerSetter = eventBase.listenerSetter(); Class listenerType = eventBase.listenerType(); String methodName = eventBase.methodName(); //使用动态代理 DynamicHandler handler = new DynamicHandler(activity); Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[] { listenerType }, handler); handler.addMethod(methodName, method); // 为每个view设置点击事件 for (int viewId : viewIds) { try { Method findViewByIdMethod = aClass.getMethod("findViewById", int.class); findViewByIdMethod.setAccessible(true); View view = (View) findViewByIdMethod.invoke(activity, viewId); Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType); setEventListenerMethod.setAccessible(true); setEventListenerMethod.invoke(view, listener); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }}
源码下载链接你的支持是我前进的动力,欢迎star.有问题欢迎留言。
- Android中注解的实际运用
- @interface自定义注解的实际运用
- java注解,反射实现抽象代码工作中实际运用
- 实际开发中关于autorelease的运用
- stm32实际运用中遇到的问题
- fusioncharts的实际运用
- AcionBar的实际运用
- Android之Volley框架在实际开发过程中运用
- 命令模式在Android实际场景中运用
- 在eclipse中FreeMarker的实际运用案例
- c# 线程中任务Task的实际运用
- 在实际软件开发中运用到的工具
- hash算法的实际运用
- spring注解的运用
- android中FileObserver的运用
- android中全局变量的运用
- Android 中Notification的运用
- Android中BroadcastReceiver的运用
- Weex实现GridView的网格布局以及浮层效果
- 【1233】推箱子(右)
- android如何让布局保持位于键盘上方(一直在键盘上面)
- GemFile 学习——环境搭建
- mybatis中的#{}和${}区别
- Android中注解的实际运用
- 机器学习算法(1) KNN
- ODBC Excel驱动程序登陆失败
- [ESSAY]what are you optimistic/pessimistic about?
- SystemUI之功能介绍和UI布局介绍
- C++ primer 学习笔记(一个学过谭老师的C++书籍, 并在一年间间断使用过C++的码农)
- BZOJ 3170: [Tjoi 2013]松鼠聚会 切比雪夫距离
- ssl与ssh协议的一些笔记
- Rythm.js 使用教程详解