Android网络框架
来源:互联网 发布:淘宝店铺企业认证 编辑:程序博客网 时间:2024/06/07 07:26
从代码学习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
- Android流行框架+网络框架
- Android 移动网络框架
- Android网络框架Volley
- Android网络框架Volley
- Android网络框架Volley
- Android网络开发框架
- android 网络框架 volley
- android Volley网络框架
- Android网络框架Volley
- Android-网络框架
- Android网络框架Volley
- Android网络通信框架
- Android 网络框架
- android网络框架
- Android网络框架笔记
- Android网络框架Volley
- Android网络框架Volley
- android网络框架
- InnoDB的行锁
- spring boot No Identifier specified for entity的解决办法
- 【百度、腾讯、阿里等】+【JAVA开发实习生】+春招面试经验
- C与指针 7-9章
- Java将字符串转换为时间,支持多种格式
- Android网络框架
- 简述ByteBuffer的使用
- Centos7下将FTP用户锁定在指定目录的解决办法
- struts2配置文件的加载顺序(三)
- 继承与多态
- 读书笔记《C++ Primer》第五版——第十一章 关联容器
- vmware安装centos7 输入ifconfig出现ens33,没有eth0
- kotlin的三目运算
- jQuery学习之二---jq核心