在项目中一般使用使用volley方式如下,用起来给人一种很乱的感觉,于是一种盘它的想法油然而生。
public void get() {
String url = "https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=......";
StringRequest request = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override
public void onResponse(String s) {
Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() { @Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(MainActivity.this,volleyError.toString(),Toast.LENGTH_SHORT).show();
}
});
request.setTag("abcGet");
MyApplication.getHttpQueues().add(request);
}首先看一下我封装后的使用例子:
private void initData() {
NewsApi.getInfo(new NetCallback<News>() { @Override
public void OnSuccess(final News result) {
mAdapter.setData(result.getResult().getData());
} @Override
public void OnError(RestfulError error) {
}
});
}有没有看起来很舒服的感觉。好吧,让我开始盘它吧!
1.首先我先去写了一个基类,用来创建一个新的request并把它加入到volley内部封装的请求队列中,代码如下:
public abstract class AuthenticatedRequestBase<T> extends Request<T> { private final static String TAG = "AuthenticatedRequestBase"; private static final int TIME_OUT = 30000; private static final int MAX_RETRIES = 1; private static final float BACKOFF_MULT = 2f; protected Context mContext; protected RequestQueue mRequestQueue; /**
* 创建新的请求,并把请求加入到请求队列requestQueue中
*
* @param method
* @param url
* @param cache
* @param errorListener
*/
@SuppressLint("LongLogTag") public AuthenticatedRequestBase(int method, String url, boolean cache, Response.ErrorListener errorListener) { super(method, url, errorListener); //this.setShouldCache(cache);
this.setRetryPolicy(new DefaultRetryPolicy(
TIME_OUT,
MAX_RETRIES,
BACKOFF_MULT));
mRequestQueue = YZ.getInstance().getRequestQueue(); if (mRequestQueue == null) { throw new IllegalArgumentException("mRequestQueue can't be null");
}
mContext = YZ.getInstance().getContext(); if (mContext == null) { throw new IllegalArgumentException("mContext can't be null");
} //如果重新发出服务器请求的时候,需要清除之前的缓存。
if (!cache) {
Cache volleyCache = mRequestQueue.getCache();
Cache.Entry cacheEntry = volleyCache.get(url); if (cacheEntry != null) {
volleyCache.remove(url);
Log.d(TAG, "remove volley cache:" + url);
}
}
mRequestQueue.add(this);
} /**
* 重写这个方法,可以在http请求头里面加入token,客户端能接受的数据类型
*
* @return
* @throws AuthFailureError
*/
@CallSuper
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> headers = new HashMap<>();
String token = "............"; //headers.put("Authorization", "bearer " + token);
//针对Get方法,申明接受的enum类型
// headers.put("Accept", "charset=utf-8");
return headers;
} /**
* 网络错误问题统一处理
*
* @param volleyError
* @return
*/
@CallSuper
@Override
protected VolleyError parseNetworkError(VolleyError volleyError) { return super.parseNetworkError(volleyError);
}
}代码注释比较清晰,就不在赘述。
2.新建一个CommonRequest去继承这个基类,并出解析结果:
/**
* Created by yz on 2019/2/2.
*/public class CommonRequest<TRequest, TResponse> extends AuthenticatedRequestBase<TResponse> { private String TAG = this.getClass().getSimpleName(); private final static Gson gson = new Gson(); private final Response.Listener<TResponse> listener; private final Class<TResponse> tResponseClazz; private TRequest request; private static final String PROTOCOL_CHARSET = "utf-8"; private boolean cacheHit; private String mUrl; private NetCallback<TResponse> cb; /**
* @param url
* @param callback
*/
public CommonRequest(String url, NetCallback<TResponse> callback) { this(url, null, null, false, callback);
} /**
* @param url
* @param request
* @param callback
*/
public CommonRequest(String url, TRequest request, NetCallback<TResponse> callback) { this(url, request, null, false, callback);
} /**
* @param url
* @param responseClass
* @param cache
* @param callback
*/
public CommonRequest(String url, Class<TResponse> responseClass, boolean cache, NetCallback<TResponse> callback) { this(url, null, responseClass, cache, callback);
} /**
* @param url
* @param request
* @param responseClass
* @param cache
* @param callback
*/
public CommonRequest(String url, TRequest request, Class<TResponse> responseClass, boolean cache, NetCallback<TResponse> callback) { super(request == null ? Method.GET : Request.Method.POST, url, cache, callback.getErrorListener()); this.request = request; this.tResponseClazz = responseClass; this.mUrl = url; this.listener = callback.getSuccessListener(); this.cb = callback;
} /**
* get请求返回null,post请求返回byte【】
*
* @return
*/
@Override
public byte[] getBody() { if (request == null) return null;
String mRequestBody = gson.toJson(this.request); try { return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET); return null;
}
} /**
* 这个是缓存的标记,与本地缓存相关,返回response时才会使用此标志
*
* @param tag
*/
@Override
public void addMarker(String tag) { super.addMarker(tag);
cacheHit = tag.equals("cache-hit");
} /**
* clazz为空时,返回null
*
* @param response
* @return
*/
@Override
protected Response<TResponse> parseNetworkResponse(NetworkResponse response) { //无需返回数据
if (tResponseClazz == null) { return Response.success(null, parseCacheHeaders(response));
}
Gson gson = new Gson(); //无网络时,使用本地缓存,通过url去匹配缓存,volley sdk是通过url创建不同的文件来实现缓存的
if (!NetUtils.isConnect(mContext) && mRequestQueue.getCache().get(mUrl) != null) {
String json = new String(mRequestQueue.getCache().get(mUrl).data);
Log.d(TAG, "url==" + mUrl + ",json" + json);
cb.fResponseCacheStatus = ResponseCacheStatus.StaleFromCache; return Response.success(gson.fromJson(json, tResponseClazz), parseCacheHeaders(response));
} //数据是否有更新
try { if (response.statusCode == 304) { //服务端返回缓存数据
cb.fResponseCacheStatus = ResponseCacheStatus.NotModifiedFromServer;
} else if (response.statusCode == 200) { if (cacheHit) { //使用本地缓存
cb.fResponseCacheStatus = ResponseCacheStatus.FreshFromCache;
} else { //使用服务端更新数据
cb.fResponseCacheStatus = ResponseCacheStatus.NewFromServer;
}
} else {
cb.fResponseCacheStatus = ResponseCacheStatus.NewFromServer;
}
Log.d(TAG, "fResponseCacheStatus = " + cb.fResponseCacheStatus);
String json = new String(response.data, parseCharset(response.headers)); return Response.success(gson.fromJson(json, tResponseClazz), parseCacheHeaders(response));
} catch (UnsupportedEncodingException | JsonSyntaxException e) { return Response.error(new ParseError(e));
}
} @Override
protected void deliverResponse(TResponse response) {
listener.onResponse(response);
}
}3.上面只做了返回成功的处理方式,返回失败时由NetCallback内部统一处理:
@UiThreadpublic abstract class NetCallback<TResponse> { public ResponseCacheStatus fResponseCacheStatus = ResponseCacheStatus.NewFromServer; private String TAG = this.getClass().getSimpleName(); public boolean enableAutomaticToastOnError = true; public NetCallback() {
} public NetCallback(boolean enableAutomaticToastOnError) { this.enableAutomaticToastOnError = enableAutomaticToastOnError;
} public abstract void OnSuccess(TResponse result); public abstract void OnError(RestfulError error); public void OnNetworkOff() { //do nothing ,use it according to requirement
} public Response.Listener<TResponse> getSuccessListener() { return new Response.Listener<TResponse>() { @Override
public void onResponse(TResponse result) {
OnSuccess(result);
}
};
} public Response.ErrorListener getErrorListener() { return new Response.ErrorListener() { @Override
public void onErrorResponse(VolleyError volleyError) { if (volleyError instanceof TimeoutError) {
Log.e(TAG, "networkResponse == null"); //volley TimeoutError
OnError(new RestfulError());
} if (volleyError.networkResponse != null) { int statusCode = volleyError.networkResponse.statusCode;
String errorMessage = new String(volleyError.networkResponse.data); switch (statusCode) { case 401: //post a Permission authentication failed event
break; default:
Log.d(TAG, "errorMessage =" + errorMessage); try {
RestfulError error = new Gson().fromJson(errorMessage, RestfulError.class); if (enableAutomaticToastOnError && error.getCode() != null) { //toast(error.ExceptionMessage); //toast it in main thread
}
OnError(error);
} catch (Exception e) {
OnError(new RestfulError());
Log.d(TAG, "e =" + e.toString());
} break;
}
}
}
};
}
}4.注意到没有,在AuthenticatedRequestBase内部有一个环境类YZ,主要负责获取项目主程序中的context和请求队列:
public class YZ implements AppRequestQueue { private static final int DEFAULT_VOLLEY_CACHE_SIZE = 100 * 1024 * 1024; private Context context; private int cacheSize; private YZ() {
} @Override
public RequestQueue getRequestQueue() { return Volley.newRequestQueue(context, cacheSize);
} public Context getContext() { return context;
} private static class SingletonHolder { private static YZ instance = new YZ();
} public static YZ getInstance() { return SingletonHolder.instance;
} /**
* need a app context
*
* @param appContext
*/
public void init(final Context appContext) {
init(appContext, DEFAULT_VOLLEY_CACHE_SIZE);
} /**
* @param appContext
* @param cacheSize
*/
public void init(final Context appContext, final int cacheSize) { this.context = appContext; this.cacheSize = cacheSize;
}
}这个类需要在app的application中初始化:
public class BaseApp extends Application { public String TAG = this.getClass().getSimpleName(); public static Context applicationContext; public static Executor threadPool; public static final int THREAD_POOL_SIZE = 3; public static final boolean isDebug = BuildConfig.BUILD_TYPE.equals("debug"); @Override
public void onCreate() { super.onCreate();
applicationContext = getApplicationContext();
threadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
initNet();
} private void initNet() {
YZ.getInstance().init(this);
} public Context getInstance() { return applicationContext;
}
}4.现在可以开始外部封装啦,测试了一下,get和post请求都可以成功。
public class NewsApi {
public static void getInfo(NetCallback<News> callback) { new CommonRequest<>(INetConstant.NEWS, News.class, true, callback);
} public static void postInfo(NetCallback<News> callback) {
RequestBody body = new RequestBody("top", "b2f8e4aeacfa310cabfadd5189bbe4d5"); new CommonRequest<>(INetConstant.NEWS, body, News.class, true, callback);
} public static void cancelGetInfo() { //how to do
}
}还有一点,volley的缓存实现需要服务端配合在http请求的Cache-control: max-age配置支持缓存,并设定好缓存时间,否则无法生效。
作者:一朵喇叭花呜拉呜拉
链接:https://www.jianshu.com/p/1f09e91bee66
随时随地看视频