网络请求框架其实google已经提供了不少的开源框架,但为什么还需要手写呢?看到这篇文章的博友不知道有没有在一二线的企业工作过,像BAT这些企业,他们开发出的项目,很少会有用到谷歌或者android提供的原生api,这也是高级Android开发工程师所必备的技术,就是自己手写出开源框架的项目。接下来我就给大家演示一下volley的框架的手写实现,这个不同于volley本身的框架,在手写的过程中,我会将不是本项目需要的代码给剔除掉,并创新出自己的设计思想。
开发一个网络访问框架需要考虑的问题:
1.扩展性:不能只处理json或者xml或者image等等的某一种;
2.易用性:解决一个小问题时候,只需要修改一两行代码;
3.稳定性:自己写的框架比开源的框架要更稳定,要明白只要是人开发出来的东西,我也能自己写出来;
4.和其他框架能否集成。
自己要写的网络框架的需求:
1.不关心数据解析,获取数据为对象;
2.网络访问是在子线程,实现线程切换,操作可以发生在主线程;
3.高并发;
4.缓存策略。
设计的框架图形为:
第一步:写一个网络消息处理中心的管理类:ThreadPoolManager,这个类有三个作用:1、将应用层去过来的任务添加到请求队列中;2、把队列中的任务放入线程池中;3、让线程池工作起来;该类的截图如下:
public class ThreadPoolManager { //1、把任务添加到请求队列中 private LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(); //添加任务,把调用层取过来到任务放入请求队列中 public void execute(Runnable runnable) { if (runnable != null) { try { queue.put(runnable); } catch (InterruptedException e) { e.printStackTrace(); } } } //2、把队列中到任务放入到线程池 private ThreadPoolExecutor threadPoolExecutor; private ThreadPoolManager() { threadPoolExecutor = new ThreadPoolExecutor(4, 20, 15, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), rejectedExecutionHandler); threadPoolExecutor.execute(runnable); } //线程池拒绝策略,防止15s内没处理完,线程池会丢弃线程 private RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { //参数r就是超时线程 try { queue.put(r); } catch (InterruptedException e) { e.printStackTrace(); } } }; //3、让线程池工作起来 private Runnable runnable = new Runnable() { @Override public void run() { while (true) { Runnable runnable1 = null; //从队列中取出请求 try { runnable1 = queue.take(); } catch (InterruptedException e) { e.printStackTrace(); } if (runnable1 != null) { threadPoolExecutor.execute(runnable1); } } } }; private static ThreadPoolManager ourInstance = new ThreadPoolManager(); public static ThreadPoolManager getOurInstance() { return ourInstance; } }
第二步:
策略设计模式:设计请求和响应的顶层接口。
请求的接口:IHttpService
/** * 封装请求 * * Created by chengkai on 2018/4/6. */ public interface IHttpService { //1.设置URL void setUrl(String url); //2.设置请求参数 void setRequestData(byte[] requestData); //3.执行请求 void execute(); //需要设置两个接口之间的关系 void setHttpCallBack(IHttpListener httpListener); }
响应的接口:IHttpListener
/** * 封装响应 * * Created by chengkai on 2018/4/6. */ public interface IHttpListener { //接收上一个接口的结果 void onSuccess(InputStream inputStream); void onFailure(); }
应用层发起一起网络请求的实体类:HttpTask
/** * 一次http的任务类 * * Created by wangsujuan on 2018/4/6. */ public class HttpTask<T> implements Runnable{ private IHttpListener httpListener; private IHttpService httpService; /** * HttpTask:应用层发起一次网络请求的实体类 * @param requestInfo 应用层发起请求的参数 * @param url 应用层发起请求的url * @param httpService * @param httpListener * @param <T> */ public<T> HttpTask(T requestInfo, String url, IHttpService httpService, IHttpListener httpListener) { this.httpService = httpService; this.httpListener = httpListener; httpService.setUrl(url); httpService.setHttpCallBack(httpListener); if (requestInfo != null) { String requestContent = JSON.toJSONString(requestInfo); try { httpService.setRequestData(requestContent.getBytes("utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } @Override public void run() { httpService.execute(); } }
IHttpService接口的实现类:JsonHttpService代码如下
public class JsonHttpService implements IHttpService{ private String url; private byte[] requestData; private IHttpListener httpListener; @Override public void setUrl(String url) { this.url = url; } @Override public void setRequestData(byte[] requestData) { this.requestData = requestData; } /** * 真实的网络操作在这里实现 */ @Override public void execute() { httpUrlconnPost(); } public void httpUrlconnPost(){ URL url = null; try { url = new URL(this.url); urlConnection = (HttpURLConnection) url.openConnection();//打开http连接 urlConnection.setConnectTimeout(6000);//连接的超时时间 urlConnection.setUseCaches(false);//不使用缓存 urlConnection.setInstanceFollowRedirects(true);//是成员函数,仅作用于当前函数,设置这个连接是否可以被重定向 urlConnection.setReadTimeout(3000);//响应的超时时间 urlConnection.setDoInput(true);//设置这个连接是否可以写入数据 urlConnection.setDoOutput(true);//设置这个连接是否可以输出数据 urlConnection.setRequestMethod("POST");//设置请求的方式 urlConnection.setRequestProperty("Content-Type", "application/json;charset=UTF-8");//设置消息的类型 urlConnection.connect();// 连接,从上述至此的配置必须要在connect之前完成,实际上它只是建立了一个与服务器的TCP连接 //-------------使用字节流发送数据-------------- OutputStream out = urlConnection.getOutputStream(); BufferedOutputStream bos = new BufferedOutputStream(out);//缓冲字节流包装字节流 if (requestData != null) { bos.write(requestData); } //把这个字节数组的数据写入缓冲区中 bos.flush();//刷新缓冲区,发送数据 out.close(); bos.close(); //------------字符流写入数据------------ if(urlConnection.getResponseCode()==HttpURLConnection.HTTP_OK) {//得到服务端的返回码是否连接成功 InputStream in = urlConnection.getInputStream(); httpListener.onSuccess(in); } } catch (Exception e) { e.printStackTrace(); httpListener.onFailure(); }finally{ urlConnection.disconnect();//使用完关闭TCP连接,释放资源 } } HttpURLConnection urlConnection = null; @Override public void setHttpCallBack(IHttpListener httpListener) { this.httpListener = httpListener; } }
IHttpListener接口的实现类JsonHttpListener代码如下:
public class JsonHttpListener<M> implements IHttpListener { private Class<M> responseClass; private IDataListener<M> dataListener; //用于线程到切换 private Handler handler = new Handler(Looper.getMainLooper()); public JsonHttpListener(IDataListener<M> dataListener, Class<M> responseClass) { this.dataListener = dataListener; this.responseClass = responseClass; } @Override public void onSuccess(InputStream inputStream) { //获取响应结果,把byte数据转换成String数据 String content = getContent(inputStream); //处理json字符串,转换为对象 final M response = JSON.parseObject(content, responseClass); //把结果传送到调用层 handler.post(new Runnable() { @Override public void run() { if (dataListener != null) { dataListener.onSuccess(response); } } }); } @Override public void onFailure() { //把结果传送到调用层 handler.post(new Runnable() { @Override public void run() { if (dataListener != null) { dataListener.onFailure(); } } }); } private String getContent(InputStream inputStream) { String content = null; try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { System.out.println("Error=" + e.toString()); } finally { try { inputStream.close(); } catch (IOException e) { System.out.println("Error=" + e.toString()); } } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return content; } }
回调给应用层的接口:IDataListener代码如下:
/** * 回调给应用层到接口 * * Created by chengkai on 2018/4/6. */ public interface IDataListener<M> { void onSuccess(M m); void onFailure(); }
第三步:框架层提供给应用层调用的接口:
public class Volley { public static<T,M> void sendJsonRequest(T requestInfo, String url, Class<M> response, IDataListener<M> dataListener) { IHttpService httpService = new JsonHttpService(); IHttpListener httpListener = new JsonHttpListener<>(dataListener,response); HttpTask<T> httpTask = new HttpTask<T>(requestInfo,url,httpService,httpListener); ThreadPoolManager.getOurInstance().execute(httpTask); } }
在button按钮点击的效果:
ublic void click(View view) { Volley.sendJsonRequest(null, url, ResponceData.class, new IDataListener<ResponceData>() { @Override public void onSuccess(ResponceData responceData) { Log.i("chengkai","城市:" + responceData.result.today.city); } @Override public void onFailure() { Log.i("chengkai", "访问失败"); } }); }
最终调试的效果为:
到此说明测试成功!项目代码github下载地址为:https://github.com/cWX411904/VolleyDesignByMySelf