反射
??JAVA反射機制是在運行狀態(tài)中,對于任意一個類助隧,都能夠知道這個類的所有屬性和方法;對于任意一個對象,都能夠調(diào)用它的任意一個方法和屬性并村;這種動態(tài)獲取的信息以及動態(tài)調(diào)用對象的方法的功能稱為java語言的反射機制巍实。
1.獲取Class對象
舉個例子,我們要獲取MainActivity的Class對象哩牍,有以下幾種方式
1.
Class<MainActivity> clazz = MainActivity.class;
2.Class<? extends MainActivity> clazz = this.getClass();
3.Class<?> clazz = Class.forName("utils.utilcode.blankj.com.dongtai.MainActivity");
2.通過Class對象獲取構(gòu)造方法棚潦,成員變量,方法等
構(gòu)造函數(shù)
getConstructor();//獲取本類或父類中public修飾的無參構(gòu)造函數(shù)
getDeclaredConstructor();//獲取本類無參構(gòu)造函數(shù)
getDeclaredConstructor(Class<?>... parameterTypes): 獲取本類中指定參數(shù)的構(gòu)造器
成員變量
getFields(): 獲取本類或父類中所有public屬性
getField(String name): 獲取本類或父類中特定名字的public屬性
getDeclaredFields(): 獲取本類中聲明的所有屬性
獲取方法
getMethods(): 獲取本類或父類中所有public方法(包括構(gòu)造器方法)
getMethod(String name, Class<?>... parameterTypes): 獲取本類或父類中特定名字和參數(shù)的public方法
getDeclaredMethods(): 獲取本類中聲明的所有方法(包括非public但不包括繼承來的)
getDeclaredMethod(String name, Class<?>... parameterTypes): 獲取本類中聲明的特定名字和參數(shù)的方法(最常用)
獲取注解
getAnnotation(Class<A> annotationClass): 獲取這個元素上指定類型的注解(常用)
getDeclaredAnnotations(): 獲取直接標注在這個元素上的注解
父類子類(接口)相關(guān)
getSuperclass(): 返回本類的父類
getGenericSuperclass(): 以Type的形式返回本類的父類, 帶有范型信息
getInterfaces(): 返回本類直接實現(xiàn)的接口
getGenericInterfaces(): 以Type的形式返回本類直接實現(xiàn)的接口, 帶有范型信息
自定義注解
新建一個InjectView類
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
int value();
}
Target表示作用范圍膝昆,這里表示作用于成員變量
/** Targe表示作用范圍
* TYPE 作用對象類/接口/枚舉
* FIELD 成員變量
* METHOD 成員方法
* PARAMETER 方法參數(shù)
* ANNOTATION_TYPE 注解的注解
*/
Retention指定了注解有效期直到運行時時期
value就是用來指定id丸边,也就是findViewById的參數(shù)
在MainActivity中添加注解
@InjectView(R.id.bind_view_btn)
Button mBindView;
@InjectView(R.id.textView)
TextView textView;
在MainActivity的OnCreate方法中進行注入
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Utils.injectView(this);
新建Utils這個類,創(chuàng)建injectView方法
代碼中的每一行都有詳細的注釋
public static void injectView(Activity activity) {
if (null == activity) return;
//獲取activity的.class對象
Class<? extends Activity> activityClass = activity.getClass();
// 獲取所有成員變量集合
Field[] declaredFields = activityClass.getDeclaredFields();
for (Field field : declaredFields) {
//找到有@InjectView注解的成員變量
if (field.isAnnotationPresent(InjectView.class)) {
//得到注解類的對象
InjectView annotation = field.getAnnotation(InjectView.class);
//找到VIew的id
int value = annotation.value();
try {
//找到findViewById方法
Method findViewByIdMethod = activityClass.getMethod("findViewById", int.class);
//暴力訪問荚孵,可獲取私有方法
findViewByIdMethod.setAccessible(true);
View view = (View) findViewByIdMethod.invoke(activity, value);
//將結(jié)果賦值給成員變量
field.set(activity, view);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
首先是獲取activity的.class對象妹窖,通過Class對象找到該類中所有的成員變量。繼而找到有InjectView注解的成員變量收叶,然后得到注解類的對象骄呼,找到VIew的id,通過反射獲取Activity的findViewById方法判没,然后將結(jié)果傳給成員變量蜓萄。
注解onClick點擊事件
新建一個onClick類
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")
public @interface onClick {
int[] value();
}
Target指定了onClick注解作用對象是成員方法
Retention指定了onClick注解有效期直到運行時時期
value就是用來指定id,也就是findViewById的參數(shù)
新建一個EventType類
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventType {
Class listenerType();
String listenerSetter();
String methodName();
}
Target指定了EventType注解作用對象是注解澄峰,也就是注解的注解
Retention指定了EventType注解有效期直到運行時時期
listenerType用來指定點擊監(jiān)聽類型嫉沽,比如OnClickListener
listenerSetter用來指定設(shè)置點擊事件方法,比如setOnClickListener
methodName用來指定點擊事件發(fā)生后會回調(diào)的方法摊阀,比如onClick
MainActivity中添加注解
@onClick({R.id.button})
public void onclick(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
switch (view.getId()) {
case R.id.button:
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
break;
}
}
記得注入onClick事件
setContentView(R.layout.activity_main);
Utils.injectView(this);
Utils.injectEvent(this);
創(chuàng)建injectEvent方法
public static void injectEvent(Activity activity) {
if (null == activity) {
return;
}
//獲取activity的.class對象
Class<? extends Activity> activityClass = activity.getClass();
// 獲取所有方法
Method[] declaredMethods = activityClass.getDeclaredMethods();
for (Method method : declaredMethods) {
//找到有@onClick注解的方法
if (method.isAnnotationPresent(onClick.class)) {
//得到注解類的對象
onClick annotation = method.getAnnotation(onClick.class);
//獲取控件id的集合
int[] value = annotation.value();
//獲取注解類中的注解
EventType eventType = annotation.annotationType().getAnnotation(EventType.class);
//得到注解類中的注解的屬性
Class listenerType = eventType.listenerType();
String listenerSetter = eventType.listenerSetter();
String methodName = eventType.methodName();
//創(chuàng)建InvocationHandler和動態(tài)代理(代理要實現(xiàn)listenerType耻蛇,這個例子就是處理onClick點擊事件)
ProxyHandler proxyHandler = new ProxyHandler(activity);
//得到實現(xiàn)類的接口對象(1:實現(xiàn)類的類加載器2:實現(xiàn)類的接口數(shù)組3:proxyHandler對象(當執(zhí)行接口的方法時會先執(zhí)行里面的invoke,再執(zhí)行實際方法胞此,實現(xiàn)動態(tài)代理)
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, proxyHandler);
// 將Activity中被注解的方法傳至ProxyHandler 中
proxyHandler.mapMethod(methodName, method);
try {
for (int id : value) {
//找到Button
Method findViewByIdMethod = activityClass.getMethod("findViewById", int.class);
findViewByIdMethod.setAccessible(true);
View btn = (View) findViewByIdMethod.invoke(activity, id);
//根據(jù)listenerSetter方法名和listenerType方法參數(shù)找到method
Method listenerSetMethod = btn.getClass().getMethod(listenerSetter, listenerType);
listenerSetMethod.setAccessible(true);
//反射執(zhí)行setOnclickListener
listenerSetMethod.invoke(btn, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
ProxyHandler代碼如下
public class ProxyHandler implements InvocationHandler {
private WeakReference<Activity> mHandlerRef;
private HashMap<String, Method> mMethodHashMap;
public ProxyHandler(Activity activity) {
mHandlerRef = new WeakReference<>(activity);
mMethodHashMap = new HashMap<>();
}
public void mapMethod(String name, Method method) {
mMethodHashMap.put(name, method);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.i("TAG", "method name = " + method.getName() + " and args = " + Arrays.toString(args));
Object handler = mHandlerRef.get();
if (null == handler) return null;
String name = method.getName();
//得到injectEvent中傳遞過來的被@onClick注解的方法
Method realMethod = mMethodHashMap.get(name);
if (null != realMethod){
//反射執(zhí)行Activity中onClick方法
return realMethod.invoke(handler, args);
}
return null;
}
}
重點是動態(tài)代理。這里簡單分析一下ProxyHandler 類跃捣,它主要是用于動態(tài)代理漱牵。這里為什么要用動態(tài)代理呢!是因為當我們點擊按鈕時疚漆,會觸發(fā)onClick方法酣胀,而我們需要在onClick里面執(zhí)行我們注解的方法,所以必須對Activity動態(tài)代理娶聘。
Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, proxyHandler);
第一個參數(shù)為接口的加載器(本例中為Activity的接口View.OnclickListener)闻镶,第二個參數(shù)為Activity的接口數(shù)組(View.OnclickListener),第三個參數(shù)為ProxyHandler 對象丸升。返回一個實現(xiàn)類(View.OnclickListener)的對象铆农。由于在injectEvent中我們反射執(zhí)行了setOnClickListener,當用戶觸發(fā)點擊事件時狡耻,會執(zhí)行
接口中的方法(setOnClickListener的onClick方法)然后會執(zhí)行ProxyHandler 類中的invoke方法墩剖,然后反射得到Activity中被@onClick注解的方法即onClick方法猴凹,然后反射調(diào)用它,執(zhí)行注解中的方法岭皂。因此
@onClick({R.id.button})
public void onclick(View view) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
switch (view.getId()) {
case R.id.button:
Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
break;
}
}
不管方法名是什么郊霎,當用戶點擊后,都會反射執(zhí)行對應(yīng)方法體
注解類對象
這次我們通過注解得到Animal 的對象
public class Animal implements Fly, Run {
public static final String TAG = "ProxyTest";
public Animal() {
}
@Override
public void fly() {
System.out.println("Animal fly");
}
@Override
public void run() {
System.out.println("Animal run");
}
}
同理新建一個自定義注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}
在MainActivity中進行注解
@Inject
Animal mAnimal;
然后注入
setContentView(R.layout.activity_main);
Utils.injectView(this);
Utils.injectEvent(this);
InjectUtils.inject(this);
InjectUtil
public class InjectUtils {
public static void inject(Activity activity) {
if (null == activity) {
return;
}
//獲取activity的.class對象
Class<? extends Activity> activityClass = activity.getClass();
// 獲取所有成員變量集合
Field[] declaredFields = activityClass.getDeclaredFields();
for (Field field : declaredFields) {
//找到有Inject注解的成員變量
if (field.isAnnotationPresent(Inject.class)) {
//得到成員變量的類型的字節(jié)碼文件
Class<?> clazz = field.getType();
Constructor con = null;
try {
//得到無參構(gòu)造
con = clazz.getConstructor();
//獲取Animal其對象
Object instance = con.newInstance();
//賦值給field
field.set(activity,instance);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
本文中所有Demo的GitHub地址為https://github.com/zhonghongwen/Custom-Annotation