最近在网易云公开课上学习了关于okhttp的网络请求底层,做一下记录。
这是关于网络请求的逻辑图片。
网络访问框架是通过请求队列和线程池来进行网络访问框架,其中线程池分为任务线程和核心线程,核心线程的作用是不间断的去请求队列里获取请求,将请求和线程关联起来。而任务线程则是用来处理请求。
(一)首先我们来线程池管理类来处理请求和线程池的关系。
也就是图中需要处理的模块
/** * 线程和请求管理者 */ public class ThreadPoolManager { private static ThreadPoolManager threadPoolManager=new ThreadPoolManager(); public static ThreadPoolManager getInstance(){ return threadPoolManager; } //请求队列 private LinkedBlockingDeque<Runnable> mQueue=new LinkedBlockingDeque<>(); //将请求任务添加到请求队列里 public void addTask(Runnable runnable){ if (runnable!=null){ try { mQueue.put(runnable); } catch (InterruptedException e) { e.printStackTrace(); } } } //创建延迟队列 private DelayQueue<HttpTask> mDelayQueue=new DelayQueue(); //将失败的请求添加到延迟队列 public void addDelayTask(HttpTask httpTask){ if (httpTask!=null){ httpTask.setDelayTime(3000); mDelayQueue.offer(httpTask); } } //创建线程池 private ThreadPoolExecutor mThreadPoolExecutor; private ThreadPoolManager(){ mThreadPoolExecutor=new ThreadPoolExecutor(3, 10, 15, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { addTask(r); } }); mThreadPoolExecutor.execute(coreThread); mThreadPoolExecutor.execute(delayThread); } //核心线程 public Runnable coreThread=new Runnable() { Runnable runnable=null; @Override public void run() { while(true){ try { //去队列里拿线程 runnable=mQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } mThreadPoolExecutor.execute(runnable); } } }; //延迟线程 public Runnable delayThread=new Runnable() { HttpTask httpTask=null; @Override public void run() { while (true){ try { httpTask=mDelayQueue.take(); if (httpTask.getRetryCount()<3){ mThreadPoolExecutor.execute(httpTask); httpTask.setRetryCount(httpTask.getRetryCount()+1); Log.e("=========>",httpTask.getRetryCount()+" "+System.currentTimeMillis()); }else { Log.e("=========>","重试超过三次"); } } catch (InterruptedException e) { e.printStackTrace(); } } } }; }
其中比较难理解的点是关于LinkedBlockingQueue,这个地方对于这个不太常用,感觉以后可以去认真学习一下,现在对于他的理解就是一个阻塞队列。
另外一点就是关于ThreadPoolExecutor线程池,线程池的构造方法
RejectedExecution因为某些原因被拒绝,我们就重新将线程添加到请求队列里。
还有一个关键点就是核心线程中通过while(true)的死循环来不停的监听队列里的请求。
另外关于重试的后面再讲。
现在已经完成了请求的核心。
(二)然后我们继续来写框架,基于这几天的学习,如果想架构框架之类的东西,我们就要学会面向接口的架构思想
所以下面我们基于接口来进行架构
/** * 请求接口约束规范 */ public interface IHTTPRequest { //设置url void setUrl(String url); //设置参数 void setData(byte[] data); //返回监听 void setListener(CallbackListener listener); //访问网络 void execute(); }
/** * 返回监听 */ public interface CallbackListener { void onSuccess(InputStream inputStream); void onFail(); } 在写完两个Base类之后,我们根据base来进行二次约束 /** * 在IHttpRequest的基础上封装 */ public class JsonHttpRequest implements IHTTPRequest { private String url; private byte[] data; private CallbackListener listener; private HttpURLConnection urlConnection; @Override public void setUrl(String url) { this.url=url; } @Override public void setData(byte[] data) { this.data=data; } @Override public void setListener(CallbackListener listener) { this.listener=listener; } @Override public void execute() { URL url=null; try { url=new URL(this.url); urlConnection= (HttpURLConnection) url.openConnection(); 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(); OutputStream outputStream=urlConnection.getOutputStream(); BufferedOutputStream bufferedOutputStream=new BufferedOutputStream(outputStream); bufferedOutputStream.write(data); bufferedOutputStream.flush(); outputStream.close(); if (urlConnection.getResponseCode()==HttpURLConnection.HTTP_OK){ InputStream inputStream=urlConnection.getInputStream(); listener.onSuccess(inputStream); }else { throw new RuntimeException("请求失败"); } } catch (IOException e) { e.printStackTrace(); throw new RuntimeException("请求失败"); }finally { urlConnection.disconnect(); } } }
/** * 约束Callback */ public class JsonCallbackListener<T> implements CallbackListener { private Class<T> responseClass;//结果bean private Handler handler=new Handler(Looper.getMainLooper()); private JsonDataTrasformListener listener; public JsonCallbackListener(Class<T> responseClass,JsonDataTrasformListener listener) { this.responseClass = responseClass; this.listener=listener; } @Override public void onSuccess(InputStream inputStream) { //将InputString装换成对象 String respose = getContent(inputStream); final T clazz = new Gson().fromJson(respose, responseClass); handler.post(new Runnable() {//主线程 @Override public void run() { listener.onSuccess(clazz);//传递对象 } }); } @Override public void onFail() { } /** * 将inputsteanm转化为String * @param inputStream * @return */ private String getContent(InputStream inputStream) { String content = null; try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder stringBuilder = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { stringBuilder.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } return stringBuilder.toString(); } catch (Exception e) { e.printStackTrace(); } return content; } }
/** * 传递参数 */
public interface JsonDataTrasformListener<T> { void onTrasformSuccess(T z); }
(三)创建HttpTask
/** * 封装请求,也就是放入ThreadPoolManager里的请求队列的runable */ public class HttpTask<T> implements Runnable, Delayed { private IHTTPRequest ihttpRequest; public HttpTask(String url, T requestData, IHTTPRequest ihttpRequest, CallbackListener callbackListener) { this.ihttpRequest = ihttpRequest; ihttpRequest.setUrl(url); ihttpRequest.setListener(callbackListener); //将request类转化为string 后面再转为byte String content = new Gson().toJson(requestData); try { ihttpRequest.setData(content.getBytes("utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } @Override public void run() { try { ihttpRequest.execute(); } catch (Exception e) { //重试机制 ThreadPoolManager.getInstance().addDelayTask(this); } } private long delayTime; private int retryCount; public long getDelayTime() { return delayTime; } public void setDelayTime(long delayTime) { //延迟的时候需要算上当前时间 this.delayTime = System.currentTimeMillis() + delayTime; } public int getRetryCount() { return retryCount; } public void setRetryCount(int retryCount) { this.retryCount = retryCount; } @Override public long getDelay(@NonNull TimeUnit unit) { return unit.convert(this.delayTime-System.currentTimeMillis(),TimeUnit.MILLISECONDS); } @Override public int compareTo(@NonNull Delayed o) { return 0; } }
关于delayed后面再讲
(四)创建工具类
/** * 网络访问工具类 */ public final class TXHttp { public static <T, M> void sendJsonRequest(String url, T requestData, Class<M> response, JsonDataTrasformListener listener) { //请求 IHTTPRequest ihttpRequest = new JsonHttpRequest(); //回调监听 CallbackListener callbackListener = new JsonCallbackListener<>(response, listener); //HttpTask的请求 runnable HttpTask httpTask = new HttpTask(url, requestData, ihttpRequest, callbackListener); //线程池添加请求 ThreadPoolManager.getInstance().addTask(httpTask); } }
(五)调用工具类
TXHttp.sendJsonRequest(url, null, BaseResponse.class, new JsonDataTrasformListener() { @Override public void onTrasformSuccess(Object z) { } });
以上就完成了网络请求框架的搭建。
(六)重试机制
关于重试机制,其实跟正常的请求是一样的,创建一个重试的请求队列,然后将重试的队列同样添加到线程池中,设置一个重试的次数和重试时间。
需要注意的点是HttpTask需要实现delay
在ThreadPoolManager里创建延迟队列和延迟线程
当网络请求出错或者抛出异常时,我们就可以进行重试,所以我们在HTTPTask的run方法里try catch一下。
学过之后目前有问题的其实还是请求队列里的
LinkedBlockingDeque
DelayQueue
这两个队列还不是很清晰,以后可以研究一下,现在还处于模仿阶段。