面的几节涉及反射的一些基本概念,下面介绍在以后开发是经常需要的用到的一些反射技术使用.
由于在前面没有特别指出来,如何反射一个类,并且得到一个类的实例,所以在这里,首先列出一个我们经常在网上面见到的方式:
public static void getReflectionFields(ReflectionTest r) { Class temp = r.getClass(); String className = temp.getName(); /**获取public的成员变量*/ Field[] fields = temp.getFields(); /**获取所有成员变量*/ Field[] dfields = temp.getDeclaredFields(); // printFields(fields ,temp ,r); printFields(dfields, temp ,r); }
说明:ReflectionTest是自定义的一个类,然后通过 Class temp = r.getClass();获取类的一个对象.接下来就是去获取类里面的变量,方法.为什么需要特别列出这么一段呢?如果从实际的开发角度出发,既然是要使用反射,怎么可能有ReflectionTest这种”显示”的类提供呢?我们想到反射的时候,一般或者说更多是去反射别人的类,而不是自己工程里面的类,这个类可能作为系统的类,也可能只是放在某个文件夹下.所以这里我建议这样:
private static void FlexClass(String packagename){ try { Class clazz=Class.forName(packagename); try { Object obj=clazz.newInstance(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
这样做的好处是,我只需要传递一个packagename 给这个方法就好了,至于这个包名可以是自己工程里面的,也可以是系统里面的,也可以是放在某个文件夹中的.
注意到上面的区别以后,正式开始看一下,通过反射类,如何得到类里面的参数(Field),下面新建一个java工程,目录如下:
<1> : 首先新建一个被用于反射的类FlexClass.java:
/** * */ package com.oneplus.flex; /** * @author zhibao.liu * @Date 2015/11/18 * @company oneplus.Inc */ public class FlexClass { private int Var1; private String Var2; private int Var3=12354; private String Var4="hello , this is Var4 !"; private char Var5='B'; }
<2> : 由于这里面只涉及到Field的反射,所以类里面不再增加其他方法等.关于Field具体的API这里面不过去讲解(可以参考Field.java源代码),下面给出下面例子当中用到的:
getChar |
|
getInt |
|
getName |
|
getType |
|
http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Field.html |
其余以此类推.
由于开篇的时候已经说了如何得到类的实例,下面我们获取类里面是参数变量,程序如下:
private static void FlexFieldClass() { try { Class clazz = Class.forName("com.oneplus.flex.FlexClass"); try { Object obj = clazz.newInstance(); //获取类中所有Field Field[] fields = clazz.getDeclaredFields(); //下面循环获取所有类的名字,数据类型,以及对应值 for (int i = 0; i < fields.length; i++) { fields[i].setAccessible(true); System.out.println("Field " + i + " name : " + fields[i].getName() + " Type : " + fields[i].getType()); try { Field field = clazz.getDeclaredField(fields[i] .getName()); field.setAccessible(true); //获取对应的值 Object vobj = field.get(obj); if (vobj != null) { System.out.println("Field " + i + " name : " + vobj.toString()); } else { System.out.println("Field " + i + " name : NULL"); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
然后将上面放到:
public static void main(String[] args) { // TODO Auto-generated method stub FlexFieldClass(); }
运行结果:
大家需要注意的是:
<1> : field.setAccessible(true);设置这个可以保证将类中的private成员变量也反射出来,建议无论类中是否有私有还是无私有,均加上,当然如果读者能够时刻分的清楚,可以规规矩矩,是反射私有的时候才设置为true;
<2> : 如果反射的类里面的变量没有赋初值,像int型,系统会给一个默认值0,但是String就不会,所以在得到String类型的时候,一定要判断返回值是否为NULL,当然这并不是说其他的基本类型就不需要判断的,只是String等类型不判断,直接会导致程序异常!
上面只是介绍获取参数的基本信息,如名字,数据类型等,下面继续举例如何获取变量值,程序如下,以获取Char Var5为例 :
private static void GetCharFlexFieldClass(String packagename,String fieldname){ try { Class clazz=Class.forName(packagename); try { Object obj=clazz.newInstance(); try { // 获取制定字段名的对象 Field field=clazz.getDeclaredField(fieldname); field.setAccessible(true); //注意下面,使用的是getChar(Object object)方式 //对于其他类型,以此类推,如getInt(Object object) Object vobj=field.getChar(obj); if(vobj!=null){ System.out.println("Field Char : "+vobj.toString()); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
注意 : 程序Field类提供了比如getChar(Object object),getInt(Object object)等方法,用于获取制定类型的值,也提供get(Object object) 方法,当如果你的反射类中包含另外一些自定义的类变量时,可以使用这个方法去获取.
好了,虽然上面是以获取Char型,但是可以举一反三,在这一节的后面,我尽量提供其他一些实例参考作为补充.
下面接下来是修改读取出来的Field参数值.在做之前,我们可以做一次联想,java等高级语言往往有成对的方法接口,有getChar(Object object),就意味着有setChar(Object object, Object object),看看下面设置参数值,程序如下:
private static void SetCharFlexFieldClass(String packagename,String fieldname){ try { Class clazz=Class.forName(packagename); try { Object obj=clazz.newInstance(); try { Field field=clazz.getDeclaredField(fieldname); field.setAccessible(true); // 设置一个Char 字符进去 field.setChar(obj, 'H'); //然后再获取出来 Object vobj=field.getChar(obj); //获取出来以后,再打印出来 if(vobj!=null){ System.out.println("Modified Field Char : "+vobj.toString()); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
结果变成了字符H.
看了上面,很多读者会问,上面就是简简单单的java语言使用而已,和Android没什么关系,也没看见如何在Android使用,那么下面通过一个实例来看一看在Android是如何使用的:
<1> : 新建一个Android 工程,目录如下:
<2>: 布局如下 :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".OneplusFlexFieldActivity" > <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/button" android:text="@string/oneplus_button"/> </RelativeLayout>
Value目录下如下:
增加一个oneplus_string的文件,内容如下 :
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="oneplus_button">button</string> </resources>
<3> : 主类程序如下 :
package com.oneplus.oneplusandroidflexfield; import java.lang.reflect.Field; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class OneplusFlexFieldActivity extends Activity implements OnClickListener { private final static String TAG="OneplusFlexFieldActivity"; private final static String ONEPLUS_ANDROID_RESOURCE = "com.android.internal.R$dimen"; private Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.oneplus_flex_field); mButton = (Button) findViewById(R.id.button); mButton.setOnClickListener(this); } @Override public void onClick(View arg0) { // TODO Auto-generated method stub int resId = arg0.getId(); switch (resId) { case R.id.button: OneplusFlexAndroidResource(OneplusFlexFieldActivity.this,ONEPLUS_ANDROID_RESOURCE); break; default: } } private void OneplusFlexAndroidResource(Context context, String packagename) { try { Class clazz = Class.forName(packagename); try { Object obj = clazz.newInstance(); Field fields[] = clazz.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { try { Field field = clazz.getDeclaredField(fields[i].getName()); field.setAccessible(true); Object vobj = field.get(obj); if (vobj != null) { //下面是获取到了android系统的资源ID了 int resID = Integer.parseInt(vobj.toString()); Object ret = context.getResources().getDimension(resID); Log.i(TAG,"Field Name : " + field.getName() + " Field Resource Value : " + ret.toString()); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果 :
下面在给出获取制定参数的值 :
private void OneplusFlexAndroidResource(Context context, String packagename, String fieldname) { try { Class clazz = Class.forName(packagename); try { Object obj = clazz.newInstance(); try { Field field = clazz.getDeclaredField(fieldname); field.setAccessible(true); Object ret = field.get(obj); int resID = Integer.parseInt(ret.toString()); Object vret = context.getResources().getDimension(resID); Log.i(TAG, "vret : " + vret.toString()); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
在主工程里面增加一个按钮,在按钮点击事件中调用:
OneplusFlexAndroidResource(OneplusFlexFieldActivity.this, ONEPLUS_ANDROID_RESOURCE, "status_bar_height");
运行结果 :
根据上面的方式,同样可以获取Android里面很多其他类型的资源ID,从而可以调用Android系统各种系统资源,正如上面的,既然可以获取,那么同样也可以设置某些参数的值,的确是这样的,同样可以设置,但是Android系统的这些参数值用Set…可是搞不定的,这里暂时不介绍!
附录 :
下面贴出一些其他供参考的测试代码,这些代码可以在我提供的demo程序中找到:
private static void GetFlexFieldClass(String packagename, String fieldname) { try { Class clazz=Class.forName(packagename); try { Object obj=clazz.newInstance(); try { Field field=clazz.getDeclaredField(fieldname); field.setAccessible(true); Object vobj=field.get(obj); if(vobj!=null){ System.out.println("Field Name : "+field.getName()+" Field Type : "+field.getType()+" Field Value : " + vobj.toString()); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private static void SetFlexFieldClass(String packagename, String fieldname) { try { Class clazz = Class.forName(packagename); try { Object obj = clazz.newInstance(); try { Field field = clazz.getDeclaredField(fieldname); field.setAccessible(true); Object vobj = field.get(obj); if (vobj != null) { System.out.println("before read field value : " + vobj.toString()); } // make String as example followly field.set(obj, "oneplus zhibao.liu"); // read it again after modify vobj = field.get(obj); if (vobj != null) { System.out.println("after read field value : " + vobj.toString()); } } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } }