反射和动态加载的灵活运用,可以减小项目开发的难度,提升项目的可维护性,是需要仔细研究的。
转自:http://blog.csdn.net/hyx1990/article/details/7584789
1.在任意位置获取应用程序Context
Android程序中访问资源时需要提供Context,一般来说只有在各种component中(Activity, Provider等等)才能方便的使用api来获取Context;喜欢编程的人都知道,编写工具类可以有效的实现代码复用,而在Android下某些工具类的编写很让人困惑,例如:我们要在工具类中获取SharedPreferences,那就需要Context的支持。
为了解决这写由Context带来的麻烦,我们可以自定义一个Application类来实现这种功能。
import android.app.Application;
public class ContextUtil extends Application {
private static ContextUtil instance;
public static ContextUtil getInstance() {
return instance;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
instance = this;
}
}
然后在manifest中<application>中加入Android:name="mypackage.ContextUtil",这样我们就可以在任何一个类下面获取Context,例如:Context c=ContextUtil.getInstance();
2.context注意事项:
在android中context可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是 application context,一种是activity context,通常我们在各种类和方法间传递的是activity context。
比如一个activity的onCreate:
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this); //传递context给view control
label.setText("Leaks are bad");
setContentView(label);
}
把activity context传递给view,意味着view拥有一个指向activity的引用,进而引用activity占有的资源:view hierachy, resource等。
这样如果context发生内存泄露的话,就会泄露很多内存。
这里泄露的意思是gc没有办法回收activity的内存。
Leaking an entire activity是很容易的一件事。
当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息,再创建一个新的。
比如我们写了一个应用程序,它需要加载一个很大的图片,我们不希望每次旋转屏 幕的时候都销毁这个图片,重新加载。实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity 类创建销毁它始终保存在内存中。
实现类似:
public class myactivity extends Activity {
private static Drawable sBackground;
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);//drawable attached to a view
setContentView(label);
}
}
这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有leak(即gc没法销毁activity)。
我们刚才说过,屏幕旋转的时候系统会销毁当前的activity。但是当drawable和view关联后,drawable保存了view的 reference,即sBackground保存了label的引用,而label保存了activity的引用。既然drawable不能销毁,它所 引用和间接引用的都不能销毁,这样系统就没有办法销毁当前的activity,于是造成了内存泄露。gc对这种类型的内存泄露是无能为力的。
避免这种内存泄露的方法是避免activity中的任何对象的生命周期长过activity,避免由于对象对 activity的引用导致activity不能正常被销毁。我们可以使用application context。application context伴随application的一生,与activity的生命周期无关。application context可以通过Context.getApplicationContext或者Activity.getApplication方法获取。
避免context相关的内存泄露,记住以下几点:
1. 不要让生命周期长的对象引用activity context,即保证引用activity的对象要与activity本身生命周期是一样的
2. 对于生命周期长的对象,可以使用application context
3. 避免非静态的内部类,尽量使用静态类,避免生命周期问题,注意内部类对外部对象引用导致的生命周期变化
3.获取别的包的Context
Android中有Context的概念,想必大家都知道。Context可以做很多事情,打开activity、发送广播、打开本包下文件夹和数据库、获取classLoader、获取资源等等。如果我们得到了一个包的Context对象,那我们基本上可以做这个包自己能做的大部分事情。
Context有个createPackageContext方法,可以创建另外一个包的上下文,这个实例不同于它本身的Context实例,但是功能是一样的。
这个方法有两个参数:
1。packageName 包名,要得到Context的包名
2。flags 标志位,有CONTEXT_INCLUDE_CODE和CONTEXT_IGNORE_SECURITY两个选项。 CONTEXT_INCLUDE_CODE的意思是包括代码,也就是说可以执行这个包里面的代码。CONTEXT_IGNORE_SECURITY的意思是忽略安全警告,如果不加这个标志的话,有些功能是用不了的,会出现安全警告。
下面给个小例子,执行另外一个包里面的某个类的方法。
另外一个包的包名是chroya.demo,类名Main,方法名print,代码如下:
- package chroya.demo;
- import android.app.Activity;
- import android.os.Bundle;
- import android.util.Log;
- class Main extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
- public void print(String msg) {
- Log.d("Main", "msg:"+ msg);
- }
- }
- package chroya.demo;
- import android.app.Activity;
- import android.os.Bundle;
- import android.util.Log;
- class Main extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
- public void print(String msg) {
- Log.d("Main", "msg:"+ msg);
- }
- }
本包的调用Main的print方法的代码块如下:
- Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
- //载入这个类
- Class clazz = c.getClassLoader().loadClass("chroya.demo.Main");
- //新建一个实例
- Object owner = clazz.newInstance();
- //获取print方法,传入参数并执行
- Object obj = clazz.getMethod("print", String.class).invoke(owner,"Hello");
- Context c = createPackageContext("chroya.demo", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
- //载入这个类
- Class clazz = c.getClassLoader().loadClass("chroya.demo.Main");
- //新建一个实例
- Object owner = clazz.newInstance();
- //获取print方法,传入参数并执行
- Object obj = clazz.getMethod("print", String.class).invoke(owner, "Hello");
ok,这样,我们就调用了chroya.demo包的Main类的print方法,执行结果,打印出了Hello。
转自:
一、反射的概念及在Java中的类反射
反射主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。在计算机科学领域,反射是一类应用,它们能够自描述和自控制。这类应用通过某种机制来实现对自己行为的描述和检测,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。
好,了解这些,那我们就知道了,我们可以利用反射机制在Java程序中,动态的去调用一些protected甚至是private的方法或类,这样可以很大程度上满足我们的一些比较特殊需求。你当然会问,反射机制在Android平台下有何用处呢?
我们在进行Android程序的开发时,为了方便调试程序,并快速定位程序的错误点,会从网上下载到对应版本的Android
那么,对于这个问题,第一种方法就是自己去掉Android源码中的"@hide"标记,然后重新编译生成一个SDK。另一种方法就是使用Java反射机制了,可以利用这种反射机制访问存在访问权限的方法或修改其域。
废话半天,该入正题了,在进入正题之前,先给上一个反射测试类的代码,该代码中定义了我们需要进行反射的类,该类并没有实际的用途,仅供做为测试类。提示:本文提供的代码,并不是Android平台下的代码,而是一个普通的Java程序,仅仅是对Java反射机制的Demo程序,所以大家不要放在Android下编译啊,否则出现问题,别追究我的责任啦!

importjava.awt.event.ActionListener;
importjava.io.Serializable; publicclass ReflectionTest extends Object implements ActionListener,Serializable{ //成员变量
private int bInt;
public Integer bInteger = new Integer(4);
public String strB = "crazypebble";
private String strA; //构造函数
public ReflectionTest() {
}
protectedReflectionTest(int id, String name) {}
//成员方法public int abc(int id, String name) {
System.out.println("crazypebble --->" + id + "-" + name);
return 0;
} protectedstatic void edf() {
}
@Override
// TODO Auto-generated methodstub
}
}
二、反射机制中需要使用到的类
我把需要使用的类列在下表中,其中对我们特别有用的类,通过着重标记显示出来,并将在后面的使用中逐步解释:
三、Class类
首先向大家说明一点,Class本身就是一个类,Class是该类的名称。看以下下面这个类的定义:
public
Class类是整个Java反射机制的源头,Class类本身表示Java对象的类型,我们可通过一个Object对象的getClass()方法取得一个对象的类型,此函数返回的就是一个Class类。获取Class对象的方法有很多种:
在平时的使用,要注意对这几种方法的灵活运用,尤其是对Class.forName()方法的使用。因为在很多开发中,会直接通过类的名称取得Class类的对象。
四、获取类的相关信息
1、获取构造方法
Class类提供了四个public方法,用于获取某个类的构造方法。
Constructor
Constructor
Constructor
Constructor
由于Java语言是一种面向对象的语言,具有多态的性质,那么我们可以通过构造方法的参数列表的不同,来调用不同的构造方法去创建类的实例。同样,获取不同的构造方法的信息,也需要提供与之对应的参数类型信息;因此,就产生了以上四种不同的获取构造方法的方式。

public static void get_Reflection_Constructors(ReflectionTest r){
Class temp
=r.getClass();String className =temp.getName(); // 获取指定类的类名
try {
Constructor[] theConstructors = temp.getDeclaredConstructors();// 获取指定类的公有构造方法
for (int i = 0;i <theConstructors.length;i++) {
int mod = theConstructors[i].getModifiers();// 输出修饰域和方法名称
System.out.print(Modifier.toString(mod)+ " " + className + "(");
Class[] parameterTypes
= theConstructors[i].getParameterTypes();// 获取指定构造方法的参数的集合for (int j = 0;j <parameterTypes.length;j++) { // 输出打印参数列表
System.out.print(parameterTypes[j].getName());
if (parameterTypes.length > j+1){
System.out.print(", ");
}
}
System.out.println(")");
}
} catch(Exception e) {
e.printStackTrace();
}
}
2、获取类的成员方法
与获取构造方法的方式相同,存在四种获取成员方法的方式。
Method
Method[]
Method
Method[]

public static void get_Reflection_Method(ReflectionTest r) {
Class temp
=r.getClass();String className =temp.getName();
//Method[] methods =temp.getDeclaredMethods();
Method[] methods = temp.getMethods(); for(int i = 0;i <methods.length;i++) { //打印输出方法的修饰域
int mod = methods[i].getModifiers();
System.out.print(Modifier.toString(mod) + " "); //输出方法的返回类型
System.out.print(methods[i].getReturnType().getName()); //获取输出的方法名
System.out.print(" " + methods[i].getName() + "("); //打印输出方法的参数列表
Class[] parameterTypes = methods[i].getParameterTypes();
for (int j = 0;j <parameterTypes.length;j++) {
System.out.print(parameterTypes[j].getName());
if (parameterTypes.length > j+1){
System.out.print(", ");
}
}
System.out.println(")");
}
}
在获取类的成员方法时,有一个地方值得大家注意,就是getMethods()方法和getDeclaredMethods()方法。
getMethods():用于获取类的所有的public修饰域的成员方法,包括从父类继承的public方法和实现接口的public方法;
getDeclaredMethods():用于获取在当前类中定义的所有的成员方法和实现的接口方法,不包括从父类继承的方法。
大家可以查考一下开发文档的解释:
getDeclaredMethods()
因此在示例代码的方法get_Reflection_Method(...)中,ReflectionTest类继承了Object类,实现了actionPerformed方法,并定义如下成员方法:
通过这两个语句执行后的结果不同:
a、Method[]
b、Method[]
3、获取类的成员变量(成员属性)
存在四种获取成员属性的方法
Field
Field[]
Field
Field[]

public static void get_Reflection_Field_Value(ReflectionTest r){
Class temp
=r.getClass(); // 获取Class类的对象的方法之一try {
System.out.println("public属性");
Field[] fb =temp.getFields();
for (int i = 0;i <fb.length; i++){
Class cl
=fb[i].getType();// 属性的类型int md = fb[i].getModifiers(); // 属性的修饰域
Field f =temp.getField(fb[i].getName());// 属性的值
f.setAccessible(true);
Object value =(Object)f.get(r); //判断属性是否被初始化
if (value == null){
System.out.println(Modifier.toString(md) + " " + cl + " : " + fb[i].getName());
}
else{
System.out.println(Modifier.toString(md) + " " + cl + " : " + fb[i].getName() + " = " + value.toString());
}
}
System.out.println(
"public& 非public 属性");Field[] fa =temp.getDeclaredFields();
for (int i = 0;i <fa.length; i++){
Class cl
=fa[i].getType();// 属性的类型int md = fa[i].getModifiers(); // 属性的修饰域
Field f =temp.getDeclaredField(fa[i].getName());// 属性的值
f.setAccessible(true); // Very Important
Object value = (Object) f.get(r); if(value == null){
System.out.println(Modifier.toString(md) + " " + cl + " : " + fa[i].getName());
}
else{
System.out.println(Modifier.toString(md) + " " + cl + " : " + fa[i].getName() + " = " + value.toString());
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
4、获取类、属性、方法的修饰域
类Class、Method、Constructor、Field都有一个public方法int
在开发文档中,可以查阅到,Modifier类中定义了若干特定的修饰域,每个修饰域都是一个固定的int数值,列表如下:
该类不仅提供了若干用于判断是否拥有某中修饰域的方法boolean
五、如何调用类中的private方法
在介绍之前,先放一个代码吧,这段代码是参考其他文章的代码拷贝过来的,代码不算长,但是动态调用类的成员方法的过程讲解的通俗易懂。

importjava.lang.reflect.Method; publicclass LoadMethod {
public Object Load(String cName, String MethodName,String[] types, String[] params) {
Object retObject
=null; try{// 加载指定的类
Class cls = Class.forName(cName); // 获取Class类的对象的方法之二 //利用newInstance()方法,获取构造方法的实例
// Class的newInstance方法只提供默认无参构造实例
// Constructor的newInstance方法提供带参的构造实例
Constructor ct= cls.getConstructor(null);
Object obj =ct.newInstance(null);
//Object obj = cls.newInstance(); //构建 方法的参数类型
Class paramTypes[] = this.getMethodTypesClass(types); //在指定类中获取指定的方法
Method meth = cls.getMethod(MethodName, paramTypes); //构建 方法的参数值
Object argList[] = this.getMethodParamObject(types, params); //调用指定的方法并获取返回值为Object类型
retObject = meth.invoke(obj, argList);
}
catch(Exception e) {System.err.println(e);
} returnretObject;
}
public Class[] getMethodTypesClass(String[] types){
Class[] cs =new Class[types.length]; for(int i = 0;i <cs.length; i++){
if(types[i] != null || !types[i].trim().equals("")){
if(types[i].equals("int")|| types[i].equals("Integer")){
cs[i] = Integer.TYPE;
}
elseif (types[i].equals("float") || types[i].equals("Float")){
cs[i] = Float.TYPE;
}
elseif (types[i].equals("double") || types[i].equals("Double")){
cs[i] = Double.TYPE;
}
elseif (types[i].equals("boolean") || types[i].equals("Boolean")){
cs[i] = Boolean.TYPE;
}
else{
cs[i] = String.class;
}
}
}
returncs;
}
public Object[] getMethodParamObject(String[] types,String[] params) {
Object[] retObjects
=new Object[params.length]; for(int i = 0;i <retObjects.length;i++) {if(!params[i].trim().equals("")||params[i]!=null){
if(types[i].equals("int")||types[i].equals("Integer")){
retObjects[i]=new Integer(params[i]);
}
elseif(types[i].equals("float")||types[i].equals("Float")){
retObjects[i]=new Float(params[i]);
}
elseif(types[i].equals("double")||types[i].equals("Double")){
retObjects[i]=new Double(params[i]);
}
elseif(types[i].equals("boolean")||types[i].equals("Boolean")){
retObjects[i]=new Boolean(params[i]);
}
else{
retObjects[i] =params[i];
}
}
} returnretObjects;
}
}
要调用一个类的方法,首先需要一个该类的实例(当然,如果该类是static,就不需要实例了,至于原因,你懂得!)。
1、创建一个类的实例
在得到一个类的Class对象之后,我们可以利用类Constructor去实例化该对象。Constructor支持泛型,也就是它本身应该是Constructor<T>。这个类有一个public成员函数:T
在代码LoadMethod.java和LoadMethodEx.java中,分别给出了两种实例化Class类的方法:一种是利用Constructor类调用newInstance()方法;另一种就是利用Class类本身的newInstance()方法创建一个实例。两种方法实现的效果是一样的。
Constructor ct =cls.getConstructor(null);
Object obj
=ct.newInstance(null);Object obj
=cls.newInstance();2、行为
Method类中包含着类的成员方法的信息。在Method类中有一个public成员函数:Object
如果某一个方法是Java类的静态方法,那么Object
对类的成员变量进行读写,在Field类中有两个public方法:
Object
Void
其中,Object参数是需要传入的对象;如果成员变量是静态属性,在object可传入null。
六、对LoadMethod.java的优化处理
在上一节中给出的LoadMethod.java中,类LoadMethod对固定参数类型的方法进行了调用,并且参数类型是通过一个String[]数组传入,然后经过方法
因此,我对LoadMethod类进行了一定的优化处理。先附上代码:

importjava.lang.reflect.Method; publicclass LoadMethodEx {
public Object Load(String cName, String MethodName,Object[] params) {
Object retObject
=null; try{// 加载指定的类
Class cls = Class.forName(cName); // 获取Class类的对象的方法之二 //利用newInstance()方法,获取构造方法的实例
// Class的newInstance方法只提供默认无参构造实例
// Constructor的newInstance方法提供带参的构造实例
Constructor ct= cls.getConstructor(null);
Object obj =ct.newInstance(null);
//Object obj = cls.newInstance(); //根据方法名获取指定方法的参数类型列表
Class paramTypes[] = this.getParamTypes(cls, MethodName); //获取指定方法
Method meth = cls.getMethod(MethodName, paramTypes);
meth.setAccessible(true); //调用指定的方法并获取返回值为Object类型
retObject = meth.invoke(obj, params);
}
catch(Exception e) {System.err.println(e);
} returnretObject;
}
public Class[] getParamTypes(Class cls, String mName){
Class[] cs =null;
Method[] mtd =cls.getDeclaredMethods();
for (int i = 0;i <mtd.length; i++){
if(!mtd[i].getName().equals(mName)) {// 不是我们需要的参数,则进入下一次循环
continue;
}
cs
= mtd[i].getParameterTypes();}
returncs;
}
}
如果我们已经知道某个类名和需要动态调用的方法名,怎样才能不用传入方法的参数类型就可以调用该方法呢?
1、LoadMethodEx类,少了一个参数(方法参数类型列表),本文直接从类LoadMethod内部获取该参数类型列表,不需要用户传入该信息,好处其实也不言而喻了。
2、方法的参数值:类LoadMethod是将所有的方法参数都做为一个String来传入,在传入再进行解析;而本文则直接使用Object类型做为参数类型,因为invoke(Object
在调用LoadMethod的Load()方法时,用户只需要知道类名、方法名,并且将已经初始化的参数先向上转型为Object,然后传递给Load()方法即可。方法的返回值为Object,这个肯定是由用户根据自己的需要,再转换成自己所需的类型。

public 属性
public class java.lang.Integer : bInteger= 4
public class java.lang.String : strB = crazypebble
public & 非public 属性
private int :bInt = 0
public class java.lang.Integer : bInteger= 4
public class java.lang.String : strB = crazypebble
private class java.lang.String : strA
构造方法:
protectedcrazypebble.reflectiontest.ReflectionTest(int,java.lang.String)
父类
/接口:父类: java.lang.Object
接口0: java.awt.event.ActionListener
接口1: java.io.Serializable
成员方法:
public void actionPerformed(java.awt.event.ActionEvent)
public final native void wait(long)
public final void wait()
public final void wait(long, int)
public boolean equals(java.lang.Object)
publicjava.lang.StringtoString()
public native int hashCode()
public final native java.lang.ClassgetClass()
public final native void notify()
public final native void notifyAll()
反射机制调用方法:LoadMethod
crazypebble
反射机制调用方法:LoadMethodEx
crazypebble
返回结果:0
七、总结
关于反射机制,其实还有一个比较敏感的话题,就是反射机制带来我们的安全性问题。由于我在这方面研究的不是很深入,所以讲不好。大家有空可以跟踪一下在本文最后提供的两个链接,里面有一些介绍。
我们介绍了Java的反射机制,但是在Android平台下,反射机制具体有没有什么用途呢?答案是肯定的。推荐大家看一篇文章《利用Java反射技术阻止通过按钮关闭对话框》,这篇文章为CSDN推荐为精品文章,所以还是很值得一看的。我特地从CSDN转载过来供大家一起学习。
原链接:http://blog.csdn.net/nokiaguy/archive/2010/07/27/5770263.aspx
转载链接:http://www.cnblogs.com/crazypebble/archive/2011/04/13/2014297.html
程序实现的源码:

import android.app.AlertDialog;
importandroid.content.DialogInterface;
importandroid.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
importandroid.widget.Button; publicclass MainActivity extends Activity {
private static Button btnHandler = null;
private static Button btnShowing = null;
AlertDialog alertDialog = null;
@Override
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnHandler
=(Button)findViewById(R.id.btn_mHandler);btnHandler.setOnClickListener(new ButtonListener());
btnShowing
=(Button)findViewById(R.id.btn_mShowing);btnShowing.setOnClickListener(new ButtonListener());
alertDialog
=new AlertDialog.Builder(this).setTitle("abc")
.setMessage("Content")
.setIcon(R.drawable.icon)
.setPositiveButton("确定", new PositiveClickListener())
.setNegativeButton("取消", new NegativeClickListener())
.create();
} privateclass ButtonListener implements OnClickListener {
@Override
switch (v.getId()) {
case R.id.btn_mHandler:
modify_mHandler();
alertDialog.show();
break;
case R.id.btn_mShowing:
alertDialog.show();
break;
default:
break;
}
}
} privateclass PositiveClickListener implements android.content.DialogInterface.OnClickListener{
@Override
// 方法二时启用
modify_dismissDialog(false);
}
} privateclass NegativeClickListener implements android.content.DialogInterface.OnClickListener{
@Override
// 方法一时启用
//dialog.dismiss(); //方法二时启用
modify_dismissDialog(true);
}
}
public void modify_mHandler() {
try {
Field field =alertDialog.getClass().getDeclaredField("mAlert");
field.setAccessible(true);
// 获取mAlert变量的值
Object obj = field.get(alertDialog);
field = obj.getClass().getDeclaredField("mHandler");
field.setAccessible(true);
// 修改mHandler变量的值,使用新的ButtonHandler类
field.set(obj,new MyButtonHandler(alertDialog));
}
catch(Exception e) {e.printStackTrace();
}
}
public void modify_dismissDialog(boolean flag) {
try {
Field field =alertDialog.getClass().getSuperclass().getDeclaredField("mShowing");
field.setAccessible(true);
// 将mShowing变量设为false,表示对话框已经关闭
field.set(alertDialog,flag);
alertDialog.dismiss();
} catch(Exception e) {
e.printStackTrace();
}
}
}

importandroid.os.Handler;
import android.os.Message; publicclass MyButtonHandler extends Handler{ //Button clicks have Message.whatas the BUTTON{1,2,3} constant
private static final int MSG_DISMISS_DIALOG = 1; privateWeakReference<DialogInterface> mDialog; publicMyButtonHandler(DialogInterfacedialog) {
mDialog =new WeakReference<DialogInterface>(dialog);
}
@Override
switch (msg.what) { caseDialogInterface.BUTTON_POSITIVE:
case DialogInterface.BUTTON_NEGATIVE:
case DialogInterface.BUTTON_NEUTRAL:
((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(),msg.what);
break;
}
}
}
看完上面这篇文章之后,希望大家明确一点的是:反射机制通过void
希望本言语对大伙有所帮助!!!