在Android中,我们经常会用到匿名内部类,比如:
new Thread(new Runnable(){
public void run(){
System.out.println("test");
}
}).start();
Buttion buttion = new Buttion(this);
buttion.setOnClickListener(new OnClickListener(){
public void onClick(View view){
}
});
private Handler handler = new Handler(){
public void HandleMessage(Message msg){
}
}
刚刚接触编程的时候可能觉得这种用法比较怪,我们明明可以单独写成一个类,在类中写上要处理的逻辑,比如:
public class MyListener implements OnClickListener {
public void onClick(View view){
}
}
而且这个类可以重复使用,而匿名内部类只使用了一次,这个类的内部实现是无法被保存下来的,那么我们为什么还要使用内部类呢?这种方式有什么好处呢?
首先在android中有很多的界面UI响应事件需要处理,每个处理的Listener类我们如果都要去写一个类的话,那么会有很多代码,另一个方面,我们处理UI问题时,经常涉及到的就是Activity中的显示的各种控件,每种处理都是针对当前的布局页面或者可以说成是当前的Activity,这正处理和其他类有共同之处的地方可能不多,所以单独写一个类的必要性很小,而且单独写成一个类的话,还要把我们当前的Activity作为参数传过去,才能处理Activity中的控件,而匿名内部类可以无限范围的访问外部类的成员和方法,这就显示出它的优点了。
回过头来我们在细细看下代码:
buttion.setOnClickListener(new OnClickListener(){
public void onClick(View view){
}
});
这个new 出来的Listener没有具体的实现类类名,没有引用,只有一个接口或者抽象类,然后我们在new的同时来完成抽象方法的具体实现,而且可以随意调用外部类的成员和方法,就像自己在调用自己的方法,完全是透明的,这种使用就很方便了。它既可以用面向对象的方式来构造或者区分一个类型的处理,又可以省略单独写一个完整的类,关键的地方在于随意调用修改外部类,完全没有任何对象参数的传递,就好像外部类在自己处理自己。
上面说了匿名内部类的好处,尤其在处理界面UI方面使用很多,但是过多的控件需要处理时,我们就要new 很多的内部类,使我们的代码看起来有些杂乱,毕竟匿名内部类也是一个类,和我们所关注使用的外部类(比如Activity)看起来还是不协调,于是我们习惯的做法是让一个Activity实现OnClickListener接口,这时Activity就是一个公用类,它本身既可以显示控件,同时又可以处理控件的点击事件:
public void MyActivity extends Activity implements OnClickListener{
private Buttion buttion = new Buttion(this);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.buttion.setOnClickListener(this);
}
@Override
public void onClick(View view){
if (view.getTag().equals("test")){
//todo
}
}
}
这样代码看起来整洁了很多,而且事件处理方法onClick()就是Activity本身的实现方法,看起来好像更舒服了,在这里处理修改控件的事件也更加顺利成章了。
在xUtils这种通过Annotation注解方式来动态注入控件,onClick事件中,我们的控件省略的手动调用findViewById(); buttion.setOnClickListener(this);Activity也不用实现一堆的Listener接口了,对于类的继承和实现有强迫症的同学来说更加简洁了。
@OnClick(R.id.search_top_back)
private void onClick(View v) {
switch (v.getId()) {
case R.id.search_top_back:
finish();
break;
case R.id.search_top_search:
Intent intent = new Intent(MapsActivity.this, SearchActivity.class);
intent.putExtras(bundle);
context.startActivity(intent);
break;
}
}
这种动态注入其实就是使用动态代理的方式,表面返回的是Listener的代理对象,真正调用的是Activity中的加入@onClick注解的方法。
说了这么多,现在回到我们的主题,在xUtils中没有发现Handler的动态注入,为什么Handler不能使用动态代理呢,我们看下动态代理的代码:Proxy.java中
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h);
这里Class<?>[] interfaces就是返回的动态代理类的接口,也就是返回我们一个有着interfaces行为实现方法的对象,当然真正的处理是在InvocationHandler中的
public Object invoke(Object proxy, Method method, Object[] args)
这里来真正调用我们传入的对象,在Proxy.newProxyInstance()方法中调用了
return getProxyClass(loader, interfaces).getConstructor(
new Class<?>[] { InvocationHandler.class }).newInstance(
new Object[] { h });
然后我们再看下getProxyClass方法
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces) throws IllegalArgumentException {
// check that interfaces are a valid array of visible interfaces
if (interfaces == null) {
throw new NullPointerException("interfaces == null");
}
String commonPackageName = null;
for (int i = 0, length = interfaces.length; i < length; i++) {
Class<?> next = interfaces[i];
if (next == null) {
throw new NullPointerException("interfaces[" + i + "] == null");
}
String name = next.getName();
if (!next.isInterface()) {
throw new IllegalArgumentException(name + " is not an interface");
}
我们发现了 if (!next.isInterface())如果不是接口的话,就会抛出异常,正是这样由于Handler是一个普通的类,没有实现接口,所以无法使用动态代理。还有一个更重要的原因是Handler的使用需要new Handler();在初始化的过程中来绑定当前线程,得到线程中的MessageQueue队列,new之后有了消息队列和所属线程,我们在handler.sendMessage发送消息的时候,才能把Message放入到handler对应的线程的消息队列中。
基于以上原因我们无法使用动态代理类来注入handler,但是我们也想在xUtils的ViewUtils中加入,那我们只好手动改造一下了,这个时候我们就像最原始的使用那样,使用匿名内部类,不过要借助反射。
我们先定义了一个叫handle类型的注解,注意这个命名为了和handler区别开
@Retention(RetentionPolicy.RUNTIME)
public @interface Handle {
String value();
}
我们在ViewUtils中的
private static void injectObject(final Object handler, ViewFinder finder) 方法中,加入下面的代码:
Handle handle = field.getAnnotation(Handle.class);
if (handle != null) {
//得到Handle注解类型成员变量的值
String name = handle.value();
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (final Method method : methods) {
//得到handle注解类型方法
Handle handleMessage = method.getAnnotation(Handle.class);
if (handleMessage != null) {
String methodValue = handleMessage.value();
//如果注解值value相同(建议使用handler成员的引用名)
if (name.equals(methodValue)){
try {
field.setAccessible(true);
//这里的field就是成员变量handler,注意参数handler是
//我们的Acitivity实例,这里注入匿名内部类的实现
field.set(handler, new Handler(){
@Override
public void handleMessage(Message msg){
//调用Acitvity的handle注解类型方法
try {
method.invoke(handler, msg);
} catch (Exception e) {
LogUtils.e(e.getMessage(), e);
}
}
});
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
}
}
}
}
然后在Activity中我们就可以像其他控件一样注入handler了,
@Handle("myHandler ")
private Handler myHandler = null;
@Handle("myHandler ")
public void handleMsg(Message msg){
if (msg.what == 1){
aniEnd = true;
showMap();
}
}
现在代码看起来更加统一风格了,优雅的设计,简易的代码始终是我们喜欢的风格吧,
第一次写博客,有点乱,后面继续,回家吃饭了。