一,反射:说白了反射主要解决那些我们平时解决不了的问题。比如一个类的构造,属性,普通方法为私有的情况下,或者某些方法虽然不是私有的方法但是是被隐藏的方法,我们就需要使用反射才能获得他们。一般使用Class类来完成反射,这个类把成员变量划分为Filed区域,把构造划分在Construct,把普通方法划分在method区域。下面就来看反射的应用。
1,通过反射获得构造方法
1.1 通过反射获得构造为空的构造方法:
首先定义一个TestBean类,testBean的构造方法是私有我空
public class TestBean {
private String name;
private TestBean(){
}
public String getName() {
return name;
}
public void systemInfo(){
Log.e("TAG","无参构造");
}
}
MainActivity的onCreate()方法中这样写:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
//newInstance()方法是基于无参构造调用的,所以需要被反射调用的类中必须要有无参构造
TestBean testBean=TestBean.class.newInstance();
testBean.systemInfo();
} catch (Exception e) {
e.printStackTrace();
}
}
1.2通过反射获得构造不为空的构造方法:
首先定义一个TestBean类,testBean的构造方法是私有不为空
public class TestBean {
private String name;
private TestBean(String name){
this.name=name;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void systemInfo(){
Log.e("TAG",this.name);
}
}
MainActivity的onCreate()方法中这样写:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这种是带构造参数的
try {
//如果有两个参数,第二个参数是int型的,那么第二个参数就应该写成int.class
Constructor<TestBean> constructor= TestBean.class.getDeclaredConstructor(String.class);
constructor.setAccessible(true);//设置权限
//如果有两个参数第二个参数是int型的,那么第二个参数在这里直接写数字
TestBean testBean=constructor.newInstance("yaoliangyong");//调用构造方法传参数
testBean.systemInfo();
} catch (Exception e) {
e.printStackTrace();
}
}
输出结果是:
2019-06-18 14:26:29.959 22982-22982/com.example.didi.myproject E/TAG: yaoliangyong
2,通过反射获取私有的方法
定义一个类,这个类中有一个私有的方法
import android.util.Log;
public class TestBean {
private String name;
public TestBean(String name){
this.name=name;
}
public String getName() {
return name;
}
private void systemInfo(){
Log.e("TAG","私有方法");
}
}
MainActivity中的写法:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这种是带构造参数的
try {
//newInstance()方法是基于无参构造调用的,所以需要被反射调用的类中必须要有无参构造
TestBean testBean=new TestBean("yaoliangyogn1");
//因为systemInfo()方法没有参数可以这样写,如果一个方法有参数(假设为String)的话就要写成
//Method method=TestBean.class.getDeclaredMethod("systemInfo",String.class);
Method method=TestBean.class.getDeclaredMethod("systemInfo");
//这句话是说谁去执行它
method.setAccessible(true);
method.invoke(testBean);
//这是有参数的使用用的
//method.invoke(testBean,"zhenxin1");
} catch (Exception e) {
e.printStackTrace();
}
}
输出结果:
2019-06-18 15:28:06.017 28957-28957/? E/TAG: 私有方法
3,通过反射获取属性:
首先定义一个类
public class TestBean {
private String name="真心英雄";
public TestBean(){
}
public String getName() {
return name;
}
}
MainActivity中的写法:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//这种是带构造参数的
try {
//newInstance()方法是基于无参构造调用的,所以需要被反射调用的类中必须要有无参构造
TestBean testBean=new TestBean();
Field field= testBean.getClass().getDeclaredField("name");
field.setAccessible(true);
String name= (String) field.get(testBean);
Log.e("TAG",name);
} catch (Exception e) {
e.printStackTrace();
}
}
二,注解
注解知识一个标识,下面是注解的基本使用方法
1,创建一个ViewById接口
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)//这表示放在属性上面
@Retention(RetentionPolicy.RUNTIME)//这表示运行时起作用,也就是App已经起来了
//@Retention(RetentionPolicy.CLASS)这表示编译时,代表点了上面的三角按钮起作用
public @interface ViewById {
int value();//这表示只能放一个参数,如果想放两个参数的话可以使用int[] value();
//若果想放一个String类型的参数,可以使用
//String name="";
}
2,在MAinActivity中这样使用:
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.widget.TextView;
public class MainActivity extends Activity {
@ViewById(R.id.text_view)
private TextView textView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView.setText("textView");
}
}
当然,这样肯定还是编译不过的,但是大致使用是这样的,具体使用方法请看下面的讲解:
首先新建一个注解接口ViewById
mport java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)//这表示放在属性上面
@Retention(RetentionPolicy.RUNTIME)//这表示运行时起作用,也就是App已经起来了
//@Retention(RetentionPolicy.CLASS)这表示编译时,代表点了上面的三角按钮起作用
public @interface ViewById {
int value();//这表示只能放一个参数,如果想放两个参数的话可以使用int[] value();
//若果想放一个String类型的参数,可以使用
//String name="";
}
接着新建一个类
import android.app.Activity;
import android.view.View;
import java.lang.reflect.Field;
//所有的注解还是需要用代码实现的,不可能用了注解之后就不使用findViewById()了
public class ViewUtils {
public static void inject(Activity activity){
//1,找到MainActivity中的所有属性
Field[] fields=activity.getClass().getDeclaredFields();//这就获取到了所有属性,也包括viewPager,当然我们并不关心viewPager属性
//2,过滤关于ViewById属性(之所以过滤是因为不能任何属性都需要注解)
//一般使用for循环过滤
for (Field field:fields){
ViewById viewById=field.getAnnotation(ViewById.class);
if (viewById!=null){
//3,findVieById肯定是不能少
View view=activity.findViewById(viewById.value());
//4,反射注入(把找到的属性注入)
field.setAccessible(true);
try {
//第一个参数代表属性所在类,第二个属性代表属性值
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
在MAinActivity中的写法:
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.widget.TextView;
public class MainActivity extends Activity{
@ViewById(R.id.text_view)
private TextView textView;
int viewPager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewUtils.inject(this);
textView.setText("textView");
}
}