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() ;
相关文章推荐
- android开发游记:弹出窗和底部弹出窗的实现和动画效果
- Android---再按一次退出页面
- 《Android源码设计模式解析与实战》读书笔记(十二)
- Android Kitkat 如何让系统永久不休眠
- Android:Attribute is missing the Android namespace prefix
- android的显式提交和隐式提交
- android删除文件出错
- 欢迎使用CSDN-markdown编辑器
- android130 android启动
- Android遇到java.lang.RuntimeException: Binary XML file line #20这样的问题怎么办
- Android开发之ActionBar与DrawerLayout
- Android官方刷新组件 SwipeRefreshLayout 的使用
- [android]_[ListView的基本使用]
- [android]_[四种基本布局]
- Android开发之应用程序创建与目录结构
- android 属性动画(view普通使用 和 自定义view使用)
- 【Android】Android插件开发 —— 打开插件的Activity(代理方式)
- 捐助android-x86 项目
- 定义 Android 清单
- 真正的Android