先给大家推荐一个开源项目:点这里
这是一款基于网易云音乐UI,使用Gank.Io及豆瓣api开发的符合Google Material Design的Android开源项目。项目采取的是MVVM-DataBinding架构开发,现主要包括:干货区、电影区和书籍区三个子模块。
我也是看了这个开源项目决定新项目框架使用MVVM DataBinding 和Retrofit的。
还是感谢这些无私奉献的人们。
关于Retrofit和binding的知识也出现非常多的资料了,我就不献丑了。
下面说下我遇到的问题。
新项目的网络请求,小白我用了仰慕已久的Retrofit。
项目中使用的jar是上面开源框架中使用的:
compile 'com.jakewharton.retrofit:retrofit1-okhttp3-client:1.1.0'
前几天需要在网络请求的头中塞入登录成功时返回的码,用来后台验证身份。验证不通过会返回401.
这个需求并不陌生,以前项目中几乎每个都会有。
于是我去查了一下,大概思路明确了,在初始化okhttpclient builder对象时,和restAdapter对象的时候插入请求拦截器。
注:下面贴出的一种方式是我最开始尝试的方式,可以实现,但是麻烦,所以大家可以作为思路的参考,在后面贴出后来改进之后的简便方式。
下面是在OkhttpClient.Builder 对象初始化时插入拦截器,是在网络请求返回response时拦截头部取出cookies并自行保存。
okBuilder.addInterceptor(new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Response originalResponse = chain.proceed(chain.request());
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
HashSet cookies = new HashSet();
for (String header : originalResponse.headers("Set-Cookie")) {
cookies.add(header);
Log.v("OkHttp", "save Header: " + header);
}
// 保存上面的cookies
}
return originalResponse;
}
});
在下面就是拦截请求request,自行将上面保存的cookies放入request的header中。这步操作我放在了RestAdapter.Builder对象初始化中(其实也可以放入上面的位置,在add一个interceptor)。
builder.setRequestInterceptor(new RequestInterceptor() {
@Override
public void intercept(RequestFacade request) {
HashSet preferences = // 取出你保存的cookies
for (String cookie : preferences) {
request.addHeader("Cookie", cookie);
Log.v("OkHttp", "Adding Header: " + cookie);
}
}
});
检查了N遍,登录成功返回的cookies和请求时塞入head的cookies。没毛病啊。
这个问题从早上九点坐着对着电脑到晚上九点。提出了无数猜想。
直接说最后一个猜想:上面代码中保存的cookies是用HashSet。我猜是因为它无序导致的。同事们都不信是这个问题。但是我没招了还是试试吧。
于是我将HashSet换成了ArrayList。
成了。真的是这个问题。(感觉这辈子不想再用HashSet了)
上面就是我在项目中,找到的解决办法,但是总感觉怪怪的,后来我又查资料进行尝试,找到了比较方便的方式。
注:下面的两个类是用kotlin的语言编写的,(与java兼容,不知道的同学该学一学了)。
下面先贴出,拦截请求头的cookies,直接存到缓存中:
package com.reliable.mihophysical.api
import android.content.Context
import android.text.TextUtils
import java.io.IOException
import java.util.HashSet
import okhttp3.Interceptor
import okhttp3.Response
/**
* 自定义请求拦截器
* Created by ge
*/
class SaveCookiesInterceptor(private val mContext: Context) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val response = chain.proceed(request)
//set-cookie可能为多个
if (!response.headers("set-cookie").isEmpty()) {
val cookies = response.headers("set-cookie")
val cookie = encodeCookie(cookies)
saveCookie(request.url().toString(), request.url().host(), cookie)
}
return response
}
//整合cookie为唯一字符串
private fun encodeCookie(cookies: List): String {
val sb = StringBuilder()
val set = HashSet()
for (cookie in cookies) {
val arr = cookie.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (s in arr) {
if (set.contains(s)) continue
set.add(s)
}
}
val ite = set.iterator()
while (ite.hasNext()) {
val cookie = ite.next()
sb.append(cookie).append(";")
}
val last = sb.lastIndexOf(";")
if (sb.length - 1 == last) {
sb.deleteCharAt(last)
}
return sb.toString()
}
//保存cookie到本地,这里我们分别为该url和host设置相同的cookie,其中host可选
//这样能使得该cookie的应用范围更广
private fun saveCookie(url: String, domain: String, cookies: String) {
val sp = mContext.getSharedPreferences(COOKIE_PREF, Context.MODE_PRIVATE)
val editor = sp.edit()
if (TextUtils.isEmpty(url)) {
throw NullPointerException("url is null.")
} else {
editor.putString(url, cookies)
}
if (!TextUtils.isEmpty(domain)) {
editor.putString(domain, cookies)
}
editor.apply()
}
companion object {
private val COOKIE_PREF = "cookies_prefs"
}
}
package com.reliable.mihophysical.api
import android.content.Context
import android.text.TextUtils
import java.io.IOException
import okhttp3.Interceptor
import okhttp3.Response
/**
* 自定义请求拦截器
* Created by ge
*/
class AddCookiesInterceptor(private val mContext: Context) : Interceptor {
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val builder = request.newBuilder()
val cookie = getCookie(request.url().toString(), request.url().host())
if (!TextUtils.isEmpty(cookie)) {
builder.addHeader("Cookie", cookie!!)
}
return chain.proceed(builder.build())
}
private fun getCookie(url: String, domain: String): String? {
val sp = mContext.getSharedPreferences(COOKIE_PREF, Context.MODE_PRIVATE)
if (!TextUtils.isEmpty(url) && sp.contains(url) && !TextUtils.isEmpty(sp.getString(url, ""))) {
return sp.getString(url, "")
}
if (!TextUtils.isEmpty(domain) && sp.contains(domain) && !TextUtils.isEmpty(sp.getString(domain, ""))) {
return sp.getString(domain, "")
}
return null
}
companion object {
private val COOKIE_PREF = "cookies_prefs"
}
}
// 自定义请求拦截
okBuilder.addInterceptor(AddCookiesInterceptor(context!!))
okBuilder.addInterceptor(SaveCookiesInterceptor(context!!))