例子摘自李宁先生的面试宝典
反射修改 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() ;