1.HTTP协议了解

http是一种应用层的协议,底层通过TCP来进行可靠的数据传输。HTTP是基于TCP的应用层协议,它在更高的层次封装了TCP的使用细节,使网络请求更加易用,TCP连接是因特网基于流的一种可靠连接,它为HTTP提供了一条可靠的比特传输管道。从TCP连接一端填入的字节会从另一端以原有的顺序、正确的传送过来。

HTTP的7种请求方式

GET

POST

DELETE

PUT

HEAD

TRACE

POTIONS

[图片上传失败...(image-aa15fa-1517135394572)]

HTTP报文格式解析

不同的请求方式,它们的请求格式也是不一样的,请求格式也就是报文格式。 通常来说一个HTTP请求报文由 请求行(request line)、请求头部(head)、空行、请求数据 4个部分组成。

[图片上传失败...(image-fbc600-1517135394572)]

请求行

报文的第一行就是请求行,这一行说明了这段报文以什么方式请求,包含了HTTP的版本号等一些协议信息。

请求头部

请求头部是以 key:value的形式来说明请求数据的,这里面说明了请求服务器的一些host,content-tye,Encoding的一些说明。

请求数据

POST请求的方式才会有请求数据,如果请求是以POST提交过来,那么这里面就会有post的请求数据,可以文本形式或者二进制数据根据你请求post提交的数据类型决定。

GET 请求报文格式

www.jinweime.com?id=2

这个是一个典型的get请求,get请求的参数会跟在url后面。问号后面作为第一个参数,以&进行参数拼接。

http请求协议如下

GET /?id=2 HTTP/1.1

Host: jinweime.com

Cache-Control: no-cache

可以看到第一行为请求行,请求方式为GET,子路径是?id=2 代表参数id的值为2,HTTP版本为1.1。 后面二行是head区域,第一个请求头是主机地址,第三行也是一个head。GET方式的请求参数都是附加的URL中所以请求数据部分为空。

POST请求报文格式

POST /api/feed. HTTP/1.1

Accept-Encoding: gzip

Content-Length: 225873

Content-Ttpe: multipart/form-data; boundary=Ocxx3329f....

Host: www.myhost.com

Connection: Keep-Alive

--OCxxqFJE...

Content-Dispotition: form-data; name=="username"

Content-Type: text/plain; charset=UTF-8

Content-Transfer-Encoding: 8bit

MrSimple

--Cxxii32F..

Content-Dispotition: form-data; name=="title"

Content-Type: text/plain; charset=UTF-8

Content-Transfer-Encoding: 8bit

TEST

--Ffsaxx......--

这串报代表向 www.myhost.com/api/feed/这个地址发送了一个POST请求。 请求的数据格式为 Content-Type: multipart.form-data,报文有二个参数 username title,username的值为MrSimple。title的值为TEST。 最后一行是结束行以 -- boundary值 -- 结束, 如果格式不正确服务器将会解析不到的你请求。

响应报文

HTTP响应报文也是由三个部分组成,分别是:状态行 消息head 响应报文,和请求报文的格式十分相似。

可以看到和请求报文相比,只是把第一行的请求行换成了状态行了,状态行提供一个状态码来说明此次请求的资源情况。

HTTP-Version Status-Code Reason-Phrase CRLF

其中的HTTP-Version表示服务器HTTP协议的版本,Status-Code表示服务器响应请求的状态码;Reason-Phrase表示状态码的文本描述。状态码是一个三位的数字,第一个数字定义了响应的类别。

常见的状态码

200 OK;客户端请求成功

400 Bad Request:客户端请求有语法错误,服务器不能正确的解析

401 Unauthorized;请求未授权

403 Forbidden; 服务器收到请求,但是拒绝提供服务

404 Not Found; 请求的资源不存在, 比如输入了错误的地址;

500 Internal Server Error; 服务器发生了错误

503 Server Unavailable; 服务器当前不能处理客户端请求

常见的请求头部

Content-Type: 请求数据的格式

Content-Length; 消息的长度

Host: 请求主机名

User-Agent; 发出请求的浏览器类型,可以自定义

Accept: 客户端可识别的内容类型

Accept-Encoding: 客户端可识别的数据编码

Connection: 允许客户端和服务器指定请求/响应连接有关的选项,比如设置Keep-Alive 表示保持连接

Android中执行网络请求

android中提供了二种执行网络请求的方式,一种使用 Apache的HttpClient,另一种Java提供的 HttpUrlConnection。这二种方法都提供了完整的 API, 都很很好的实现对网络的请求功能,但是某些情况下我们需要做取舍分清楚二种方式的区别。

HttpClient

android SDK自带了 Apache的HttpClient,它提供了对HTTP协议的全面支持,可以使用HttpClient来执行 HTTP GET和HTTP POST请求。

HttpUrlConnection

最佳选择HttpUrlConnection。二者对比来说,在android 2.2版本之前,HttpClient有较少的一些BUG,而HttpURLConnection一直存在一些让厌烦的BUG,比如在对一个可读的InputStream 调用colse()方法时,就有可能导致连接池失败。因此在 android 2.2版本之前使用 HttpClient是比较好的选择。 但是在 android2.3及之后 HttpUrlConnect有了进一步的更新, 它api 简单,体积小,因此非常适用于 Android项目中。 HttpUrlConnection的压缩和缓存机制可以有效的减少网络访问的流量,这块在提升手机省电和流量方面也起来很多的作用。另外在Android 6.0中,HttpClient以及被移除了,所以以后开发中HttpUrlConnection是我们唯一的选择了。

使用HttpUrlConnection请求

fun sendHttpClient(url: String) {

val url = URL(url)

var conn = url.openConnection() as HttpURLConnection

//读取时时间为 2s

conn.readTimeout = 2000

//请求超时时间为 5s

conn.connectTimeout = 5000

//设置请求方式

conn.requestMethod = "POST"

//接收输入流

conn.doInput = true

//启动输出流,需要传递参数时需要开启

conn.doInput = true

//添加 Header

conn.setRequestProperty("Connection", "Keep-Alive")

//添加请求参数

var paramsList = ArrayList()

paramsList.add(BasicNameValuePair("username", "jinwei"))

paramsList.add(BasicNameValuePair("pwd", "pwd.com"))

writeParams(conn.outputStream, paramsList)

//发起请求

conn.connect()

var input = conn.inputStream

//获取结果

var str = convertStreamToString(input)

Log.i(TAG, "Request Data: " + str)

input.close()

}

fun writeParams(outpit: OutputStream, paramsList: List) {

val paramStr = StringBuffer()

for (value in paramsList) {

if (!TextUtils.isEmpty(paramStr)) {

paramStr.append("&")

}

paramStr.append(URLEncoder.encode(value.name, "UTF-8"))

paramStr.append("=")

paramStr.append(URLEncoder.encode(value.value, "UTF-8"))

}

var writer = BufferedWriter(OutputStreamWriter(outpit, "UTF-8"))

//写入参数写入输入流

Log.i(TAG, paramStr.toString())

writer.write(paramStr.toString())

writer.flush()

writer.close()

}

fun convertStreamToString(input: InputStream): String {

var buffer = BufferedReader(InputStreamReader(input))

var sb = StringBuffer()

var line: String

try {

while (true) {

line = buffer.readLine()

if (!TextUtils.isEmpty(line)) {

sb.append(line + "\n")

} else {

break

}

}

} catch (e: IOException) {

e.printStackTrace()

}

return sb.toString()

}

2. Volley使用

安静

volley的架构图

架构图

volley是2013年 google I/O大会上推出的一款网络请求相关的框架

它有以下好处

网络请求的自动调度。

多个并发网络连接。

具有标准HTTP缓存一致性的透明磁盘和内存响应缓存。

支持请求优先级。

取消请求api。可以取消单个请求,也可以设置要取消的请求的块或范围。

自定义重试

缺点

不适合数据量过大的传输操作

构建一个stringRequest

一般来说我们一个应用启动后只需要全局获取一个

Volley.newRequestQueue(this)实例就够了,这样可以有效的节省系统资源的消耗。

fun sendStringRequest() {

var request = Volley.newRequestQueue(this)

var url = "http://baidu.com"

var strrequest = StringRequest(Request.Method.GET, url,

object : Response.Listener {

override fun onResponse(response: String?) {

Log.i(TAG, response)

}

},

object : Response.ErrorListener {

override fun onErrorResponse(error: VolleyError?) {

Log.i(TAG, "error")

}

})

request.add(strrequest)

}

很简单成功的打印了返回的Html数据

JsonRequest

这里我们用的JsonRequest的子类JsonObjectRequest来构建了一个请求,它支持JSON格式的Request和Response。

fun sendJsonRequest() {

var request = Volley.newRequestQueue(this)

var url = "http://baike.baidu.com/api/openapi/BaikeLemmaCardApi?scope=103&format=json&appid=379020&bk_key=关键字&bk_length=600"

var strrequest = JsonObjectRequest(Request.Method.POST, url, null,

object : Response.Listener {

override fun onResponse(response: JSONObject?) {

Log.i(TAG, response.toString())

}

},

object : Response.ErrorListener {

override fun onErrorResponse(error: VolleyError?) {

Log.i(TAG, "error")

}

})

request.add(strrequest)

}

成功的返回了一串JSON格式的数据

{"id":390935,"subLemmaId":390935,"newLemmaId":7105697,"key".......

ImageRequest

Volley还支持对图片的获取

fun sendImageRequest() {

var request = Volley.newRequestQueue(this)

var url = "https://img-my.csdn.net/uploads/201603/26/1458988468_5804.jpg"

var strrequest = ImageRequest(url,

object : Response.Listener {

override fun onResponse(response: Bitmap?) {

findViewById(R.id.imageView).setImageBitmap(response)

}

}, 511, 511, Bitmap.Config.ARGB_8888,

object : Response.ErrorListener {

override fun onErrorResponse(error: VolleyError?) {

Log.i(TAG, "error")

}

})

request.add(strrequest)

}

这段代码成功的Bitmap显示到了ImageView上面

3. Volley源码分析

执行

var request = Volley.newRequestQueue(this)

最终会到newRequestQueue方法

public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {

BasicNetwork network;

if (stack == null) {

if (Build.VERSION.SDK_INT >= 9) {

network = new BasicNetwork(new HurlStack());

} else {

// Prior to Gingerbread, HttpUrlConnection was unreliable.

// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html

// At some point in the future we'll move our minSdkVersion past Froyo and can

// delete this fallback (along with all Apache HTTP code).

String userAgent = "volley/0";

try {

String packageName = context.getPackageName();

PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);

userAgent = packageName + "/" + info.versionCode;

} catch (NameNotFoundException e) {

}

network = new BasicNetwork(

new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));

}

} else {

network = new BasicNetwork(stack);

}

return newRequestQueue(context, network);

}

可以看到代码进入到Build.VERSION.SDK_INT >= 9的逻辑,network = new BasicNetwork(new HurlStack());创建了一个network对象,我们重点关系HrlStack()这个对象,这个对象是最终执行网络请求的地方。

@Override

public HttpResponse executeRequest(Request> request, Map additionalHeaders)

throws IOException, AuthFailureError {

String url = request.getUrl();

// Log.i("jinwei"," ## executeRequest = "+url);

HashMap map = new HashMap<>();

map.putAll(request.getHeaders());

map.putAll(additionalHeaders);

if (mUrlRewriter != null) {

String rewritten = mUrlRewriter.rewriteUrl(url);

if (rewritten == null) {

throw new IOException("URL blocked by rewriter: " + url);

}

url = rewritten;

}

URL parsedUrl = new URL(url);

HttpURLConnection connection = openConnection(parsedUrl, request);

for (String headerName : map.keySet()) {

connection.addRequestProperty(headerName, map.get(headerName));

}

setConnectionParametersForRequest(connection, request);

// Initialize HttpResponse with data from the HttpURLConnection.

int responseCode = connection.getResponseCode();

if (responseCode == -1) {

// -1 is returned by getResponseCode() if the response code could not be retrieved.

// Signal to the caller that something was wrong with the connection.

throw new IOException("Could not retrieve response code from HttpUrlConnection.");

}

if (!hasResponseBody(request.getMethod(), responseCode)) {

return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));

}

return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()),

connection.getContentLength(), inputStreamFromConnection(connection));

}

通过代码可以看到执行网络请求使用的HttpURLConnection处理的,具体的调用过程我们继续分析。

newRequestQueue方法最好执行到了return newRequestQueue(context, network);

private static RequestQueue newRequestQueue(Context context, Network network) {

File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);

queue.start();

return queue;

}

在这里执行了queue.start()方法

public void start() {

stop(); // Make sure any currently running dispatchers are stopped.

// Create the cache dispatcher and start it.

mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);

mCacheDispatcher.start();

// Create network dispatchers (and corresponding threads) up to the pool size.

Log.i("jinwei"," ## mDispatchers.length ## " +mDispatchers.length);

for (int i = 0; i < mDispatchers.length; i++) {

NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,

mCache, mDelivery);

mDispatchers[i] = networkDispatcher;

networkDispatcher.start();

}

}

这里总共开启了5个 Thread,一个CacheThread和四个NetWorkThread。

NetWorkThread的run方法

@Override

public void run() {

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

while (true) {

try {

Log.i("jinwei"," ## run ##");

processRequest();

} catch (InterruptedException e) {

// We may have been interrupted because it was time to quit.

Log.i("jinwei"," ## mQuit ##");

if (mQuit) {

return;

}

}

}

}

这里是一个while循环,内部有一个PriorityBlockingQueue队列take数据,如果返回为Null线程就会挂起等待新的队列进来。

重点方法

private void processRequest() throws InterruptedException {

long startTimeMs = SystemClock.elapsedRealtime();

// Take a request from the queue.

Request> request = mQueue.take();

try {

request.addMarker("network-queue-take");

// If the request was cancelled already, do not perform the

// network request.

if (request.isCanceled()) {

request.finish("network-discard-cancelled");

request.notifyListenerResponseNotUsable();

return;

}

addTrafficStatsTag(request);

// Perform the network request.

NetworkResponse networkResponse = mNetwork.performRequest(request);

request.addMarker("network-http-complete");

// If the server returned 304 AND we delivered a response already,

// we're done -- don't deliver a second identical response.

if (networkResponse.notModified && request.hasHadResponseDelivered()) {

request.finish("not-modified");

request.notifyListenerResponseNotUsable();

return;

}

// Parse the response here on the worker thread.

Response> response = request.parseNetworkResponse(networkResponse);

request.addMarker("network-parse-complete");

// Write to cache if applicable.

// TODO: Only update cache metadata instead of entire record for 304s.

if (request.shouldCache() && response.cacheEntry != null) {

mCache.put(request.getCacheKey(), response.cacheEntry);

request.addMarker("network-cache-written");

}

// Post the response back.

request.markDelivered();

mDelivery.postResponse(request, response);

request.notifyListenerResponseReceived(response);

} catch (VolleyError volleyError) {

volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);

parseAndDeliverNetworkError(request, volleyError);

request.notifyListenerResponseNotUsable();

} catch (Exception e) {

VolleyLog.e(e, "Unhandled exception %s", e.toString());

VolleyError volleyError = new VolleyError(e);

volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);

mDelivery.postError(request, volleyError);

request.notifyListenerResponseNotUsable();

}

}

Request> request = mQueue.take()取出之前request.add(strrequest)的数据,如果没有则会一直挂起。

执行这里就会执行到之前HurlStack类中的executeRequest方法来通过HttpUrlConnection来构建一个网络请求了

NetworkResponse networkResponse = mNetwork.performRequest(request)

源码的简单分析就到这了,具体的设计思路和实现还需要深入研究了。

http请求过程 Android,android HTTP网络请求回顾相关推荐

  1. Android新的网络请求框架volley源码解释及示例

    最近遇到一个问题:我想用HttpClient来访问网络,发现怎么都无法new出HttpClient的对象,这我就有点摸不着头脑了.记得我之前都是可以使用这个类的,怎么突然间就用不了了.因为不知情,一下 ...

  2. 一个整合OkHttp 、Retrofit 、Volley 、RxJava、Novate多种开源网络框架的项目,高度的封装和集成,Android中Web网络请求一行代码解决

    一个整合OkHttp .Retrofit .Volley .RxJava.Novate多种开源网络框架的项目,高度的封装和集成,Android中Web网络请求一行代码解决 AndroidHttp 一个 ...

  3. Python网络爬虫过程中,构建网络请求的时候,参数`stream=True`的使用

    点击上方"Python共享之家",进行关注 回复"资源"即可获赠Python学习资料 今 日 鸡 汤 海内存知己,天涯若比邻. 大家好,我是皮皮. 一.前言 前 ...

  4. 计算机网络中请求超时是什么意思,网络请求超时怎么解决

    我们知道不少朋友在上网的时候,会遇到网络请求超时的情况,那造成网络请求超时的原因是什么呢?网络请求超时就是在程序默认的等待时间内没有得到服务器的响应.跟着小编一起来看看请求超时解决方法. 网络请求超时 ...

  5. Android之解剖网络请求框架Volley

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! Volley介绍 Volley是Google推出的网络请求库,包含的特性有JSON ...

  6. Android应用中网络请求库Volley的使用

    接上文,这次来说一下如何使用Volley,会给出一些范例,和原理 Volley使用 StringRequest // 初始化一个请求队列,RequestQueue是volley库的类 RequestQ ...

  7. Android之三种网络请求解析数据(最佳案例)

    小武:相信大家都用过网络请求解析数据,只是方法不一样而已,但是,逻辑都是差不多的: 一:AsyncTask解析数据 AsyncTask主要用来更新UI线程,比较耗时的操作可以在AsyncTask中使用 ...

  8. Android中Http网络请求库框架Volley和Asnyc-http的使用---第三方库学习笔记(一)

    Volley框架: 特点: 通信更快,更简单 Get,Post网络请求及网络图像的高效率异步处理请求. 当多个请求时,对网络请求进行排序,按优先级处理 当网络状况不好时,Volley可以自动对上次请求 ...

  9. android手机对网络请求抓包

    抓包工具 Fiddler Everywhere | Debugging Proxy for Mac, Linux, Windows Fiddler下载注册完毕后打开如下,它会自动抓取当前电脑上的所有网 ...

  10. Android高版本网络请求失败 Cleartext HTTP traffic to xxx not permitted

    我可以失败,但是不可以有遗憾! ----泰语心说 今天升级build gradle的targetversion,发现升级完后,网络请求一直失败,而且用Charles抓包看,发现连网络请求都没有,后来在 ...

最新文章

  1. Android中对Handler用法的总结
  2. python 类的封装、继承、重写方法
  3. 又跌了!7 月程序员工资平均 14357 元 | 原力计划
  4. python 制作gif-怎样用Python制作好玩的GIF动图
  5. javascript:闭包的总结
  6. - (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds
  7. python的gc模块_Python的内存泄漏及gc模块的使用分析
  8. Change Eclipse Tooltip's Color in Ubuntu
  9. Compass.net
  10. Spark mapPartition方法与map方法的区别
  11. 定制你自己的CRF模型以及JAVA实现的Word2Vec模型和一些java版NLP的工具
  12. 第一个Maven工程的目录结构和文件内容及联网问题
  13. 模拟无线透传的STM32的24L01无线模块与串口USART通信
  14. rrd文件导入mysql_RRDtool 系列连载-5 :查询 RRD 数据库信息
  15. 基于tensorflow的Word2Vec实现
  16. 【机器学习】机器学习30个笔试题
  17. 【顺序表】顺序表定位
  18. 关于win10基础上安装ubuntu遇到的坑
  19. Word学习笔记:P5-标尺和定位点要如何使用
  20. d3.js 旋转图形_一个简单易用但功能强大的图形矢量化软件,扫描图片转换成CAD图的软件等等...

热门文章

  1. systemctl 命令完全指南
  2. Qt Marketplace
  3. 【小米校招笔试】给定一些线段,线段有起点和终点,求这些线段的覆盖长度,重复的部分只计算一次
  4. 【已解决】Android 如何让应用在后台运行
  5. 强化学习6——Value-based RL和Policy-based RL 的区别
  6. 决策树准确率低原因_智能质检优化实践:召回率和准确率,哪个更重要?
  7. cadshx字体怎么安装_福利 | 关于PPT字体,你应该知道的几件事...字体包福利见文末...
  8. excel中match函数_Excel函数轻松学02:详解Excel函数中的数据类型
  9. Linux进程间通信——使用共享内存
  10. IPC\DVS\DVR与NVR之间的区别