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

Android 反射Field应用

2015-12-08 19:25 387 查看
面的几节涉及反射的一些基本概念,下面介绍在以后开发是经常需要的用到的一些反射技术使用.

由于在前面没有特别指出来,如何反射一个类,并且得到一个类的实例,所以在这里,首先列出一个我们经常在网上面见到的方式:

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
(
Object
 obj)


 

getInt
(
Object
 obj)


 

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();
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: