Android进阶|手撸一个简单的网络框架

开始前 网络访问框架关心的问题:

  • 能并发接受多个请求,并返回"用户"需要的数据
  • 重试机制
实现方式:
  • 队列
  • 线程池
网络框架实现步骤
  1. 创建线程池管理类(队列,线程池)
  2. 封装请求参数
  3. 封装响应数据
  4. 封装请求任务
  5. 封装"使用工具"
  6. 添加重试机制
创建线程池管理类 创建 ThreadPoolManager.java 类,负责管理请求队列和线程池
//1. 创建队列,用来保存异步请求任务 private LinkedBlockingQueue mQueue = new LinkedBlockingQueue<>(); //LinkedBlockingQueue FIFO //2. 添加异步任务到队列中 public void addTask(Runnable runnable) { try { if (runnable != null) { mQueue.put(runnable); } } catch (InterruptedException e) { e.printStackTrace(); } } //3. 创建线程池 private ThreadPoolExecutor mThreadPoolExecutor; //4. 创建队列与线程池的"交互"线程 public Runnable communicateThread = new Runnable() { @Override public void run() { Runnable runnable = null; while (true) { try { runnable = mQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } //执行线程池中的线程任务 mThreadPoolExecutor.execute(runnable); } } };

[注] communicateThread 线程负责从 mQueue 队列中获取请求任务,并放到 mThreadPoolExecutor 线程池中执行.
构造单例的 ThreadPoolManager,构造方法中初始化线程池并执行 communicateThread 线程
private ThreadPoolManager() {mThreadPoolExecutor = new ThreadPoolExecutor( 3, 10, 15, TimeUnit.SECONDS, new ArrayBlockingQueue(4), new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { //处理被抛出来的任务(被拒绝的任务) addTask(r); } }); mThreadPoolExecutor.execute(communicateThread); }

[注] 线程池的设定依据具体项目而定.

RejectedExecutionHandler回调, 任务拒绝后,重新添加到队列之中.
封装请求参数 定义接口 IHttpRequest.java 实现必要的参数
public interface IHttpRequest {/** * 协议地址 * @param url */ void setUrl(String url); /** * 设置请求参数 */ void setData(byte[] bytes); /** * 数据数据回调 * @param callbackListener */ void setListener(CallbackListener callbackListener); /** * 执行请求 */ void execute(); }

execute 方法负责具体的任务执行.
【Android进阶|手撸一个简单的网络框架】例如我们的请求类型为JSON, 我们可以实现一个JSON的请求
public class JsonHttpRequest implements IHttpRequest {// 省略其他实现方法@Override public void execute() { URL url = null; HttpURLConnection urlConnection = null; try { url = new URL(this.url); //省略HttpURLConnection请求参数 if (urlConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {//得到服务器返回码是否连接成功 InputStream in = urlConnection.getInputStream(); mCallbackListener.onSuccess(in); } else { throw new RuntimeException("请求失败"); } } catch (Exception e) { throw new RuntimeException("请求失败"); } finally { if (urlConnection != null) { urlConnection.disconnect(); } }} }

封装响应数据 从上面可以看到有一个 CallbackListener 接口, 负责数据的成功和失败回调
public interface CallbackListener {/** * 成功回调 * @param inputStream */ void onSuccess(InputStream inputStream); /** * 失败 */ void onFailed(); }

特别的,如果我们请求的是JSON格式的数据, 我们可以自己实现一个Callback, JsonCallbackListener 用于数据的获取和解析
public class JsonCallbackListener implements CallbackListener {private Class resposeClass; private IJsonDataListener jsonDataListener; Handler handler = new Handler(Looper.getMainLooper()); public JsonCallbackListener(Class responseClass, IJsonDataListener listener) { this.resposeClass = responseClass; this.jsonDataListener = listener; }@Override public void onSuccess(InputStream inputStream) { String response = getContent(inputStream); Log.d(TAG, "onSuccess: response: " + response); final T clazz = new Gson().fromJson(response, resposeClass); handler.post(new Runnable() { @Override public void run() { jsonDataListener.onSuccess(clazz); } }); }private String getContent(InputStream inputStream) { String content = ""; //省略解析过程 return content; }@Override public void onFailed() {} }

封装请求任务 添加一个 HttpTask 继承自 Runnable, 作为请求任务
public class HttpTask implements Runnable {private IHttpRequest mHttpRequest; public HttpTask(T requestData, String url, IHttpRequest httpRequest, CallbackListener callbackListener) { mHttpRequest = httpRequest; httpRequest.setUrl(url); httpRequest.setListener(callbackListener); Log.d(TAG, "HttpTask: url: " + url); String content = new Gson().toJson(requestData); try { httpRequest.setData(content.getBytes("utf-8")); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }/// // implements Runnable ///@Override public void run() { try { mHttpRequest.execute(); } catch (Exception e) { //.... } }}

在构造方法中获取请求参数, run 方法中执行 IHttpRequest 中的 execute 获取网络数据
封装使用工具 为方便使用方使用,有必要封装成工具类
添加 LuOkHttp.java 作为请求工具类
public class LuOkHttp {/** * 发送网络请求 */ public static void sendJsonRequest(T request, String url, Class response, IJsonDataListener listener) { IHttpRequest httpRequest = new JsonHttpRequest(); JsonCallbackListener mJsonCallbackListener = new JsonCallbackListener<>(response, listener); HttpTask httpTask = new HttpTask<>(request, url, httpRequest, mJsonCallbackListener); ThreadPoolManager.getInstance().addTask(httpTask); } }

至此,基本的请求已经实现, 可以运行试一下了.
添加重试机制 网络访问在很多情况下会失败,例如通过隧道,坐电梯等,所以有必要在框架层实现重试机制.
首先,需要在我们的线程池管理类 ThreadPoolManager 中添加延时队列
// 创建延时队列 private DelayQueue mDelayQueue = new DelayQueue<>(); //添加到延时队列 public void addDelayTask(HttpTask httpTask) { if (httpTask != null) { httpTask.setDelayTime(3000); mDelayQueue.offer(httpTask); Log.d(TAG, "addDelayTask: "); } }

同样的, 也需要一个线程来负责将延时队列中的任务放到线程池中.
public Runnable delayThread = new Runnable() { @Override public void run() { HttpTask ht = null; while (true) { try { ht = mDelayQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } if (ht != null && ht.getRetryCount() < 3) { mThreadPoolExecutor.execute(ht); ht.setRetryCount(ht.getRetryCount() + 1); Log.d(TAG, "run: 重试机制: " + ht.getRetryCount()); } else { Log.d(TAG, "run: 重试机制:超出次数 "); } } } };

另外,不要忘记在 ThreadPoolManager 的构造方法中执行这个线程.
private ThreadPoolManager() { //... mThreadPoolExecutor.execute(delayThread); }

现在, 你可以断网测试一下我们的重试机制是否生效.
源码地址 https://github.com/changer0/OkHttpDemo

    推荐阅读