Android中volley封装实践记录

前言

在项目中一般使用使用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.以get方法为例,新建一个GetRequest去继承这个基类,并出解析结果:

public class GetRequest<TResponse> extends AuthenticatedRequestBase<TResponse> {

private final Response.Listener<TResponse> listener;

private final Class<TResponse> clazz;

private final static String TAG = "GetRequest";

private String mUrl;

private NetCallback<TResponse> cb;

private boolean cacheHit;

public GetRequest(String url, Class<TResponse> clazz, boolean cache, NetCallback<TResponse> callback) {

super(Request.Method.GET, url, cache, callback.getErrorListener());

this.listener = callback.getSuccessListener();

this.clazz = clazz;

this.mUrl = url;

this.cb = callback;

//无网络时300ms后返回callback

if (!NetUtils.isConnect(mContext) && mRequestQueue.getCache().get(url) == null) {

Handler handler = new Handler();

handler.postDelayed(new Runnable() {

@Override

public void run() {

cb.OnNetworkOff();

}

}, 300);

}

}

/**

* 这个是缓存的标记,与本地缓存相关

* @param tag

*/

@Override

public void addMarker(String tag) {

super.addMarker(tag);

cacheHit = tag.equals("cache-hit");

}

@Override

protected Response<TResponse> parseNetworkResponse(NetworkResponse 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, clazz), 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, clazz), parseCacheHeaders(response));

} catch (UnsupportedEncodingException | JsonSyntaxException e) {

return Response.error(new ParseError(e));

}

}

@Override

protected void deliverResponse(TResponse response) {

listener.onResponse(response);

}

@Override

protected VolleyError parseNetworkError(VolleyError volleyError) {

return super.parseNetworkError(volleyError);

}

}

3.上面只做了返回成功的处理方式,返回失败时由NetCallback内部统一处理:

@UiThread

public 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.现在可以开始外部封装啦。

public class NewsApi {

public static void getInfo(NetCallback<News> callback) {

new GetRequest<>(INetConstant.NEWS, News.class, true, callback);

}

}

还有一点,volley的缓存实现需要服务端配合在http请求的Cache-control: max-age配置支持缓存,并设定好缓存时间,否则无法生效。

最后贴一张效果图:

 

图片发自简书App

到此结束,后期还会进行优化,代码在[github] (https://github.com/daydaydate/sample (本地下载))。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。

以上是 Android中volley封装实践记录 的全部内容, 来源链接: utcz.com/p/241618.html

回到顶部