Android中java反射(Reflection)实战

反射作为java语言非常重要的特性之一,在开发的过程中可以为我们提供极大的便利。在J2EE中,java反射得到了大量的应用,尤其是在一些主流框架中,如Spring中反射就发挥了极大的作用。那将java作为开发语言的Android,我们能否利用java反射这一重要特性,帮助我们更加灵活、高效的进行开发呢?答案是肯定的。

我们先来了解一下反射(Reflection)到底是什么?

官方一点的解释:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

个人认为反射的精髓在于两个字—动态。我们在实际编程解决问题的时候,往往会遇到这种情况,就是程序运行的时候应该去实例化哪个类,只有在运行时才知道,因此不能够在编译阶段就做出决定。Java反射就可以帮助我们在运行时决定应该实例化哪个类。因此,java反射的强大就在于它能够在程序运行时实例化对象,这是同我们平时用new实例化对象最根本的区别。

说了这么多,对于反射可能还是会感到有些模糊,一般java初学者都会对反射感到难于理解。遇到这种情况,最好的方法就是在实际项目中去利用它,在实战中理解反射的强大,感受它带给我们的便利。说实话笔者在写这篇博客之前对于反射的理解也是非常表面的,直到最近review之前写的Android项目,发现当年的我还是太年轻,囧……写的代码质量实在是太低了,项目中充斥着大量的重复代码,原因就是当时不会用反射,导致大量代码冗余。但是利用反射后,我竟发现代码量少了1/3, 而且代码整体也比原先的整洁、优雅了很多~

先来简单了解一下我这个Android项目的business吧。这个app是一款为服务于广大高考考生的一个错题本。用户可以用他利用摄像头对错题进行捕获,用户可以自定义每个科目的分类,按照分类保存到本地。并可以查看每个分类的错题墙;摇一摇随机要出原来的错题;错题云备份。下面看一下这款应用的几个截图,马上就可以看到反射在Android项目中的实际应用了。

如图一所示是app首页,用户可以点击想要收集错题的科目,然后应用跳转到图二所示的界面。用户可以点击“+”来添加错题分类,这时应用就会跳转到图三所示的界面。

Android中java反射(Reflection)实战_第1张图片     Android中java反射(Reflection)实战_第2张图片     Android中java反射(Reflection)实战_第3张图片

                 图一 App首页                                   图二 分类展示界面                              图三 添加分类界面

在做这个项目的时候,每个错题的分类管理我是利用SQLite实现的,而所有的数据库操作我全部使用一个Adapter类来进行封装的。在进入到图二、图三所示界面的时候就会分别实例化我这个定义好的Adapter类进行查询和插入操作,之前的代码是这样的:

private PhysicsDBAdapter dbAdapter;
/*取得数据库对象*/
dbAdapter = new PhysicsDBAdapter(this);
dbAdapter.open();
/*查询物理科目的所有分类信息*/
final Image[] image = dbAdapter.queryAllImage();
Text[] text = dbAdapter.queryAllText();
cn.swu.SQLite.Date[] date = dbAdapter.queryAllDate();
Id[] id = dbAdapter.queryAllId();

/*插入新的分类信息*/
dbAdapter.insert(CategoryInfo);
但是这样做就会有一个问题,使用new的方式来实例化,在编译阶段就必须知道这个类是什么。所以在进入图二、图三界面的时候就只能实例化,调用固定的Adapter类。针对例子中给出的“物理”科目实例化相对应的PhysicsDBAdapter类,但是当我要点击其他科目的时候,又要实例化其他的Adapter类,比如BiologyDBAdapter、ChemistryDBAdapter…这样图二、图三中的代码就不能很好地得到重用,就必须为每个科目单独写图二、图三对应的代码,这样就会出现大量的重复代码,当然这是非常horrible& stupid的。那么有什么办法,可以让我们写图二、三对应代码的时候动态的去实例化数据库管理的Adapter类呢?有一个很好地解决方法就是利用JAVA的反射机制。利用反射可以在写图二、三对应代码的时候。动态的实例化,而不用像之前那样,傻傻的一个一个去new了,利用JAVA反射重构项目的代码如下:

/*取得数据库对象*/
try {
	Class dbAdapter = Class.forName(className);
	/*当要实例化的类构造函数含有参数的时候需要得到构造参数的实例(stackoverflow上面找到的答案)*/
	Constructor constructor = dbAdapter.getConstructor(Context.class);
	Object obj = constructor.newInstance(this);
	Method method = dbAdapter.getMethod("open", null);
	Object o = method.invoke(obj, null);
} catch (InstantiationException e) {

	e.printStackTrace();
} catch (IllegalAccessException e) {

	e.printStackTrace();
} catch (SecurityException e) {

	e.printStackTrace();
} catch (NoSuchMethodException e) {

	e.printStackTrace();
} catch (IllegalArgumentException e) {

	e.printStackTrace();
} catch (InvocationTargetException e) {

	e.printStackTrace();
} catch (ClassNotFoundException e) {

	e.printStackTrace();
}
/*查询物理科目的所有分类信息*/
try {
	Class dbAdapter = Class.forName(className);
	Constructor constructor = dbAdapter.getConstructor(Context.class);
	Object obj = constructor.newInstance(this);
	
	Method method0 = dbAdapter.getMethod("queryAllImage", null);
	Method method1 = dbAdapter.getMethod("queryAllText", null);
	Method method2 = dbAdapter.getMethod("queryAllId", null);
	Method method3 = dbAdapter.getMethod("queryAllDate", null);

	image = (Image[]) method0.invoke(obj, null);
	text = (Text[]) method1.invoke(obj, null);
	id = (Id[]) method2.invoke(obj, null);
	date = (cn.swu.SQLite.Date[]) method3.invoke(obj, null);

} catch (InstantiationException e) {

	e.printStackTrace();
} catch (IllegalAccessException e) {

	e.printStackTrace();
} catch (SecurityException e) {

	e.printStackTrace();
} catch (NoSuchMethodException e) {

	e.printStackTrace();
} catch (IllegalArgumentException e) {

	e.printStackTrace();
} catch (InvocationTargetException e) {

	e.printStackTrace();
} catch (ClassNotFoundException e) {

	e.printStackTrace();
}
/*插入新增一个分类*/
try {
	Class dbAdapter = Class.forName(className);
	Constructor constructor = dbAdapter.getConstructor(Context.class);
	Object obj = constructor.newInstance(this);
	Class[] param = new Class[1];
	/*CategoryInfo类中定义了一个分类信息的全部内容,id,Name,Image,text.将其作为参数传入insert方法中*/
	param[0] = CategoryInfo.class;
	Method method = dbAdapter.getMethod("insert", param);
	method.invoke(obj, categoryInfo);
} catch (InstantiationException e) {

	e.printStackTrace();
} catch (IllegalAccessException e) {

	e.printStackTrace();
} catch (SecurityException e) {

	e.printStackTrace();
} catch (NoSuchMethodException e) {

	e.printStackTrace();
} catch (IllegalArgumentException e) {

	e.printStackTrace();
} catch (InvocationTargetException e) {

	e.printStackTrace();
} catch (ClassNotFoundException e) {

	e.printStackTrace();
}

重构后发现项目的代码少了1/3而且更加清晰,更加优雅了~(^o^)~

最后说点个人小小的心得体会,其实有的时候,只顾埋头写代码,并没有多大的提高。反而更应该多抽出点时间,去review一下原来写的代码,重构一下,让它变得更加简洁、优雅。多想一想用另一种方式去实现,有意识的去用一些设计模式,这对个人水平的提高会更有裨益。



你可能感兴趣的:(Android)