image
反射
什么是反射? 反射:是一種機(jī)制,利用該機(jī)制可以在程序運(yùn)行過程中對類進(jìn)行解剖并操作類中的方法,屬性,構(gòu)造方法等成員。
主要是指程序可以訪問,檢測和修改它本身狀態(tài)或行為的一種能力,并能根據(jù)自身行為的狀態(tài)和結(jié)果,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語義。
反射是java中一種強(qiáng)大的工具,能夠很方便的創(chuàng)建靈活的代碼,這些代碼可以再運(yùn)行時裝配,無需在組件之間進(jìn)行源代碼鏈接。但是反射使用不當(dāng)會成本很高
反射的調(diào)用:創(chuàng)建對象Constructor:newInstance()、執(zhí)行方法Method:invoke()、設(shè)置及獲取屬性Filed: set(), get()
常用方法:
getDeclaredFields() : 獲得class的成員變量
getDeclaredMethods() :獲得class的成員方法
getDeclaredConstructors() :獲得class的構(gòu)造函數(shù)
...等等一系列方法
通過反射的方式,運(yùn)行時動態(tài)獲取注解信息。如下Demo
Demo
通過自定義注解+反射實(shí)現(xiàn)牛油刀功能
自定義注解:
@BindView
@Target(ElementType.FIELD) // 作用在屬性之上
@Retention(RetentionPolicy.RUNTIME) // 運(yùn)行時
public @interface BindView {
/** View Id */
@IdRes int value() default -1;
}
@OnClick
@Target(ElementType.METHOD) // 作用在屬性之上
@Retention(RetentionPolicy.RUNTIME) // 運(yùn)行時
public @interface OnClick {
/** View Ids */
@IdRes int[] value();
}
處理類 Reflex
public class Reflex {
/** 綁定 */
public static void bind(Activity activity) {
// 判斷傳入的Activity是否為空,為空進(jìn)行過濾不處理
if (activity == null) return;
// 查找所有使用了自定義 @BindView 注解的屬性并賦值
assignmentFields(activity);
// 查找所有使用了自定義 @OnClick 注解的方法,使用動態(tài)代理實(shí)現(xiàn)點(diǎn)擊事件
dynamicProxyMethod(activity);
}
/** 查找所有使用了自定義 @BindView 注解的屬性并賦值 */
private static void assignmentFields(Activity activity) {
// 獲取class對象
Class<? extends Activity> clazz = activity.getClass();
// 獲取所有成員屬性
Field[] fields = clazz.getDeclaredFields();
// 遍歷所有成員屬性
for (Field field : fields) {
// 判斷該屬性是否使用了自定義注解且為View
if (field.isAnnotationPresent(BindView.class)) {
// 獲取當(dāng)前屬性上的注解對象
BindView annotation = field.getAnnotation(BindView.class);
// 獲取注解的值
int idRes = annotation.value();
// 判斷注解值
if (idRes != -1) {
try {
// 設(shè)置屬性可見
field.setAccessible(true);
// 賦值
field.set(activity, activity.findViewById(idRes));
} catch (IllegalAccessException e) {
throw new RuntimeException(clazz.getName() + "的" + field.getName() + "注解異常!");
}
}
}
}
}
/** 查找所有使用了自定義 @OnClick 注解的方法,使用動態(tài)代理實(shí)現(xiàn)點(diǎn)擊事件 */
private static void dynamicProxyMethod(final Activity activity) {
// 獲取class對象
Class<? extends Activity> clazz = activity.getClass();
// 獲取所有的方法
Method[] methods = clazz.getDeclaredMethods();
// 遍歷所有的方法
for (final Method method : methods) {
// 判斷該方法是否使用了自定義注解
if (method.isAnnotationPresent(OnClick.class)) {
// 獲取當(dāng)前方法上的注解對象
OnClick annotation = method.getAnnotation(OnClick.class);
// 獲取注解的值
int[] values = annotation.value();
// 遍歷所有的值
for (int value : values) {
// 根據(jù)ID獲取View
View view = activity.findViewById(value);
// 設(shè)置方法可見
method.setAccessible(true);
// 設(shè)置View的點(diǎn)擊事件
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
try {
method.invoke(activity, view);
} catch (Exception e) {
throw new RuntimeException(activity.getClass().getName() + "的" + method.getName() + "注解異常!");
}
}
});
}
}
}
}
}
MainActivity
public class MainActivity extends AppCompatActivity {
@BindView(R.id.textView)
TextView textView;
@BindView(R.id.textView1)
TextView textView1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 使用自定義反射工具,動態(tài)獲取信息并進(jìn)行業(yè)務(wù)處理
Reflex.bind(this);
}
@OnClick({R.id.textView, R.id.textView1})
public void onClick(View view) {
switch (view.getId()) {
case R.id.textView:
Toast.makeText(this, "你好呀!", Toast.LENGTH_SHORT).show();
textView.setText("你好呀");
break;
case R.id.textView1:
Toast.makeText(this, "嗯,我很好。", Toast.LENGTH_SHORT).show();
textView1.setText("嗯,我很好。");
break;
default:
break;
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView1"
app:layout_constraintBottom_toTopOf="@+id/textView1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="TextView2"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>