1. 背景
Android开发,少不了遇到注解,比如写一个Activity,基本都要用注解@Override。反射呢,一般用不到,但是如果想自己实现注解,就得用了。所以一般注解和反射,都放在一起讨论。
2. 注解
Annotation(注解)就是Java提供了一种源程序中的元素关联任何信息或者任何元数据(metadata)的途径和方法。
Annotation是被动的元数据,永远不会有主动行为
自定义一个注解,格式通常如下:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
@Inherited
public @interface BindView {
int value() default 1;
boolean visiable() default false;
}
@interface 表明这是一个注解,它只有成员变量,没有方法。
Annotation的成员变量在Annotation定义中以“无形参的方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。比如上面的value和visiable。
自定义注解里也会有注解存在,给自定义注解使用的注解就是元注解。
@Rentention
@Rentention 用来标记自定义注解的有效范围,他的取值有以下三种:
RetentionPolicy.SOURCE: 只在源代码中保留 一般都是用来增加代码的理解性或者帮助代码检查之类的,比如我们的Override;
RetentionPolicy.CLASS: 默认的选择,能把注解保留到编译后的字节码class文件中,仅仅到字节码文件中,运行时是无法得到的;
RetentionPolicy.RUNTIME: ,注解不仅 能保留到class字节码文件中,还能在运行通过反射获取到,这也是我们最常用的。
@Target
@Target指定Annotation用于修饰哪些程序元素。
@Target也包含一个名为”value“的成员变量,该value成员变量类型为ElementType[ ],ElementType为枚举类型,值有如下几个:
ElementType.TYPE:能修饰类、接口或枚举类型
ElementType.FIELD:能修饰成员变量
ElementType.METHOD:能修饰方法
ElementType.PARAMETER:能修饰参数
ElementType.CONSTRUCTOR:能修饰构造器
ElementType.LOCAL_VARIABLE:能修饰局部变量
ElementType.ANNOTATION_TYPE:能修饰注解
ElementType.PACKAGE:能修饰包
使用了@Documented的可以在javadoc中找到
使用了@Interited表示注解里的内容可以被子类继承,比如父类中某个成员使用了上述@From(value),From中的value能给子类使用到。
3. 反射
Java反射机制是指在运行状态中
对于任意一个类,都能知道这个类的所有属性和方法;
对于任何一个对象,都能够调用它的任何一个方法和属性;
这样动态获取新的以及动态调用对象方法的功能就叫做反射。
比如像下面:
//获取类
Class c = Class.forName("java.lang.String");
// 获取所有的属性
Field[] fields = c.getDeclaredFields();
StringBuffer sb = new StringBuffer();
sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + "{\n");
// 遍历每一个属性
for (Field field : fields) {
sb.append("\t");// 空格
sb.append(Modifier.toString(field.getModifiers()) + " ");// 获得属性的修饰符,例如public,static等等
sb.append(field.getType().getSimpleName() + " ");// 属性的类型的名字
sb.append(field.getName() + ";\n");// 属性的名字+回车
}
sb.append("}\n");
System.out.println(sb);
反射涉及的关键类:
java.lang.Class 编译后的class文件的对象
java.lang.reflect.Constructor 构造方法
java.lang.reflect.Field 类的成员变量(属性)
java.lang.reflect.Method 类的成员方法
java.lang.reflect.Modifier 判断方法类型
java.lang.annotation.Annotation类的注解
3.1 Class的常用方法
- 获取类的属性(成员变量)
Field[] fields = c.getDeclaredFields();
返回的是一个数组 ,包含所有的属性。每一个属性就是一个Filed - 获取类的方法
Method[] ms = c.getDeclaredMethods();
和属性类似,我们依然可以通过一系列的方法获取到方法的返回值类型,名称以及参数。
4. 注解,反射的应用–轻量级ButterKnife实现
ButterKnife 是一个非常有名的开源包,主要用于帮你节省findViewById的工作。它主要是用注解与反射实现。如果不想引用它,我们完全可以通过几个类,自己实现一个。
4.1 定义BindView注解
package com.example.annotationtest;
import android.support.annotation.IdRes;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Bind a field to the view for the specified ID. The view will automatically be cast to the field
* type.
* <pre><code>
* {@literal @}BindView(R.id.title) TextView title;
* </code></pre>
*/
@Retention(RUNTIME) @Target(FIELD)
public @interface BindView {
/** View ID to which the field will be bound. */
@IdRes int value(); //@IdRes 注解这个值为资源ID,如果不写,也可以
}
4.2 使用注解
package com.example.annotationtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.text_view)//这里只是表示,textView有个注解,值为text_view
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);//这里才是关键,读取textView的注解,把值取出来,然后执行findViewById(R.id.text_view)
textView.setText("CHENXF CHENXF");
}
}
ButterKnife.bind(this) 是真正初始化textView的地方,这个是运行期间执行的。怎么实现呢
4.3 ButterKnife.bind(this) 实现
package com.example.annotationtest;
import android.app.Activity;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.util.Log;
import android.view.View;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.List;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.STATIC;
public final class ButterKnife {
private ButterKnife() {
throw new AssertionError();
}
private static final String TAG = "ButterKnife";
private static boolean debug = false;
/**
* Control whether debug logging is enabled.
*/
public static void setDebug(boolean debug) {
ButterKnife.debug = debug;
}
/**
* BindView annotated fields and methods in the specified {@link Activity}. The current content
* view is used as the view root.
*
* @param target Target activity for view binding.
*/
@NonNull
@UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link View} as the view root.
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull
@UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
List<Unbinder> unbinders = new ArrayList<>();
Class<?> targetClass = target.getClass();//读取当前的activity
if ((targetClass.getModifiers() & PRIVATE) != 0) {
throw new IllegalArgumentException(targetClass.getName() + " must not be private.");
}
while (true) {
String clsName = targetClass.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
break;
}
for (Field field : targetClass.getDeclaredFields()) {//读取activity的变量
int unbinderStartingSize = unbinders.size();
Unbinder unbinder;
unbinder = parseBindView(target, field, source);//根据变量的注解,初始化变量
if (unbinder != null) unbinders.add(unbinder);
if (unbinders.size() - unbinderStartingSize > 1) {
throw new IllegalStateException(
"More than one bind annotation on " + targetClass.getName() + "." + field.getName());
}
}
targetClass = targetClass.getSuperclass();
}
if (unbinders.isEmpty()) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return Unbinder.EMPTY;
}
if (debug) Log.d(TAG, "HIT: Reflectively found " + unbinders.size() + " bindings.");
return new CompositeUnbinder(unbinders);
}
private static @Nullable
Unbinder parseBindView(Object target, Field field, View source) {
BindView bindView = field.getAnnotation(BindView.class);//获得一个变量的注解
if (bindView == null) {
return null;
}
validateMember(field);
int id = bindView.value();//获得注解的值
Class<?> viewClass = field.getType();
if (!View.class.isAssignableFrom(viewClass) && !viewClass.isInterface()) {
throw new IllegalStateException(
"@BindView fields must extend from View or be an interface. ("
+ field.getDeclaringClass().getName()
+ '.'
+ field.getName()
+ ')');
}
String who = "field '" + field.getName() + "'";
Object view = Utils.findOptionalViewAsType(source, id, who, viewClass);//相当于view = activity.findViewById(id)
trySet(field, target, view);//相当于把变量赋值为view,即field = view
return new FieldUnbinder(target, field);//有需要解绑view才用,你就算想return null,也可以
}
private static <T extends AccessibleObject & Member> void validateMember(T object) {
int modifiers = object.getModifiers();
if ((modifiers & (PRIVATE | STATIC)) != 0) {
throw new IllegalStateException(object.getDeclaringClass().getName()
+ "."
+ object.getName()
+ " must not be private or static");
}
if ((modifiers & PUBLIC) == 0) {
object.setAccessible(true);
}
}
static void trySet(Field field, Object target, @Nullable Object value) {
try {
field.set(target, value);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to assign " + value + " to " + field + " on " + target, e);
}
}
}
实现很简单,看我上面的注释就可以。
4.4 Utils.findOptionalViewAsType 实现
package com.example.annotationtest;
import android.support.annotation.IdRes;
import android.view.View;
@SuppressWarnings("WeakerAccess") // Used by generated code.
public final class Utils {
public static <T> T findOptionalViewAsType(View source, @IdRes int id, String who,
Class<T> cls) {
View view = source.findViewById(id);//哈哈,关键代码在这里
return castView(view, id, who, cls);
}
public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) {
try {
return cls.cast(view);
} catch (ClassCastException e) {
String name = getResourceEntryName(view, id);
throw new IllegalStateException("View '"
+ name
+ "' with ID "
+ id
+ " for "
+ who
+ " was of the wrong type. See cause for more info.", e);
}
}
private static String getResourceEntryName(View view, @IdRes int id) {
if (view.isInEditMode()) {
return "<unavailable while editing>";
}
return view.getContext().getResources().getResourceEntryName(id);
}
private Utils() {
throw new AssertionError("No instances.");
}
}
4.5 其他2个非重要的类:
package com.example.annotationtest;
import android.support.annotation.UiThread;
/** An unbinder contract that will unbind views when called. */
public interface Unbinder {
@UiThread
void unbind();
Unbinder EMPTY = null;
}
package com.example.annotationtest;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.util.List;
final class CompositeUnbinder implements Unbinder {
private @Nullable
List<Unbinder> unbinders;
CompositeUnbinder(@NonNull List<Unbinder> unbinders) {
this.unbinders = unbinders;
}
@Override public void unbind() {
if (unbinders == null) {
throw new IllegalStateException("Bindings already cleared.");
}
for (Unbinder unbinder : unbinders) {
unbinder.unbind();
}
unbinders = null;
}
}
本文详细解析了Android开发中注解与反射的基本概念及应用,重点介绍了自定义注解和反射机制的结合,通过实例展示了如何实现一个轻量级的ButterKnife框架,用于自动绑定视图。

1066

被折叠的 条评论
为什么被折叠?



