日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

http请求过程 Android,android HTTP网络请求回顾

發布時間:2024/10/8 Android 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 http请求过程 Android,android HTTP网络请求回顾 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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网络请求回顾的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。