AndroidInject项目使用动态代理增加对网络请求的支持
以下内容为原创,欢迎转载,转载请注明
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3540427.html
AndroidInject项目是我写的一个使用注解注入来简化代码的开源项目
https://github.com/wangjiegulu/androidInject
今天新增功能如下:
1. 增加@AIScreenSize注解,作用于属性,用于注入当前设备的屏幕大小(宽高)
2. 增加对网络请求的支持,使用动态代理实现:@AIGet注解,作用于接口方法,表示以GET来请求url;@AIPost注解,作用于接口方法,表示以POST来请求url;@AIParam,用于注入请求参数
3. 增加@AINetWorker注解,作用于属性,用于注入网络请求服务
4. 增加GET或POST请求时请求参数可使用Params类传入,简化代码
主要执行代码如下:
用@AINetWorker注解注入NetWorker接口的子类代理(动态代理模式):
@AINetWorker
private PersonWorker personWorker;
然后启动线程,在线程中调用进行网络请求:
new Thread(new Runnable() {
@Override
public void run() {
// RetMessage<Person> retMsg = personWorker.getPersonsForGet("a1", "b1", "c1");
// RetMessage<Person> retMsg = personWorker.getPersonsForGet2(new Params().add("aa", "a1").add("bb", "b1").add("cc", "c1"));
RetMessage<Person> retMsg = personWorker.getPersonsForPost2(new Params().add("aa", "a1").add("bb", "b1").add("cc", "c1"));
System.out.println(retMsg.getList().toString());
}
}).start();
请求的结果封装在RetMessage类中(AndroidInject框架所作的事就是执行Get或者Post请求,获得返回结果,然后json解析后封装在RetMessage中):
package com.wangjie.androidinject.annotation.core.net; import com.google.gson.Gson; import java.util.List; /**
* Json响应结果包装类
* Created with IntelliJ IDEA.
* Author: wangjie email:tiantian.china.2@gmial.com
* Date: 14-2-7
* Time: 下午4:25
*/
public class RetMessage<T>
{
private int resultCode; // 结果码,必须包含 private List<T> list; // 返回的数据 private T obj; // 返回的数据 private Integer size; // 返回数据长度 private String errorMessage; // 返回错误信息 public String toJson(){
return new Gson().toJson(this);
}
// getter和setter方法省略...
}
接下来看下PersonWorker接口中所作的事情:
package com.wangjie.androidinject; import com.wangjie.androidinject.annotation.annotations.net.AIGet;
import com.wangjie.androidinject.annotation.annotations.net.AIParam;
import com.wangjie.androidinject.annotation.annotations.net.AIPost;
import com.wangjie.androidinject.annotation.core.net.RetMessage;
import com.wangjie.androidinject.annotation.util.Params;
import com.wangjie.androidinject.model.Person; /**
* Created with IntelliJ IDEA.
* Author: wangjie email:tiantian.china.2@gmail.com
* Date: 14-2-7
* Time: 下午1:44
*/
public interface PersonWorker {
@AIGet("http://192.168.2.198:8080/HelloSpringMVC/person/findPersons?aa=#{a3}&bb=#{b3}&cc=#{c3}")
public RetMessage<Person> getPersonsForGet(@AIParam("a3")String a2, @AIParam("b3") String b2, @AIParam("c3") String c2); @AIPost("http://192.168.2.198:8080/HelloSpringMVC/person/findPersons")
public RetMessage<Person> getPersonsForPost(@AIParam("aa")String a2, @AIParam("bb") String b2, @AIParam("cc") String c2); @AIGet("http://192.168.2.198:8080/HelloSpringMVC/person/findPersons")
public RetMessage<Person> getPersonsForGet2(Params params); @AIPost("http://192.168.2.198:8080/HelloSpringMVC/person/findPersons")
public RetMessage<Person> getPersonsForPost2(Params params); }
PersonWorker是自己写的一个接口(以后需要有新的网络请求,都可以类似编写Worker),声明了执行网络请求的各种方法,这些方法需要加上@AIGet或者@AIPost注解,用于声明请求方式,并在此注解中的value()值设置为所要请求的url(此注解的其他属性后续会陆续扩展)
方法的@AIParam注解是作用与mybatis的@Param注解类似,可以设置请求携带的参数
如果参数比较多,则推荐使用Params来存放参数,以此来简化代码,Params类实质上就是一个HashMap,存放参数的键值对即可。
接下来分析下框架是怎么实现的,其实上面讲过,主要是用Annotaion和动态代理了
首先看看PersonWorker的注入,在AIActivity(AIActivity,AndroidInject开源项目中的Activity使用注解的话,你写的Activity必须继承AIActivity,另外如果要使用FragmentActivity,则需要继承AISupportFragmentActivity)启动时,首先会去解析添加的注解,这里讨论@AINetWorker注解,内部代码很简单:
/**
* 注入NetWorker
* @param field
* @throws Exception
*/
private void netWorkerBind(Field field) throws Exception{
field.setAccessible(true);
field.set(present, NetInvoHandler.getWorker(field.getType()));
}
通过代码可知,是使用反射来实现的,主要的代码是这句:
NetInvoHandler.getWorker(field.getType());
这句代码的作用是通过Class获得一个PersonWorker实现类的代理对象,这里很明显是使用了动态代理。
所以,最核心的类应该是NetInvoHandler这个类,这个类的代码如下(篇幅问题,所以就折叠了):
package com.wangjie.androidinject.annotation.core.net; import android.text.TextUtils;
import com.google.gson.Gson;
import com.wangjie.androidinject.annotation.annotations.net.AIGet;
import com.wangjie.androidinject.annotation.annotations.net.AIParam;
import com.wangjie.androidinject.annotation.annotations.net.AIPost;
import com.wangjie.androidinject.annotation.util.Params;
import com.wangjie.androidinject.annotation.util.StringUtil; import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map; /**
* Created with IntelliJ IDEA.
* Author: wangjie email:tiantian.china.2@gmail.com
* Date: 14-2-7
* Time: 下午1:40
*/
public class NetInvoHandler implements InvocationHandler{
private static HashMap<Class<?>, NetInvoHandler> invoHandlers = new HashMap<Class<?>, NetInvoHandler>(); private Object proxy; // 代理对象 public synchronized static<T> T getWorker(Class<T> clazz){
NetInvoHandler netInvoHandler = invoHandlers.get(clazz);
if(null == netInvoHandler){
netInvoHandler = new NetInvoHandler();
netInvoHandler.setProxy(Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, netInvoHandler));
invoHandlers.put(clazz, netInvoHandler);
}
return (T)netInvoHandler.getProxy();
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // get请求
if(method.isAnnotationPresent(AIGet.class)){
AIGet aiGet = method.getAnnotation(AIGet.class);
String url = aiGet.value();
if(TextUtils.isEmpty(url)){
throw new Exception("net work [" + method.getName() + "]@AIGet value()[url] is empty!!");
}
Annotation[][] annotaions = method.getParameterAnnotations();
for(int i = 0; i < args.length; i++){
if(Params.class.isAssignableFrom(args[i].getClass())){ // 如果属性为Params,则追加在后面
url = StringUtil.appendParamsAfterUrl(url, (Params)args[i]);
}else{ // 如果属性添加了@AIParam注解,则替换链接中#{xxx}
String repName = ((AIParam)annotaions[i][0]).value();
url = url.replace("#{" + repName + "}", args[i] + "");
} }
StringBuilder sb = NetWork.getStringFromUrl(url);
if(null == sb){
return null;
}
return new Gson().fromJson(sb.toString(), method.getReturnType());
} // post请求
if(method.isAnnotationPresent(AIPost.class)){
AIPost aiPost = method.getAnnotation(AIPost.class);
String url = aiPost.value();
if(TextUtils.isEmpty(url)){
throw new Exception("net work [" + method.getName() + "]@AIPost value()[url] is empty!!");
}
Annotation[][] annotaions = method.getParameterAnnotations();
Map<String, String> map = new HashMap<String, String>();
for(int i = 0; i < args.length; i++){
if(Params.class.isAssignableFrom(args[i].getClass())){ // 如果属性为Params,则追加在后面
map.putAll((Params)args[i]);
}else{
String repName = ((AIParam)annotaions[i][0]).value();
map.put(repName, args[i] + "");
} }
StringBuilder sb = NetWork.postStringFromUrl(url, map);
if(null == sb){
return null;
}
return new Gson().fromJson(sb.toString(), method.getReturnType()); } return null;
} public Object getProxy() {
return proxy;
} public void setProxy(Object proxy) {
this.proxy = proxy;
}
}
里面的代码还没有好好的重构,所以,看起来会更直白,该类实现了InvocationHandler,很明显的动态代理。
我们通过NetInvoHandler的getWorker静态方法,来获取一个指定Class的Worker实现类的代理对象,由于实际应用时,Worker接口应该会很多,为了不重复生成相同Worker实现类的代理对象,所以这里在生成一个后,保存起来,确保一个Worker只生成一个代理对象,一个NetInvoHandler。
这里有个地方需要注意一下,以前使用的动态代理,需要一个RealSubject,也就是真实对象,是Worker的实现类。这样,在invoke方法中就可以调用真实对象的对应方法了,但是现在,进行网络请求,我们没有去写一个类然后实现PersonWorker接口,因为没有必要,我们完全可以在invoke方法中去执行相同的网络请求。
请想下,之所以需要框架的存在 不就是为了把一些模板的东西给简化掉么?现在的网络请求这些步骤就是一些模板话的东西,我们需要的就是调用方法,自动进行网络请求(框架做的事),然后返回给我结果。所以网络请求这一步,写在invoke方法中即可。
而不是所谓的编写一个类,实现PersonWorker接口,在这个实现类中进行网络请求,然后在invoke方法中调用真实对象的对应该方法。
因此,在Proxy.newProxyInstance的interfaces中填写需要实现的接口,也就是现在的PersonWorker。
接下来看下invoke中做的事情,首先根据方法增加的注解来识别是GET请求还是POST请求。然后各自执行请求(因为我请求的执行,写在NetWork中了,这里直接返回了请求结果字符串StringBuilder sb)。
接下来,使用Gson这个霸气的工具,一键从json解析封装成RetMessage对象。(所以,这里是需要Gson库的支持,大家网上下载gson.jar,或者使用maven)
当然,要使用Gson一键解析封装的前提是服务器端的编写需要保存一致性,下面是我服务器端测试的代码:
@RequestMapping("/findPersons")
public void findPersons(HttpServletRequest request, HttpServletResponse response,
@RequestParam("aa") String aa,
@RequestParam("bb") String bb,
@RequestParam("cc") String cc) throws IOException{
System.out.println("aa: " + aa + ", bb: " + bb + ", cc: " + cc);
RetMessage<Person> rm = new RetMessage<Person>();
rm.setResultCode(0);
List<Person> persons = new ArrayList<Person>();
for(int i = 0; i < 5; i++){
Person p = new Person();
p.setName("wangjie_" + i);
p.setAge(20 + i);
persons.add(p);
}
rm.setList(persons);
ServletUtil.obtinUTF8JsonWriter(response).write(rm.toJson());
}
服务器端返回结果时,也是封装在RetMessage类中,这个类服务器端和客户端是保持一致的,所以可以一键转换。
如果你在开发的过程中,RetMessage类中封装的东西不能满足你的需求,可以自己编写结果类,当然在Worker中声明方法中返回值就应该是你写的结果类了。
到此为止,讲解完毕
另:如果,你需要写一个查询User信息的网络请求,应该怎么写?
只需编写UserWorker接口,然后声明方法findUsers(),写上@AIGet或者@AIPost注解,写明url和请求参数。然后通过@AINetWorker注解注入userWorker,然后开启线程,调用userWorker的findUsers()方法即可。
当然UserWorker也可以不使用注解获得,而是调用“NetInvoHandler.getWorker(UserWorker.class)”获得!
AndroidInject项目使用动态代理增加对网络请求的支持的更多相关文章
- 动态代理实现设置tomcat请求编码
1)htmlcode: <html> <head> <title>$Title$</title> </head> <body> ...
- 杨晓峰-Java核心技术-6 动态代理 反射 MD
目录 第6讲 | 动态代理是基于什么原理? 典型回答 考点分析 知识扩展 反射机制及其演进 动态代理 精选留言 Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAnd ...
- 4.使用Redis+Flask维护动态代理池
1.为什么使用代理池 许多⽹网站有专⻔门的反爬⾍虫措施,可能遇到封IP等问题. 互联⽹网上公开了了⼤大量量免费代理理,利利⽤用好资源. 通过定时的检测维护同样可以得到多个可⽤用代理理. 2.代理池的要 ...
- 一文读懂Java中的动态代理
从代理模式说起 回顾前文: 设计模式系列之代理模式(Proxy Pattern) 要读懂动态代理,应从代理模式说起.而实现代理模式,常见有下面两种实现: (1) 代理类关联目标对象,实现目标对象实现的 ...
- CgLib动态代理学习【Spring AOP基础之一】
如果不了解JDK中proxy动态代理机制的可以先查看上篇文章的内容:Java动态代理学习[Spring AOP基础之一] 由于Java动态代理Proxy.newProxyInstance()的时候会发 ...
- Java提高班(六)反射和动态代理(JDK Proxy和Cglib)
反射和动态代理放有一定的相关性,但单纯的说动态代理是由反射机制实现的,其实是不够全面不准确的,动态代理是一种功能行为,而它的实现方法有很多.要怎么理解以上这句话,请看下文. 一.反射 反射机制是 Ja ...
- JDK动态代理浅析
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2018-06-29/17.html 作者:夜月归途 出处:http://www.guitu ...
- iOS项目中的网络请求和上下拉刷新封装
代码地址如下:http://www.demodashi.com/demo/11621.html 一.运行效果图 现在的项目中不可避免的要使用到网络请求,而且几乎所有软件都有上下拉刷新功能,所以我在此对 ...
- Java中的反射机制和动态代理
一.反射概述 反射机制指的是Java在运行时候有一种自观的能力,能够了解自身的情况为下一步做准备,其想表达的意思就是:在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法:对于任意一个对象 ...
随机推荐
- Android OpenGL 编写简单滤镜
Android 上使用Opengl进行滤镜渲染效率较高,比起单纯的使用CPU给用户带来的体验会好很多.滤镜的对象是图片,图片是以Bitmap的形式表示,Opengl不能直接处理Bitmap,在Andr ...
- mysql插入日期 vs oracle插入日期
今天做oracle日期插入的时候突然开始疑惑日期是如何插入的. 用框架久了,反而不自己做简单的工作了.比如插入. 通常,新建一个表对象,然后绑定数据,前端form提交,后端getModel后直接mod ...
- [UWP]涨姿势UWP源码——极简的RSS阅读器
涨姿势UWP,一个开源的RSS阅读器,一个纯粹的项目,一个有道德的APP,一个脱离了低级趣味的作者,一些有益于人民的代码.骚年,还等什么,来涨点姿势吧! 该项目代码可能会引起部分人群的不适,敏感人群请 ...
- JS魔法堂:jQuery.Deferred(jQuery1.5-2.1)源码剖析
一.前言 jQuery.Deferred作为1.5的新特性出现在jQuery上,而jQuery.ajax函数也做了相应的调整.因此我们能如下的使用xhr请求调用,并实现事件处理函数晚绑定. var p ...
- JavaScript基础—dom,事件
Js基础-DOM 1:dom:文档对象模型 Dom就是html页面的模型,将每个标签都作为一个对象,js通过调用dom中的属性,方法就可以对网页中的文本框,层等元素进行编程控制.Dom就是一些让jav ...
- HtmlAgilityPack搭配 ScrapySharp或HtmlAgilityPack.CssSelectors
Html Agility Pack 源码中的类大概有28个左右,其实不算一个很复杂的类库,但它的功能确不弱,为解析DOM已经提供了足够强大的功能支持,可以跟jQuery操作DOM媲 美:)Html A ...
- Castle ActiveRecord相关错误
1.Could not compile the mapping document: (string)错误? 如果确保配置文件没有错误,那请检查用户身份,必须是Administrator才行,即使有管理 ...
- LCA---Tarjan算法
本问题中Tarjan算法不需要设置栈和dfn,low标号,而是设置了并查集. 通过一次dfs遍历即可找出所有节点对的lca.将这样一次读取所有查询,计算一次后返回所有查询lca的算法称为离线算法 涉及 ...
- MSCRM 获取列表所选记录相关信息
问题:如何通过JS获取列表中所选记录信息? 解决办法: The CRM2011 Ribbon has a special set of parameters called 'CrmParameters ...
- Scalaz(51)- scalaz-stream: 资源使用安全-Resource Safety
scalaz-stream是一个数据流处理工具库,对资源使用,包括:开启文件.连接网络.连接数据库等这些公共资源使用方面都必须确定使用过程的安全:要保证在作业终止时能进行事后处理程序(finalize ...