Android 反射Field应用

面的几节涉及反射的一些基本概念,下面介绍在以后开发是经常需要的用到的一些反射技术使用.

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

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

	}





































你可能感兴趣的:(Android 反射Field应用)