反射(Reflect)
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性.
Person.java
public class Person {
private String name;
private int age;
private void intro() {
System.out.println("我叫" + name + ",我今年" + age + "岁.");
}
}
TestDemo.java
public class TestDemo {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class<?> cls = person.getClass();
// 获取Person运行时的类
Field nameField = cls.getDeclaredField("name");
Field ageField = cls.getDeclaredField("age");
// 获取name和age属性
// cls.getDeclaredFields(); //获取所有属性
nameField.setAccessible(true);
ageField.setAccessible(true);
// 打破name和age属性的封装性
nameField.set(person, "小明");
ageField.set(person, 19);
// 为person对象对应的属性设置值
Method method = cls.getDeclaredMethod("intro");
// 获取intro方法
// cls.getDeclaredMethods(); //获取所有方法
method.setAccessible(true);
// 打破intro方法的封装性
method.invoke(person);
//调用person的intro方法,
// method.invoke(person, "arg1","arg2"...); 支持不定长参数作为方法的参数
}
}
注解(Annotation)
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
Person.java
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "我叫" + name + ",今年" + age + "岁";
}
}
PersonInject.java
package snippet;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD) // 改注解只能注解属性
// @Target({ElementType.FIELD,ElementType.METHOD})可指定多种类型
/**
* TYPE(类型) FIELD(属性) METHOD(方法) PARAMETER(参数) CONSTRUCTOR(构造函数)
* LOCAL_VARIABLE(局部变量) ANNOTATION_TYPE,PACKAGE(包),
*/
@Retention(RetentionPolicy.RUNTIME)
/**
* SOURCE:代表的是这个Annotation类型的信息只会保留在程序源码里,源码如果经过了编译之后,Annotation的数据就会消失,
* 并不会保留在编译好的.class文件里面。
*
* ClASS:意思是这个Annotation类型的信息保留在程序源码里,同时也会保留在编译好的.class文件里面,在执行的时候,
* 并不会把这一些信息加载到虚拟机(JVM)中去.注意一下,当你没有设定一个Annotation类型的Retention值时,系统默认值是CLASS.
*
* RUNTIME:表示在源码、编译好的.class文件中保留信息,在执行的时候会把这一些信息加载到JVM中去的.
*/
public @interface PersonInject {
String name();
int age();
// 上面两个可以类比函数的形参
}
TestDemo.java
public class TestDemo {
@PersonInject(name = "小明", age = 19) // 仅仅是添加注解
Person person;
public static void main(String[] args) throws Exception {
// 现在才开始真正注射
TestDemo demo = new TestDemo();
Class<?> cls = demo.getClass();
Field declaredField = cls.getDeclaredField("person");
// 请看反射那一块
PersonInject personInject = declaredField.getAnnotation(PersonInject.class);
String name = personInject.name();
int age = personInject.age();
// 获取person属性的注解和它的值
Person person = new Person();
person.setName(name);
person.setAge(age);
declaredField.setAccessible(true);
declaredField.set(demo, person);
// 设置值
System.out.println(demo.person);
}
}
开始编写小型注解框架
好啦,现在我们把前面的东西用到Android里面吧.
布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="这是原来的文字" />
<Button
android:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="改变文字" />
<Button
android:id="@+id/btn1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="改变文字" />
</LinearLayout>
ViewInject注解Demo
ViewInject.java
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
// 这里默认是value,如果是age的话注解是写(age=19),如果是value的话可以直接(19)
}
MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
@ViewInject(R.id.tv)
private TextView tv;
@ViewInject(R.id.btn)
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Class<?> cls = MainActivity.class;
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
field.setAccessible(true);
View view = findViewById(viewInject.value());
field.set(MainActivity.this, view);
}
}
btn.setOnClickListener(this);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
tv.setText("ViewInject注解成功!");
}
}
OnClick注解Demo
OnClick.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int value();
}
MainActivity.java
package com.example.viewinjectdemo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import viewinject.OnClick;
import viewinject.ViewInject;
public class MainActivity extends Activity implements OnClickListener {
@ViewInject(R.id.tv)
private TextView tv;
@ViewInject(R.id.btn)
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Class<?> cls = MainActivity.class;
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
field.setAccessible(true);
View view = findViewById(viewInject.value());
field.set(MainActivity.this, view);
}
}
btn.setOnClickListener(this);
Method[] declaredMethods = cls.getDeclaredMethods();
for (final Method method : declaredMethods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
method.setAccessible(true);
View view = MainActivity.this.findViewById(onClick.value());
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
method.invoke(MainActivity.this);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
tv.setText("ViewInject注解成功!");
}
@OnClick(R.id.btn1)
public void clickBtn() {
tv.setText("OnClick注解成功!");
}
}
封装
从前面我们可以看得到,注射方法跟属性的代码都是固定的.
因此我们可以把代码封装成一个类JBInject
,只需要Activity
作为参数就可以了.
JBInject.java
public class JBInject {
private static boolean flag;
public static boolean inject(final Activity activity) {
flag = true;
try {
Class<?> cls = activity.getClass();
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
field.setAccessible(true);
View view = activity.findViewById(viewInject.value());
field.set(activity, view);
}
}
Method[] declaredMethods = cls.getDeclaredMethods();
for (final Method method : declaredMethods) {
OnClick onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
method.setAccessible(true);
View view = activity.findViewById(onClick.value());
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
try {
method.invoke(activity);
} catch (Exception e) {
e.printStackTrace();
flag = false;
}
}
});
}
}
} catch (Exception e) {
e.printStackTrace();
flag = false;
}
return flag;
}
}
MainActivity.java
public class MainActivity extends Activity implements OnClickListener {
@ViewInject(R.id.tv)
private TextView tv;
@ViewInject(R.id.btn)
private Button btn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JBInject.inject(MainActivity.this);
btn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
tv.setText("ViewInject注解成功!");
}
@OnClick(R.id.btn1)
public void clickBtn() {
tv.setText("OnClick注解成功!");
}
}