首先volley本身不支持cookie,但是volley又非常好用(比如封裝了網絡請求的實現,內部支持并發, 不用我們再額外設計網絡管理異步處理,網絡請求不應在UI線程等等),那既想使用volley又想在對服務器發起http請求時加上cookie,并從服 務器給的響應中讀取cookie。怎么辦呢?慶幸的是volley是開源的,我們可以重寫一些方法來實現我們的目標。
我們平時開發android應用都需要用到網絡技術,通常采用http協議來發起請求并接受網絡數據。android系統提供兩種方式進行 http通信:HttpURLConnection和HttpClient。不過這兩種方式稍復雜,如果不適當封裝回到子漢許多重復代碼。因此 android網絡通信框架應運而生,如AsynHttpClient(把Http所有的通信細節全封裝在內,只需幾行代碼就可以完成通 信),Universal_Image_loader(使界面上顯示網絡圖片的操作變得極其簡單,開發者不用關心如何從網絡上獲取圖片,也不用關心開啟線 程,回收圖片資源等細節,它已把一切都做好)。Google I/O大會上退出了新的網絡通信架構volley,volley集HttpClient和HttpURLConnection優點于一身,Volley非常適合數據量不大,通信頻繁的網絡操作,但對于大數據量的網絡操作比如下載文件,Volley表現糟糕。
什么是Cookie?
我們知道http是無連接的,不像tcp那樣始終占有一個通道。為了破除http的這個局限,所以有了cookie和session。分別對應客戶端和服務器端,以實現保持會話連接狀態。常見的應用有購物車,用戶自動登錄。
以登錄為例,客戶端將含有用戶填寫的賬號密碼的表單post給服務器端,服務器判斷其登錄成功,則返回一個response,其中reponse的 header中會包含”Cookie”字段。也就是說response的header是一長串字符串,客戶端需要從中提取類似于 “set-cookie: …… ; ”的一段子串,并將它保存在本地,比如保存在String localCookie變量中,后續發送給服務器的所有請求中都需要將該鍵值對put在http請求的header中,注意key是固定的 “Cookie”, value就是之前保存的那個localCookie變量的值。
response的header示例圖:
可以用正則表達式提取Set-Cookie:mBxa_……%09jax;子串。
我聽過一個很有意思的比喻,http請求的頭就像一輛公交車,每個座位就是Key,value對號入座。比如我們要放cookie字段在頭里,就必 需采用”Cookie”這個key,否則自己命名一個比如”mycookie“,那就只能坐地上了,這樣服務器是就識別不了你這段話是干什么用的了。
直接貼代碼
1)從服務器的response中獲得cookie串。首先是自定義一個JsonObjectPostRequest。
import android
.util.Logimport
com.android.volley.AuthFailureError
import
com.android.volley.NetworkResponse
import
com.android.volley.ParseError
import
com.android.volley.Request
import
com.android.volley.Response
import
com.android.volley.toolbox.HttpHeaderParserimport org
.apache.http.cookie.Cookie
import org
.json.JSONException
import org
.json.JSONObjectimport java
.io.UnsupportedEncodingException
import java
.util.HashMap
import java
.util.Map
import java
.util.regex.Matcher
import java
.util.regex.Patternimport info
.doufm.android.utils.ShareUtilpublic class JsonObjectPostRequest extends Request<JSONObject> {private Map<String, String> mMapprivate Response
.Listener<JSONObject> mListenerpublic String cookieFromResponseprivate String mHeaderprivate Map<String, String> sendHeader=new HashMap<String, String>(
1)public JsonObjectPostRequest(String url, Response
.Listener<JSONObject> listener, Response
.ErrorListener errorListener, Map map) {super(Request
.Method.POST, url, errorListener)mListener = listenermMap = map}//當http請求是post時,則需要該使用該函數設置往里面添加的鍵值對@Overrideprotected Map<String, String> getParams() throws AuthFailureError {return mMap}@Overrideprotected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {try {String jsonString =new String(response
.data, HttpHeaderParser
.parseCharset(response
.headers))mHeader = response
.headers.toString()Log
.w(
"LOG",
"get headers in parseNetworkResponse "+response
.headers.toString())//使用正則表達式從reponse的頭中提取cookie內容的子串Pattern pattern=Pattern
.compile(
"Set-Cookie.*?;")Matcher m=pattern
.matcher(mHeader)if(m
.find()){cookieFromResponse =m
.group()Log
.w(
"LOG",
"cookie from server "+ cookieFromResponse)}//去掉cookie末尾的分號cookieFromResponse = cookieFromResponse
.substring(
11,cookieFromResponse
.length()-
1)Log
.w(
"LOG",
"cookie substring "+ cookieFromResponse)//將cookie字符串添加到jsonObject中,該jsonObject會被deliverResponse遞交,調用請求時則能在onResponse中得到JSONObject jsonObject = new JSONObject(jsonString)jsonObject
.put(
"Cookie",cookieFromResponse)Log
.w(
"LOG",
"jsonObject "+ jsonObject
.toString())return Response
.success(jsonObject,HttpHeaderParser
.parseCacheHeaders(response))} catch (UnsupportedEncodingException e) {return Response
.error(new ParseError(e))} catch (JSONException je) {return Response
.error(new ParseError(je))}}@Overrideprotected void deliverResponse(JSONObject response) {mListener
.onResponse(response)}@Overridepublic Map<String, String> getHeaders() throws AuthFailureError {return sendHeader}public void setSendCookie(String cookie){sendHeader
.put(
"Cookie",cookie)}
}
解釋:由于所有的volley請求都是一級或多級繼承(實現)自Requst 抽象類,因此我們需要對volley Rqeust源碼有一定的了解。因為發起請求,支持并發等都在volley的內部邏輯中實現了。我們想要做的事就需要重寫這些提供給我們的方法了。當從網 絡中獲取response的時候,怎么去解析對應的請求?這是由各個對應的Request去解決的。比如我們上面的自定義JsonObjectPostRequest, 最后都要通過Response.success方法去返回一個Response對象,并且這個Response對象怎么使用則由 deliverResponse方法決定。也就是說當我們調用一般的volley請求時(比如JsonRequest),呈現給我們可以使用的服務器響應 就只有Response.Listener()中的onResposne(JsonObject response)方法中傳入的參數response了,而這個response參數往往包含的是服務器返回的原始response經過 JsonRequest定義類中的一系列加工之后的response,比如這里便只是服務器響應的 JsonObejct 對象了,是不含頭的,那怎么辦呢?怎么才能讓我在調用的時候拿到服務器返回的頭中的信息?方法是有的,我們需要在可以拿到原始response的地方,也 就是parseNetworkResponse中做一些事:進一步拿到response的頭(頭是一個很長的字符串),我們需要從中找到cookie子串 (可以采用正則表達式實現)。
那 么問題來了,我們在Acticity中使用JsonObjectRequst的時候,怎么拿到這個辛辛苦苦拿到的cookie呢?分析volley的源碼 之后我發現:在創建JsonObjectRequest對象時,我們最終拿到的關于reponse的所有操作都是在 onResponse(JSONObject jsonObject){……}中進行的。那么解決方案就有了,在parseNetworkResponse中我自己新 建一個cookie鍵值對,key隨意寫,你認識就行,值就是辛苦拿到的那個cookie值,再將該鍵值對也put到JsonObject里。那么這個 JsonObject還是走原來的通道交給想使用它的地方,即通過parseNetworkResponse中的Response.success()將 JsonObject 交給 deliverResponse()方法,最終在調用時就可以被Response.Listener()中的 onReposne( JsonObeject response) 拿到啦。
在Activity中發起請求時,可以獲取服務器返回的cookie,保存到本地,還可以在發送時將cookie附加到請求的頭中,代碼如下所示:
String userName = etUserName
.getText()
.toString()
.trim()
String userPassword = etUserPassword
.getText()
.toString()
.trim()
originPassword = userPassword
mUserName = userName
//生成MD5
userPassword = UserUtil
.toLowerCaseMD5(userPassword)
//轉成成UTF-
8
try {userName = URLEncoder
.encode(userName,
"UTF-8")userPassword = URLEncoder
.encode(userPassword,
"UTF-8")
} catch (UnsupportedEncodingException e) {e
.printStackTrace()
}
HashMap<String, String> mMap = new HashMap<String, String>()
mMap
.put(
"user_name", userName)
mMap
.put(
"password", userPassword)//發起請求
JsonObjectPostRequest jsonObjectPostRequest = new JsonObjectPostRequest(Constants
.LOGIN_URL, new Response
.Listener<JSONObject>() {@Overridepublic void onResponse(JSONObject jsonObject) {//從服務器響應response中的jsonObject中取出cookie的值,存到本地sharePreferencetry {shareUtil
.setLocalCookie(jsonObject
.getString(
"Cookie"))shareUtil
.apply()} catch (JSONException e) {e
.printStackTrace()} try {if (jsonObject
.get(
"status")
.equals(
"success")) {//登錄成功 }} catch (JSONException e) {e
.printStackTrace()}}}, new Response
.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError volleyError) { Toast
.makeText(LoginActivity
.this,
"網絡錯誤,登錄失敗!", Toast
.LENGTH_SHORT)
.show()}}, mMap)String localCookieStr = shareUtil
.getLocalCookie()if(!localCookieStr
.equals(
"")){jsonObjectPostRequest
.setSendCookie(localCookieStr)}RequestManager
.getRequestQueue()
.add(jsonObjectPostRequest)}
轉載于:https://www.cnblogs.com/lanzhi/p/6468488.html
總結
以上是生活随笔為你收集整理的volley框架下发送和读取cookie的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。