反射(Reflect)
程序集包含模塊,而模塊包含類型,類型又包含成員。反射則提供了封裝程序集、模塊和類型的對象。您可以使用反射動態(tài)地創(chuàng)建類型的實例,將類型綁定到現(xiàn)有對象,或從現(xiàn)有對象中獲取類型。然后,可以調(diào)用類型的方法或訪問其字段和屬性.
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運(yùn)行時的類
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對象對應(yīng)的屬性設(shè)置值
Method method = cls.getDeclaredMethod("intro");
// 獲取intro方法
// cls.getDeclaredMethods(); //獲取所有方法
method.setAccessible(true);
// 打破intro方法的封裝性
method.invoke(person);
//調(diào)用person的intro方法,
// method.invoke(person, "arg1","arg2"...); 支持不定長參數(shù)作為方法的參數(shù)
}
}
注解(Annotation)
定義:注解(Annotation),也叫元數(shù)據(jù)。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進(jìn)行說明,注釋。
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(參數(shù)) CONSTRUCTOR(構(gòu)造函數(shù))
* LOCAL_VARIABLE(局部變量) ANNOTATION_TYPE,PACKAGE(包),
*/
@Retention(RetentionPolicy.RUNTIME)
/**
* SOURCE:代表的是這個Annotation類型的信息只會保留在程序源碼里,源碼如果經(jīng)過了編譯之后,Annotation的數(shù)據(jù)就會消失,
* 并不會保留在編譯好的.class文件里面。
*
* ClASS:意思是這個Annotation類型的信息保留在程序源碼里,同時也會保留在編譯好的.class文件里面,在執(zhí)行的時候,
* 并不會把這一些信息加載到虛擬機(jī)(JVM)中去.注意一下,當(dāng)你沒有設(shè)定一個Annotation類型的Retention值時,系統(tǒng)默認(rèn)值是CLASS.
*
* RUNTIME:表示在源碼、編譯好的.class文件中保留信息,在執(zhí)行的時候會把這一些信息加載到JVM中去的.
*/
public @interface PersonInject {
String name();
int age();
// 上面兩個可以類比函數(shù)的形參
}
TestDemo.java
public class TestDemo {
@PersonInject(name = "小明", age = 19) // 僅僅是添加注解
Person person;
public static void main(String[] args) throws Exception {
// 現(xiàn)在才開始真正注射
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);
// 設(shè)置值
System.out.println(demo.person);
}
}
開始編寫小型注解框架
好啦,現(xiàn)在我們把前面的東西用到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();
// 這里默認(rèn)是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作為參數(shù)就可以了.
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注解成功!");
}
}