您的位置:首页 > 移动开发 > Android开发

Android反射例子的学习

2015-12-28 09:17 531 查看

例子摘自李宁先生的面试宝典

反射修改 SharedPreferences 的保存路径

代码

public void saveToSDcard(Context context) {
try {
/* 获取 ContextWrapper 对象中的 mBase 变量 */
Field field = ContextWrapper.class.getDeclaredField("mBase");
field.setAccessible(true);

/* 获取mBase变量的值 */
Object object = field.get(context);

/* 获取 ContextImpl.mPreferencesDir 变量 */
field = object.getClass().getDeclaredField("mPreferencesDir");
field.setAccessible(true);

/* 创建自定义路径 */
File file = new File("/sdcard");

/* 修改 mPreferencesDir 变量的值 */
field.set(object, file);

/* 执行该语句,在 /sdcard 目录中创建一个 save_data.xml 文件 */
SharedPreferences sharedPreferences = context.getSharedPreferences("save_data", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("afra55", "AAA啊哈");
editor.commit();
} catch (Exception e) {
e.printStackTrace();
}
}


分析源码:

找到 ContextWrapper.java中的方法 getSharedPreferences:

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
return mBase.getSharedPreferences(name, mode);
}


getSharedPreferences 调用 mBase.getSharedPreferences(name, mode) 返回SharedPreferences 对象。

public class ContextWrapper extends Context {
Context mBase;
......
}


跟踪 Context 类,找到了抽象方法:

public abstract SharedPreferences getSharedPreferences(String name,
int mode);


在这里可以看到getSharedPreferences 方法肯定被某个类实现了,那么用mBase变量获取该类的名称。

try {
Field field = ContextWrapper.class.getDeclaredField("mBase");
field.setAccessible(true);
Object object = field.get(context);
object.getClass().getName(); // 获取到名字
} catch (Exception e) {
e.printStackTrace();
}


知道了实现抽象方法的类是 android.app.ContextImpl, 在ContextImpl 中找到了实现方法:

@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
if (sSharedPrefs == null) {
sSharedPrefs = new ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>>();
}

final String packageName = getPackageName();
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
sSharedPrefs.put(packageName, packagePrefs);
}

// At least one application in the world actually passes in a null
// name.  This happened to work because when we generated the file name
// we would stringify it to "null.xml".  Nice.
if (mPackageInfo.getApplicationInfo().targetSdkVersion <
Build.VERSION_CODES.KITKAT) {
if (name == null) {
name = "null";
}
}

sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name); // 这里-----
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
}
}
if ((mode & Context.MODE_MULTI_PROCESS) != 0 ||
getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
// If somebody else (some other process) changed the prefs
// file behind our back, we reload it.  This has been the
// historical (if undocumented) behavior.
sp.startReloadIfChangedUnexpectedly();
}
return sp;
}


@Override
public File getSharedPrefsFile(String name) {
return makeFilename(getPreferencesDir(), name + ".xml");
}
private File getPreferencesDir() {
synchronized (mSync) {
if (mPreferencesDir == null) {
mPreferencesDir = new File(getDataDirFile(), "shared_prefs");
}
return mPreferencesDir;
}
}


可以看到 ContextImpl 中有个变量 mPreferencesDir 保存存储路径,因此通过反射修改该变量即可。

private File mPreferencesDir;


反射学习





/**
* 根据给定的类型名和字段名,返回R文件中的字段的值
*
* @param typeName  属于哪个类别的属性 (id,layout,drawable,string,color,attr......)
* @param fieldName 字段名
* @return 字段的值
* @throws Exception
*/
public static int getFieldValue(String typeName, String fieldName, Context context) {
int i = -1;
try {
Class<?> clazz = Class.forName(context.getPackageName() + ".R$" + typeName);
i = clazz.getField(fieldName).getInt(null);
} catch (Exception e) {
Log.d("" + context.getClass(), "没有找到" + context.getPackageName() + ".R$" + typeName + "类型资源 " + fieldName + "请copy相应文件到对应的目录.");
return -1;
}
return i;
}


可视化截图

// 打开图像缓存
view.setDrawingCacheEnabled(true);

// 必须调用measure和layout方法才能成功保存可视组建的截图到png图像文件
view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
// 发送位置和尺寸到View及所有的子View
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
try{
// 获取可视组件的截图
Bitmap bitmap = view.getDrawingCache();

// 将截图保存到SD卡目录下本app名文件夹下的.png图像
String path = "/sdcard/"+getResources().getString(R.string.app_name) + "/" ;
FileOutputStream outputStream = new FileOutputStream(path + getClass().getSimpleName() + ".png");
// 将Bitmap对象中的数据压缩成Png格式,并保存
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
showToast("已截屏到" + path + "目录下");
}catch (Exception e){
showToast("截屏失败");
}


修改ListView的快速滑块图像



反射基础

http://codekk.com/blogs/detail/5596953ed6459ae7934997c5

举例所用代码

public class Person {
String mName;

public Person(String aName) {
mName = aName;
}

private void sayHello(String friendName) {
System.out.println(mName + " say hello to " + friendName);
}

protected void showMyName() {
System.out.println("My name is " + mName);
}

public void breathe() {
System.out.println(" take breathe ");
}
}


public class Student extends Person implements Examination {
// 年级
int mGrade;

public Student(String aName) {
super(aName);
}

public Student(int grade, String aName) {
super(aName);
mGrade = grade;
}

private void learn(String course) {
System.out.println(mName + " learn " + course);
}

public void takeAnExamination() {
System.out.println(" takeAnExamination ");
}

public String toString() {
return " Student :  " + mName;
}


// 呼吸接口
public interface Breathe {
public void breathe();
}


// 考试接口
public interface Examination {
public void takeAnExamination();
}


获取类的Class对象

// 知道类的名字
Class<?> myObjectClass = MyObject.class;


// 通过对象获取Class对象
Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();


// 通过完整路径获取
Class<?> myObjectClass = Class.forName("com.simple.User");


接口说明

// 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.simple.Student". ( 常用方式 )
public static Class<?> forName (String className)

// 加载指定的 Class 对象,参数 1 为要加载的类的完整路径,例如"com.simple.Student";
// 参数 2 为是否要初始化该 Class 对象,参数 3 为指定加载该类的 ClassLoader.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)


通过反射构造对象

private static void classForName() {
try {
// 获取 Class 对象
Class<?> clz = Class.forName("org.java.advance.reflect.Student");
// 通过 Class 对象获取 Constructor,Student 的构造函数有一个字符串参数
// 因此这里需要传递参数的类型 ( Student 类见后面的代码 )
Constructor<?> constructor = clz.getConstructor(String.class);
// 通过 Constructor 来创建 Student 对象
Object obj = constructor.newInstance("mr.simple");
System.out.println(" obj :  " + obj.toString());
} catch (Exception e) {
e.printStackTrace();
}
}


获取构造函数接口

// 获取一个公有的构造函数,参数为可变参数,如果构造函数有参数,那么需要将参数的类型传递给 getConstructor 方法
public Constructor<T> getConstructor (Class...<?> parameterTypes)
// 获取目标类所有的公有构造函数
public Constructor[]<?> getConstructors ()


在反射调用之前将此对象的 accessible 标志设置为 true,以此来提升反射速度。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

Constructor<?> constructor = clz.getConstructor(String.class);
// 设置 Constructor 的 Accessible
constructor.setAccessible(true);

// 设置 Methohd 的 Accessible
Method learnMethod = Student.class.getMethod("learn", String.class);
learnMethod.setAccessible(true);


反射获取类中函数

获取当前类中定义的方法

要获取当前类中定义的所有方法可以通过 Class 中的 getDeclaredMethods 函数,它会获取到当前类中的 public、default、protected、private 的所有方法。

private static void showDeclaredMethods() {
Student student = new Student("mr.simple");
Method[] methods = student.getClass().getDeclaredMethods();
for (Method method : methods) {
System.out.println("declared method name : " + method.getName());
}

try {
// getDeclaredMethod(String name, Class...<?> parameterTypes),获取某个指定的方法
Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class);
// 获取方法的参数类型列表
Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
for (Class<?> class1 : paramClasses) {
System.out.println("learn 方法的参数类型 : " + class1.getName());
}
// 是否是 private 函数,属性是否是 private 也可以使用这种方式判断
System.out.println(learnMethod.getName() + " is private "
+ Modifier.isPrivate(learnMethod.getModifiers()));
learnMethod.invoke(student, "java ---> ");
} catch (Exception e) {
e.printStackTrace();
}
}


获取当前类、父类中定义的公有方法

private static void showMethods() {
Student student = new Student("mr.simple");
// 获取所有方法
Method[] methods = student.getClass().getMethods();
for (Method method : methods) {
System.out.println("method name : " + method.getName());
}

try {
// 通过 getMethod 只能获取公有方法,如果获取私有方法则会抛出异常,比如这里就会抛异常
Method learnMethod = student.getClass().getMethod("learn", String.class);
// 是否是 private 函数,属性是否是 private 也可以使用这种方式判断
System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers()));
// 调用 learn 函数
learnMethod.invoke(student, "java");
} catch (Exception e) {
e.printStackTrace();
}
}


接口说明

// 获取 Class 对象中指定函数名和参数的函数,参数一为函数名,参数 2 为参数类型列表
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)

// 获取该 Class 对象中的所有函数( 不包含从父类继承的函数 )
public Method[] getDeclaredMethods ()

// 获取指定的 Class 对象中的**公有**函数,参数一为函数名,参数 2 为参数类型列表
public Method getMethod (String name, Class...<?> parameterTypes)

// 获取该 Class 对象中的所有**公有**函数 ( 包含从父类和接口类集成下来的函数 )
public Method[] getMethods ()


这里需要注意的是 getDeclaredMethod 和 getDeclaredMethods 包含 private、protected、default、public 的函数,并且通过这两个函数获取到的只是在自身中定义的函数,从父类中集成的函数不能够获取到。而 getMethod 和 getMethods 只包含 public 函数,父类中的公有函数也能够获取到。

反射获取类中的属性

获取当前类中定义的属性

要获取当前类中定义的所有属性可以通过 Class 中的 getDeclaredFields 函数,它会获取到当前类中的 public、default、protected、private 的所有属性。

private static void showDeclaredFields() {
Student student = new Student("mr.simple");
// 获取当前类和父类的所有公有属性
Field[] publicFields = student.getClass().getDeclaredFields();
for (Field field : publicFields) {
System.out.println("declared field name : " + field.getName());
}

try {
// 获取当前类和父类的某个公有属性
Field gradeField = student.getClass().getDeclaredField("mGrade");
// 获取属性值
System.out.println(" my grade is : " + gradeField.getInt(student));
// 设置属性值
gradeField.set(student, 10);
System.out.println(" my grade is : " + gradeField.getInt(student));
} catch (Exception e) {
e.printStackTrace();
}
}


获取当前类、父类中定义的公有属性

要获取当前类以及父类中的所有 public 属性可以通过 Class 中的 getFields 函数,而 getField 则是获取某个指定的属性。

private static void showFields() {
Student student = new Student("mr.simple");
// 获取当前类和父类的所有公有属性
Field[] publicFields = student.getClass().getFields();
for (Field field : publicFields) {
System.out.println("field name : " + field.getName());
}

try {
// 获取当前类和父类的某个公有属性
Field ageField = student.getClass().getField("mAge");
System.out.println(" age is : " + ageField.getInt(student));
} catch (Exception e) {
e.printStackTrace();
}
}


接口说明

// 获取 Class 对象中指定属性名的属性,参数一为属性名
public Method getDeclaredField (String name)

// 获取该 Class 对象中的所有属性( 不包含从父类继承的属性 )
public Method[] getDeclaredFields ()

// 获取指定的 Class 对象中的**公有**属性,参数一为属性名
public Method getField (String name)

// 获取该 Class 对象中的所有**公有**属性 ( 包含从父类和接口类集成下来的公有属性 )
public Method[] getFields ()


这里需要注意的是 getDeclaredField 和 getDeclaredFields 包含 private、protected、default、public 的属性,并且通过这两个函数获取到的只是在自身中定义的属性,从父类中集成的属性不能够获取到。而 getField 和 getFields 只包含 public 属性,父类中的公有属性也能够获取到。

反射获取父类与接口

获取父类

Student student = new Student("mr.simple");
Class<?> superClass = student.getClass().getSuperclass();
while (superClass != null) {
System.out.println("Student's super class is : " + superClass.getName());
// 再获取父类的上一层父类,直到最后的 Object 类,Object 的父类为 null
superClass = superClass.getSuperclass();
}


获取接口

private static void showInterfaces() {
Student student = new Student("mr.simple");
Class<?>[] interfaceses = student.getClass().getInterfaces();
for (Class<?> class1 : interfaceses) {
System.out.println("Student's interface is : " + class1.getName());
}
}


获取注解信息

注解的@target 表示该注解只能用在函数上,还有 Type、Field、PARAMETER 等类型,可以参考上述给出的参考资料。通过反射 api 我们也能够获取一个 Class 对象获取类型、属性、函数等相关的对象,通过这些对象的 getAnnotation 接口获取到对应的注解信息。

@Target({
ElementType.METHOD, ElementType.FIELD, ElementType.TYPE
})
@Retention(RetentionPolicy.RUNTIME)
static @interface Test {

}


@Test(tag = "Student class Test Annoatation")
public class Student extends Person implements Examination {
// 年级
@Test(tag = "mGrade Test Annotation ")
int mGrade;

// ......
}


private static void getAnnotationInfos() {
Student student = new Student("mr.simple");
Test classTest = student.getClass().getAnnotation(Test.class);
System.out.println("class Annotatation tag = " + classTest.tag());

Field field = null;
try {
field = student.getClass().getDeclaredField("mGrade");
Test testAnnotation = field.getAnnotation(Test.class);
System.out.println("属性的 Test 注解 tag : " + testAnnotation.tag());
} catch (Exception e) {
e.printStackTrace();
}
}


接口说明

// 获取指定类型的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) ;
// 获取 Class 对象中的所有注解
public Annotation[] getAnnotations() ;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: