标签(空格分隔): Volley Session小结
Volley创建过程
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
// Prior to Gingerbread, HttpUrlConnection was unreliable.
// See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
Volley默认给出了两种创建方式,其中HurlStack对应HttpURLConnection,而HttpClientStack对应于HttpClient.默认情况下,Volley是不支持Cookies和Session的。这两种情况下添加session支持的方式如下。
两种方式对Session支持:
1.HttpURLConnection方式
CookieManager cookieManager = new CookieManager( );
CookieHandler.setDefault( cookieManager );
RequestQueue mRequestQueue = Volley.newRequestQueue( context )
添加一个CookieManager即可让HttpURLConnection支持session,在HttpURLConnectionImpl实现类中OkHttpClient会引用这个cookiemanager对象。均为java.net包下提供。这种方式目前没有并发问题,下面来看看另外一种。
2.HttpClient方式
//自定义一个PreferencesCookieStore对象,把cookie值本地化存起来
HttpClient defaultHttpClient = new DefaultHttpClient();
CookieStore cookieStore = new PreferencesCookieStore( context );
defaultHttpClient.setCookieStore( cookieStore );
HttpStack httpStack = new HttpClientStack( defaultHttpClient );
RequestQueue mRequestQueue = Volley.newRequestQueue( context, httpStack );
但是有一个严重问题使用这种方式之后,网络请求由httpClient执行,我们会失去Volley一个很重要的并发功能,因为我们使用的是HttpClient的一个单例,我们必须遵守它的规则,同一个实例下一个请求必须在上一个请求已经结束情况下才行。很显然,这样使用Volley并发的问题在实际开发中完全不能满足我们的需要。下面是一种改良法,经我测试,只不过是加上的等待机制,对于真正并发来说效率低了许多。
如下:
DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
ClientConnectionManager mClientConnectionManager = defaultHttpClient.getConnectionManager();
HttpParams mHttpParams = defaultHttpClient.getParams();
ThreadSafeClientConnManager mThreadSafeClientConnManager = new ThreadSafeClientConnManager( mHttpParams,
mClientConnectionManager.getSchemeRegistry() );
defaultHttpClient = new DefaultHttpClient( mThreadSafeClientConnManager, mHttpParams );
CookieStore cookieStore = new PreferencesCookieStore( context );
defaultHttpClient.setCookieStore( cookieStore );
HttpStack httpStack = new HttpClientStack( defaultHttpClient );
mRequestQueue = Volley.newRequestQueue( context, httpStack );
关于HttpClient的Cookie机制简要说明,假设cookie保存在本地,当去请求一个网址的时候,request会获取本地的cookie对象(CookieStore.getCookies()),即是我们代码中setCookieStore设置的,第一次,本地cookie是空,这个时候服务器给回response的时候,会根据规则判断是否要往本地写cookie (CookieStore.addCookie(Cookie)),第一次显然会将当前这次连接的cookie信息保存下来。以后再由请求的时候,即能从本地中获取cookie去请求了,当然整个过程复杂度是比较高的
eg:自定义PreferencesCookieStore如下
PreferencesCookieStore.java
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import org.apache.http.client.CookieStore;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.cookie.BasicClientCookie;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 保存到 Preferences 的cookie
*
*/
public class PreferencesCookieStore implements CookieStore {
private static final String COOKIE_PREFS = "CookiePrefsFile";
private static final String COOKIE_NAME_STORE = "names";
private static final String COOKIE_NAME_PREFIX = "cookie_";
private final ConcurrentHashMap<String, Cookie> cookies;
private final SharedPreferences cookiePrefs;
/**
* Construct a persistent cookie store.
*/
public PreferencesCookieStore(Context context) {
cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
cookies = new ConcurrentHashMap<String, Cookie>();
// Load any previously stored cookies into the store
String storedCookieNames = cookiePrefs.getString(COOKIE_NAME_STORE, null);
if(storedCookieNames != null) {
String[] cookieNames = TextUtils.split(storedCookieNames, ",");
for(String name : cookieNames) {
String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null);
if(encodedCookie != null) {
Cookie decodedCookie = decodeCookie(encodedCookie);
if(decodedCookie != null) {
cookies.put(name, decodedCookie);
}
}
}
// Clear out expired cookies
clearExpired(new Date());
}
}
@Override
public void addCookie(Cookie cookie) {
String name = cookie.getName();
// Save cookie into local store, or remove if expired
if(!cookie.isExpired(new Date())) {
cookies.put(name, cookie);
} else {
cookies.remove(name);
}
// Save cookie into persistent store
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet()));
prefsWriter.putString(COOKIE_NAME_PREFIX + name, encodeCookie( new SerializableCookie( cookie ) ));
prefsWriter.commit();
}
@Override
public void clear() {
// Clear cookies from local store
cookies.clear();
// Clear cookies from persistent store
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for(String name : cookies.keySet()) {
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
}
prefsWriter.remove(COOKIE_NAME_STORE);
prefsWriter.commit();
}
@Override
public boolean clearExpired(Date date) {
boolean clearedAny = false;
SharedPreferences.Editor prefsWriter = cookiePrefs.edit();
for(ConcurrentHashMap.Entry<String, Cookie> entry : cookies.entrySet()) {
String name = entry.getKey();
Cookie cookie = entry.getValue();
if(cookie.isExpired(date)) {
// 清除cookies
cookies.remove(name);
// Clear cookies from persistent store
prefsWriter.remove(COOKIE_NAME_PREFIX + name);
// We've cleared at least one
clearedAny = true;
}
}
// Update names in persistent store
if(clearedAny) {
prefsWriter.putString(COOKIE_NAME_STORE, TextUtils.join(",", cookies.keySet()));
}
prefsWriter.commit();
return clearedAny;
}
@Override
public List<Cookie> getCookies() {
return new ArrayList<Cookie>(cookies.values());
}
protected String encodeCookie(SerializableCookie cookie) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(cookie);
} catch (Exception e) {
return null;
}
return byteArrayToHexString(os.toByteArray());
}
protected Cookie decodeCookie(String cookieStr) {
byte[] bytes = hexStringToByteArray(cookieStr);
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
Cookie cookie = null;
try {
ObjectInputStream ois = new ObjectInputStream(is);
cookie = ((SerializableCookie)ois.readObject()).getCookie();
} catch (Exception e) {
e.printStackTrace();
}
return cookie;
}
// Using some super basic byte array <-> hex conversions so we don't have
// to rely on any large Base64 libraries. Can be overridden if you like!
protected String byteArrayToHexString(byte[] b) {
StringBuffer sb = new StringBuffer(b.length * 2);
for (byte element : b) {
int v = element & 0xff;
if(v < 16) {
sb.append('0');
}
sb.append(Integer.toHexString(v));
}
return sb.toString().toUpperCase();
}
protected byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len / 2];
for(int i=0; i<len; i+=2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
public class SerializableCookie implements Serializable {
private static final long serialVersionUID = 6374381828722046732L;
private transient final Cookie cookie;
private transient BasicClientCookie clientCookie;
public SerializableCookie(Cookie cookie) {
this.cookie = cookie;
}
public Cookie getCookie() {
Cookie bestCookie = cookie;
if(clientCookie != null) {
bestCookie = clientCookie;
}
return bestCookie;
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(cookie.getName());
out.writeObject(cookie.getValue());
out.writeObject(cookie.getComment());
out.writeObject(cookie.getDomain());
out.writeObject(cookie.getExpiryDate());
out.writeObject(cookie.getPath());
out.writeInt(cookie.getVersion());
out.writeBoolean(cookie.isSecure());
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
String name = (String)in.readObject();
String value = (String)in.readObject();
clientCookie = new BasicClientCookie(name, value);
clientCookie.setComment((String)in.readObject());
clientCookie.setDomain((String)in.readObject());
clientCookie.setExpiryDate((Date)in.readObject());
clientCookie.setPath((String)in.readObject());
clientCookie.setVersion(in.readInt());
clientCookie.setSecure(in.readBoolean());
}
}
}