从代码学习Android网络框架
代码开源地址: https://github.com/hehonghui/simple_net_framework
作者博客地址: http://blog.csdn.net/column/details/simple-net.html
SimpleNet网络框架 SimpleNet是一个简单的Android网络框架,该框架的结构类似Volley,该框架是为了让不太熟悉框架开发或者说不太了解Android网络编程的同学学习使用。
自己在这里学习一下大神的想法,稍作总结
1.Simple_Net_Framework基本结构
熟悉Volley的同学们发现很相似,没错作者也说了是按照Volley造的轮子。主要分为4部分
1.1 Request
各种请求类型。Request是个抽象,针对不同的请求数据有不同的实现类。例如返回的数据类型为json的对应为JsonRequest,返回数据字符串的为StringRequest,如果需要上传文件,那么你需要使用MultipartRequest,该请求只支持小文件的上传,如果上传的文件过大则会产生OOM
1.2 RequestQueue
第二部分为消息队列,消息队列维护了提交给网络框架的请求列表,并且根据相应的规则进行排序。默认情况下更具优先级和进入队列的顺序来执行,该队列使用的是线程安全的PriorityBlockingQueue,因为我们的队列会被并发的访问,因此需要保证访问的原子性。
1.3 NetWorkExecutors
第三部分是Executor,也就是网络的执行者。该Executor继承自Thread,在run方法中循环访问第二部分的请求队列,请求完成之后将结果投递给UI线程。为了更好的控制请求队列,例如请求排序、取消等操作,这里我们并没有使用线程池来操作,而是自行管理队列和Thread的形式,这样整个结构也变得更为灵活。
1.4 Response Delivery
第四部分则是Response投递类,在第三部分的Executor中执行网络请求,Executor是Thread,但是我们并不能在主线程中更新UI,因此我们使用ResponseDelivery来封装Response的投递,保证Response执行在UI线程。
2.Simple_Net_Framework的设计理论
Request是一个抽象的泛型类,泛型类型就是返回的Response类型,例如StringRequest就是继承自Request。第二部分的RequestQueue依赖于Request,Request是抽象的,因此任何Request的子类都可以传递到请求队列中来,它依赖的是抽象Request,而不是具体的某个实现,因此保证了可扩展性。你可以自己实现自己所需的Request,例如大文件的上传Request。同理,第三部分的NetworkExecutor也只是依赖于Request抽象,但这里又引入了一个类型HttpStack,这个网络请求的真正执行者,有HttpClientStack和HttpUrlConnStack,两者分别为Apache的HttpClient和java的HttpURLConnection,关于这两者的区别请参考:Android访问网络,使用HttpURLConnection还是HttpClient?。HttpStack也是一个抽象,具体使用HttpClient还是HttpURLConnection则由运行系统版本来定,HttpStackFactory会根据系统版本给框架返回对应的HttpStack。最后的ResponseDelivery比较简单了,只是通过Handler将结果投递给UI线程执行,也就是执行RequestListener的onComplete方法,此时网络执行完成,用户即可在该方法中更新UI或者相关的其他的操作。
3.代码阅读
在开始阅读源码前,先看一下如何使用这个Simple_Net_Framework网络框架,这样可以帮我们更好的去入手阅读代码
//建立一个队列
RequestQueue requestQueue = SimpleNet.newRequestQueue();
//我们的网络请求
StringRequest request = new StringRequest(Request.HttpMethod.GET,
"https://www.baidu.com/", new Request.RequestListener<String>() {
@Override
public void onComplete(int stCode, String response, String errMsg) {
Log.e(TAG, "stCode: "+stCode );
Log.e(TAG, "response: "+response );
Log.e(TAG, "errMsg: "+errMsg );
}
});
//加入队列就ok了,因为队列有钩子函数
requestQueue.addRequest(request);
这里不逐行来分析代码了,根据上面的使用方法来进行分析
3.1 RequestQueue requestQueue = SimpleNet.newRequestQueue()
SimpleNet.java
public final class SimpleNet {
/**
* 创建一个请求队列,NetworkExecutor数量为默认的数量
*/
public static RequestQueue newRequestQueue() {
return newRequestQueue(RequestQueue.DEFAULT_CORE_NUMS);
}
/**
* 创建一个请求队列,NetworkExecutor数量为coreNums
*/
public static RequestQueue newRequestQueue(int coreNums) {
return newRequestQueue(coreNums, null);
}
/**
* 创建一个请求队列,NetworkExecutor数量为coreNums
* @param coreNums 线程数量
* @param httpStack 网络执行者
*/
public static RequestQueue newRequestQueue(int coreNums, HttpStack httpStack) {
RequestQueue queue = new RequestQueue(Math.max(0, coreNums), httpStack);
queue.start();
return queue;
}
}
RequestQueue.java
public final class RequestQueue {
/**
* 请求队列 [ Thread-safe ]
*/
private BlockingQueue<Request<?>> mRequestQueue = new PriorityBlockingQueue<Request<?>>();
/**
* 请求的序列化生成器
*/
private AtomicInteger mSerialNumGenerator = new AtomicInteger(0);
/**
* 默认的核心数
*/
public static int DEFAULT_CORE_NUMS = Runtime.getRuntime().availableProcessors() + 1;
/**
* CPU核心数 + 1个分发线程数
*/
private int mDispatcherNums = DEFAULT_CORE_NUMS;
/**
* NetworkExecutor,执行网络请求的线程
*/
private NetworkExecutor[] mDispatchers = null; //一个Thread,进行一次网络操作
/**
* Http请求的真正执行者
*/
private HttpStack mHttpStack;
/**
* @param coreNums 线程核心数
* @param httpStack http执行器
*/
protected RequestQueue(int coreNums, HttpStack httpStack) {
mDispatcherNums = coreNums; //最终开启这么多线程来执行网络操作
mHttpStack = httpStack != null ? httpStack : HttpStackFactory.createHttpStack(); //网络请求操作的最终执行者,在现在android版本中是对应HttpUrlConnStack ,下面分析
}
/**
* 启动NetworkExecutor
*/
private final void startNetworkExecutors() {
mDispatchers = new NetworkExecutor[mDispatcherNums]; //开几个线程
for (int i = 0; i < mDispatcherNums; i++) {
mDispatchers[i] = new NetworkExecutor(mRequestQueue, mHttpStack); //一个队列,一个HttpStack执行者,多个线程去执行
mDispatchers[i].start();
}
}
public void start() {
stop(); //防止多次start(),如果mDispatcherNums个线程开启了就先全部关闭
startNetworkExecutors(); //开启mDispatcherNums个线程
}
/**
* 停止NetworkExecutor
*/
public void stop() {
if (mDispatchers != null && mDispatchers.length > 0) {
for (int i = 0; i < mDispatchers.length; i++) {
mDispatchers[i].quit();
}
}
}
/**
* 不能重复添加请求
* 很重要的方法,request加入后RequestQueue后就会被开启的多个线程获取request进而执行,
* 所以在使用中只要调用addRequest就完成了网络操作
* @param request
*/
public void addRequest(Request<?> request) {
if (!mRequestQueue.contains(request)) {
request.setSerialNumber(this.generateSerialNumber());
mRequestQueue.add(request);
} else {
Log.d("", "### 请求队列中已经含有");
}
}
public void clear() {
mRequestQueue.clear();
}
public BlockingQueue<Request<?>> getAllRequests() {
return mRequestQueue;
}
/**
* 为每个请求生成一个系列号
*
* @return 序列号
*/
private int generateSerialNumber() {
return mSerialNumGenerator.incrementAndGet();
}
}
RequestQueue是一个重要的核心类,这里总结一下,有一个阻塞队列来存储Request,每次网络请求只需要把new好的Request加入到阻塞队列就好了;还有一个网络请求的真正执行者HttpStack根据版本的不同是HttpClient或HttlUrlConnection(就是这个);多个死循环的线程(线程的数量在new RequestQueue可以控制),每个线程从阻塞队列获取Request然后交个HttpStack来执行网络请求,下面来看这些多线程的代码
NetworkExecutor.java
final class NetworkExecutor extends Thread { //就是一个线程
/**
* 网络请求队列
*/
private BlockingQueue<Request<?>> mRequestQueue;
/**
* 网络请求栈,执行者
*/
private HttpStack mHttpStack;
/**
* 结果分发器,将网络请求结果投递到主线程,后面分析
*/
private static ResponseDelivery mResponseDelivery = new ResponseDelivery();
/**
* 请求缓存,这部分就是使用LruMemCache不做解释
*/
private static Cache<String, Response> mReqCache = new LruMemCache();
/**
* 是否停止
*/
private boolean isStop = false;
//每个线程拥有RequestQueue中的阻塞队列和网络执行者
public NetworkExecutor(BlockingQueue<Request<?>> queue, HttpStack httpStack) {
mRequestQueue = queue;
mHttpStack = httpStack;
}
@Override
public void run() {
try {
while (!isStop) { //死循环
final Request<?> request = mRequestQueue.take(); //没有请求会阻塞
if (request.isCanceled()) {
Log.d("### ", "### 取消执行了");
continue;
}
Response response = null;
if (isUseCache(request)) { //request是否采用缓存
// 从缓存中取
response = mReqCache.get(request.getUrl());
} else {
// 从网络上获取数据
response = mHttpStack.performRequest(request); //执行网络操作
// 如果该请求需要缓存,那么请求成功则缓存到mResponseCache中
if (request.shouldCache() && isSuccess(response)) {
mReqCache.put(request.getUrl(), response);
}
}
// 分发请求结果
mResponseDelivery.deliveryResponse(request, response);
}
} catch (InterruptedException e) {
Log.i("", "### 请求分发器退出");
}
}
private boolean isSuccess(Response response) {
return response != null && response.getStatusCode() == 200;
}
private boolean isUseCache(Request<?> request) {
return request.shouldCache() && mReqCache.get(request.getUrl()) != null;
}
public void quit() {
isStop = true;
interrupt();
}
}
对缓存有兴趣可以参考http://blog.csdn.net/chenqianleo/article/details/75137994 ,使用了LruCache,DiskLruCache两级缓存
这部分代码也很简单,在Thread的run()方法获取一个request,使用HttpStack来进行操作然后处理返回结果,这里两个问题,一个是HttpStack是如何处理的?二是如何处理网络请求结果?第一个就是HttpUrlConnect的基本操作,我们下面先来看第二个
ResponseDelivery.java
class ResponseDelivery implements Executor {
/**
* 主线程的hander
*/
Handler mResponseHandler = new Handler(Looper.getMainLooper());
/**
* 处理请求结果,将其执行在UI线程
* @param request
* @param response
*/
public void deliveryResponse(final Request<?> request, final Response response) {
Runnable respRunnable = new Runnable() {
@Override
public void run() {
request.deliveryResponse(response); //调用了request的方法,下面来看这个方法
}
};
execute(respRunnable);
}
@Override
public void execute(Runnable command) {
mResponseHandler.post(command); //这个Runable使用Handler放到了UI线程
}
}
Request.java
public abstract T parseResponse(Response response); //不同的request类型自己处理自己请求的数据
public final void deliveryResponse(Response response) {
T result = parseResponse(response); //返回不同的结果,比如json,string等
if (mRequestListener != null) {
int stCode = response != null ? response.getStatusCode() : -1;
String msg = response != null ? response.getMessage() : "unkown error";
Log.e("", "### 执行回调 : stCode = " + stCode + ", result : " + result + ", err : " + msg);
mRequestListener.onComplete(stCode, result, msg); //回调方法,我们在new一个Request时传入的参数
}
}
阅读到这里大致的流程租完了,我们先创建一个RequestQueue,然后里面会有多个线程等待Request任务的到来,当我们新建一个Request时会传入一个onComplete的钩子函数,最后当我们把Request加入到RequestQueue后就被线程执行得到结果,执行钩子函数。。over