开始前
网络访问框架关心的问题:
- 能并发接受多个请求,并返回"用户"需要的数据
- 重试机制
实现方式:
- 队列
- 线程池
网络框架实现步骤
- 创建线程池管理类(队列,线程池)
- 封装请求参数
- 封装响应数据
- 封装请求任务
- 封装"使用工具"
- 添加重试机制
创建线程池管理类
创建 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 方法负责具体的任务执行.
例如我们的请求类型为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