JAVA反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
1. 得到某个对象的属性
1 public Object getProperty(Object owner, String fieldName) throws Exception {
2 Class ownerClass = owner.getClass();
3
4 Field field = ownerClass.getField(fieldName);
5
6 Object property = field.get(owner);
7
8 return property;
9 }
Class ownerClass = owner.getClass():得到该对象的Class。
Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。
Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。
2. 得到某个类的静态属性
1 public Object getStaticProperty(String className, String fieldName)
2 throws Exception {
3 Class ownerClass = Class.forName(className);
4
5 Field field = ownerClass.getField(fieldName);
6
7 Object property = field.get(ownerClass);
8
9 return property;
10 }
Class ownerClass = Class.forName(className) :首先得到这个类的Class。
Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。
Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。
3. 执行某对象的方法
1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
2
3 Class ownerClass = owner.getClass();
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Method method = ownerClass.getMethod(methodName, argsClass);
12
13 return method.invoke(owner, args);
14 }
Class owner_class = owner.getClass() :首先还是必须得到这个对象的Class。
5~9行:配置参数的Class数组,作为寻找Method的条件。
Method method = ownerClass.getMethod(methodName, argsClass):通过Method名和参数的Class数组得到要执行的Method。
method.invoke(owner, args):执行该Method,invoke方法的参数是执行这个方法的对象,和参数数组。返回值是Object,也既是该方法的返回值。
4. 执行某个类的静态方法
1 public Object invokeStaticMethod(String className, String methodName,
2 Object[] args) throws Exception {
3 Class ownerClass = Class.forName(className);
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Method method = ownerClass.getMethod(methodName, argsClass);
12
13 return method.invoke(null, args);
14 }
基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。
5. 新建实例
1
2 public Object newInstance(String className, Object[] args) throws Exception {
3 Class newoneClass = Class.forName(className);
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Constructor cons = newoneClass.getConstructor(argsClass);
12
13 return cons.newInstance(args);
14
15 }
这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。
Class newoneClass = Class.forName(className):第一步,得到要构造的实例的Class。
第5~第9行:得到参数的Class数组。
Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。
cons.newInstance(args):新建实例。
6. 判断是否为某个类的实例
1 public boolean isInstance(Object obj, Class cls) {
2 return cls.isInstance(obj);
3 }
7. 得到数组中的某个元素
1 public Object getByArray(Object array, int index) {
2 return Array.get(array,index);
3 }
TelephonyManager的应用
手机的最主要功用就是打电话,如果没有电信公司提供的SIM卡,就不能正常地拨打电话,那么,我们有什么方法可以取得SIM卡的状态及相关资料呢?
Android API中的TelephonyManager(Android.telephony.TelephonyManager)对象,提供了几个方法可以快速取得SIM卡的状态及相关信息。
效果图:
程序中以getSystemService(TELEPHONY_SERVICE)来取得TelephonyManager对象,以TelephonyManager提供的方法来取得SIM卡状态及相关信息,将取得的信息存入自定义的MyAdapter中,最后以setListAdapter()将MyAdpter内的信息显示于ListView中。
Java代码:
01.import android.telephony.TelephonyManager;
02.
03.public class EX05_18 extends ListActivity {
04.private TelephonyManager telMgr;
05.private List<String> item=new ArrayList<String>();
06.private List<String> value=new ArrayList<String>();
07.
08.@SuppressWarnings("static-access")
09.@Override
10.public void onCreate(Bundle savedInstanceState) {
11.super.onCreate(savedInstanceState);
12./* 载入main.xml Layout */
13.setContentView(R.layout.main);
14.telMgr = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
15./* 将取得的信息写入List中 */
16./* 取得SIM卡状态 */
17.item.add(getResources().getText(R.string.str_list0).toString());
18.if(telMgr.getSimState()==telMgr.SIM_STATE_READY) {
19.value.add("良好");
20.} else if(telMgr.getSimState()==telMgr.SIM_STATE_ABSENT) {
21.value.add("无SIM卡");
22.} else {
23.value.add("SIM卡被锁定或未知的状态");
24.}
25./* 取得SIM卡卡号 */
26.item.add(getResources().getText(R.string.str_list1).toString());
27.if(telMgr.getSimSerialNumber()!=null) {
28.value.add(telMgr.getSimSerialNumber());
29.} else {
30.value.add("无法取得");
31.}
32./* 取得SIM卡供货商代码 */
33.item.add(getResources().getText(R.string.str_list2).toString());
34.if(telMgr.getSimOperator().equals("")) {
35.value.add("无法取得");
36.} else {
37.value.add(telMgr.getSimOperator());
38.}
39./* 取得SIM卡供货商名称 */
40.item.add(getResources().getText(R.string.str_list3).toString());
41.if(telMgr.getSimOperatorName().equals("")) {
42.value.add("无法取得");
43.} else {
44.value.add(telMgr.getSimOperatorName());
45.}
46./* 取得SIM卡国别 */
47.item.add(getResources().getText(R.string.str_list4).toString());
48.if(telMgr.getSimCountryIso().equals("")) {
49.value.add("无法取得");
50.} else {
51.value.add(telMgr.getSimCountryIso());
52.}
53./* 使用自定义的MyAdapter来将数据传入ListActivity */
54.setListAdapter(new MyAdapter(this,item,value));
55.}
56.
57.}
复制代码
自定义的Adapter对象,以row_layout.xml作为Layout,程序中依照Layout的设置值来显示信息名称及信息内容。
Java代码:
01./* 自定义的Adapter,继承android.widget.BaseAdapter */
02.public class MyAdapter extends BaseAdapter {
03./* 变量声明 */
04.private LayoutInflater mInflater;
05.private List<String> items;
06.private List<String> values;
07./* MyAdapter的构造器,传入三个参数 */
08.public MyAdapter(Context context,List<String> item, List<String> value) {
09./* 参数初始化 */
10.mInflater = LayoutInflater.from(context);
11.itemitems = item;
12.valuevalues = value;
13.}
14./* 因继承BaseAdapter,需重写以下方法 */
15.@Override
16.public int getCount() {
17.return items.size();
18.}
19.
20.@Override
21.public Object getItem(int position) {
22.return items.get(position);
23.}
24.@Override
25.public long getItemId(int position) {
26.return position;
27.}
28.@Override
29.public View getView(int position,View convertView,ViewGroup par) {
30.ViewHolder holder;
31.if(convertView == null) {
32./* 使用自定义的file_row作为Layout */
33.convertView = mInflater.inflate(R.layout.row_layout,null);
34./* 初始化holder的text与icon */
35.holder = new ViewHolder();
36.holder.text1=(TextView)convertView.findViewById(R.id.myText1);
37.holder.text2=(TextView)convertView.findViewById(R.id.myText2);
38.convertView.setTag(holder);
39.} else {
40.holder = (ViewHolder) convertView.getTag();
41.}
42./* 设置要显示的信息 */
43.holder.text1.setText(items.get(position).toString());
44.holder.text2.setText(values.get(position).toString());
45.return convertView;
46.}
47./* class ViewHolder */
48.private class ViewHolder {
49./* text1:信息名称 * text2:信息内容 */
50.TextView text1;
51.TextView text2;
52.}
53.
54.}
复制代码
AndroidManifest.xml
AndroidManifest.xml本身必须要在Activity里设置读取电话状态的权限(Android.permission.READ_ PHONE_STATE)。
Java代码:
01.<uses-permission android:name="android.permission.READ_PHONE_STATE">
复制代码
在取得SIM卡的相关信息时,有时会因某些情况而取不到值。例如,手机没有插入SIM卡,这时候的返回值可能为null或空白字符串,所以在程序中添加空白与null的检查会是比较保险的做法,以免因此造成系统错误。
程序以TelephonyManager.getSimState()来取得SIM卡的状态,可能出现的返回状态如表
UriMatcher类
在Content Provider中该类帮助匹配URI。
1. 构造函数:创建URI树的根节点
a) Public UriMatcher():默认根节点编码为-1;
b) Public UriMatcher(int code):code参数指定根节点的编码。
2. 方法
a) Public void addURI(String authority,String path,int code)
添加一个用于匹配的URI,当匹配成功时则code。URI可以是精确的字符串,uri中带有*表示可匹配任意text,#表示只能匹配数字。
Authority:用于匹配的域名;
Path:匹配路径,*表示text的占位符,#表示使用数字的占位符;
Code:当使用匹配成功后返回code,值需要大于0,否则抛出IllegalArgument异常。
此方法将authority按照”/”进行拆分,然后将拆分后的每一部分保存到UriMatcher类型的ArrayList中;在添加的时候会判断当前authority是否已经添加过,若已加则break;若未添加过,则判断是否含有”#”则将其标识成1代表域名后面跟随的是数字;”*”标识成2,代表域名后面跟随的是文本;0代表后面没有跟随数据;最后创建一个新的UriMatcher对象添加到集合中。
b) Public int match(Uri uri)
尝试在url中匹配相对应的路径
Uri:指定需要匹配的url;
返回值:在使用addURI时产生的code,若没有匹配则返回-1。
使用uri. getPathSegments()获取uri中各段存入list中,若list size为0或uri的Authority为null则返回默认值(此默认值在new时指定,若为指定则为-1);
然后遍历ArrayLis<UriMatcher>进行匹配uri。
1.Uri
通用资源标志符(Universal Resource Identifier, 简称"URI")。
Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。
URI一般由三部分组成:
访问资源的命名机制。
存放资源的主机名。
资源自身的名称,由路径表示。
Android的Uri由以下三部分组成: "content://"、数据的路径、标示ID(可选)
举些例子,如:
所有联系人的Uri: content://contacts/people
某个联系人的Uri: content://contacts/people/5
所有图片Uri: content://media/external
某个图片的Uri:content://media/external/images/media/4
我们很经常需要解析Uri,并从Uri中获取数据。
Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。
虽然这两类不是非常重要,但是掌握它们的使用,会便于我们的开发工作。
下面就一起看一下这两个类的作用。
2.UriMatcher
UriMatcher 类主要用于匹配Uri.
使用方法如下。
首先第一步,初始化:
view plainprint?
1. UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
[java] view plaincopyprint?
1. UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
第二步注册需要的Uri:
view plainprint?
1. matcher.addURI("com.yfz.Lesson", "people", PEOPLE);
2. matcher.addURI("com.yfz.Lesson", "person/#", PEOPLE_ID);
[java] view plaincopyprint?
1. matcher.addURI("com.yfz.Lesson", "people", PEOPLE);
2. matcher.addURI("com.yfz.Lesson", "person/#", PEOPLE_ID);
第三部,与已经注册的Uri进行匹配:
view plainprint?
1. Uri uri = Uri.parse("content://" + "com.yfz.Lesson" + "/people");
2. int match = matcher.match(uri);
3. switch (match)
4. {
5. case PEOPLE:
6. return "vnd.android.cursor.dir/people";
7. case PEOPLE_ID:
8. return "vnd.android.cursor.item/people";
9. default:
10. return null;
11. }
[java] view plaincopyprint?
1. Uri uri = Uri.parse("content://" + "com.yfz.Lesson" + "/people");
2. int match = matcher.match(uri);
3. switch (match)
4. {
5. case PEOPLE:
6. return "vnd.android.cursor.dir/people";
7. case PEOPLE_ID:
8. return "vnd.android.cursor.item/people";
9. default:
10. return null;
11. }
match方法匹配后会返回一个匹配码Code,即在使用注册方法addURI时传入的第三个参数。
上述方法会返回"vnd.android.cursor.dir/person".
总结:
--常量 UriMatcher.NO_MATCH 表示不匹配任何路径的返回码
--# 号为通配符
--* 号为任意字符
另外说一下,官方SDK说明中关于Uri的注册是这样写的:
view plainprint?
1. private static final UriMatcher sURIMatcher = new UriMatcher();
2. static
3. {
4. sURIMatcher.addURI("contacts", "/people", PEOPLE);
5. sURIMatcher.addURI("contacts", "/people/#", PEOPLE_ID);
6. sURIMatcher.addURI("contacts", "/people/#/phones", PEOPLE_PHONES);
7. sURIMatcher.addURI("contacts", "/people/#/phones/#", PEOPLE_PHONES_ID);
8. sURIMatcher.addURI("contacts", "/people/#/contact_methods", PEOPLE_CONTACTMETHODS);
9. sURIMatcher.addURI("contacts", "/people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
10. sURIMatcher.addURI("contacts", "/deleted_people", DELETED_PEOPLE);
11. sURIMatcher.addURI("contacts", "/phones", PHONES);
12. sURIMatcher.addURI("contacts", "/phones/filter/*", PHONES_FILTER);
13. sURIMatcher.addURI("contacts", "/phones/#", PHONES_ID);
14. sURIMatcher.addURI("contacts", "/contact_methods", CONTACTMETHODS);
15. sURIMatcher.addURI("contacts", "/contact_methods/#", CONTACTMETHODS_ID);
16. sURIMatcher.addURI("call_log", "/calls", CALLS);
17. sURIMatcher.addURI("call_log", "/calls/filter/*", CALLS_FILTER);
18. sURIMatcher.addURI("call_log", "/calls/#", CALLS_ID);
19. }
[java] view plaincopyprint?
1. private static final UriMatcher sURIMatcher = new UriMatcher();
2. static
3. {
4. sURIMatcher.addURI("contacts", "/people", PEOPLE);
5. sURIMatcher.addURI("contacts", "/people/#", PEOPLE_ID);
6. sURIMatcher.addURI("contacts", "/people/#/phones", PEOPLE_PHONES);
7. sURIMatcher.addURI("contacts", "/people/#/phones/#", PEOPLE_PHONES_ID);
8. sURIMatcher.addURI("contacts", "/people/#/contact_methods", PEOPLE_CONTACTMETHODS);
9. sURIMatcher.addURI("contacts", "/people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
10. sURIMatcher.addURI("contacts", "/deleted_people", DELETED_PEOPLE);
11. sURIMatcher.addURI("contacts", "/phones", PHONES);
12. sURIMatcher.addURI("contacts", "/phones/filter/*", PHONES_FILTER);
13. sURIMatcher.addURI("contacts", "/phones/#", PHONES_ID);
14. sURIMatcher.addURI("contacts", "/contact_methods", CONTACTMETHODS);
15. sURIMatcher.addURI("contacts", "/contact_methods/#", CONTACTMETHODS_ID);
16. sURIMatcher.addURI("call_log", "/calls", CALLS);
17. sURIMatcher.addURI("call_log", "/calls/filter/*", CALLS_FILTER);
18. sURIMatcher.addURI("call_log", "/calls/#", CALLS_ID);
19. }
这个说明估计已经是Google官方没有更新,首先是初始化方法,没有传参,那么现在初始化时,实际是必须传参的。 可以看一下Android2.2的源码,无参数的构造方法已经是private的了。
另外就是addURI这个方法,第二个参数开始时不需要"/", 否则是无法匹配成功的。
3.ContentUris
ContentUris 类用于获取Uri路径后面的ID部分
1)为路径加上ID: withAppendedId(uri, id)
比如有这样一个Uri
view plainprint?
1. Uri uri = Uri.parse("content://com.yfz.Lesson/people")
[java] view plaincopyprint?
1. Uri uri = Uri.parse("content://com.yfz.Lesson/people")
通过withAppendedId方法,为该Uri加上ID
view plainprint?
1. Uri resultUri = ContentUris.withAppendedId(uri, 10);
[java] view plaincopyprint?
1. Uri resultUri = ContentUris.withAppendedId(uri, 10);
最后resultUri为: content://com.yfz.Lesson/people/10
2)从路径中获取ID: parseId(uri)
view plainprint?
1. Uri uri = Uri.parse("content://com.yfz.Lesson/people/10")
2. long personid = ContentUris.parseId(uri);
[java] view plaincopyprint?
1. Uri uri = Uri.parse("content://com.yfz.Lesson/people/10")
2. long personid = ContentUris.parseId(uri);
最后personid 为 :10
附上实验的代码:
view plainprint?
1. package com.yfz;
2. import com.yfz.log.Logger;
3. import android.app.Activity;
4. import android.content.ContentUris;
5. import android.content.UriMatcher;
6. import android.net.Uri;
7. import android.os.Bundle;
8. public class Lesson_14 extends Activity {
9.
10. private static final String AUTHORITY = "com.yfz.Lesson";
11. private static final int PEOPLE = 1;
12. private static final int PEOPLE_ID = 2;
13.
14. //NO_MATCH表示不匹配任何路径的返回码
15. private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
16. static
17. {
18. sURIMatcher.addURI(AUTHORITY, "people", PEOPLE);
19.
20. //这里的#代表匹配任意数字,另外还可以用*来匹配任意文本
21. sURIMatcher.addURI(AUTHORITY, "people/#", PEOPLE_ID);
22. }
23.
24. @Override
25. protected void onCreate(Bundle savedInstanceState) {
26. super.onCreate(savedInstanceState);
27. Logger.d("------ Start Activity !!! ------");
28.
29. Uri uri1 = Uri.parse("content://" + AUTHORITY + "/people");
30. Logger.e("Uri:" + uri1);
31. Logger.d("Match 1" + getType(uri1));
32.
33. Uri uri2 = Uri.parse("content://" + AUTHORITY + "/people" + "/2");
34.
35. Logger.e("Uri:" + uri2);
36. Logger.d("Match 2" + getType(uri2));
37.
38. //拼接Uri
39. Uri cUri = ContentUris.withAppendedId(uri1, 15);
40. Logger.e("Uri:" + cUri);
41. //获取ID
42. long id = ContentUris.parseId(cUri);
43. Logger.d("Uri ID: " + id);
44. }
45.
46. private String getType(Uri uri) {
47. int match = sURIMatcher.match(uri);
48. switch (match)
49. {
50. case PEOPLE:
51. return "vnd.android.cursor.dir/person";
52. case PEOPLE_ID:
53. return "vnd.android.cursor.item/person";
54. default:
55. return null;
56. }
57. }
58. }