首页 > 系统 > Android > 正文

Android中volley封装实践记录

2019-10-21 21:25:48
字体:
来源:转载
供稿:网友

前言

在项目中一般使用使用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内部统一处理:

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

public class NewsApi { public static void getInfo(NetCallback<News> callback) {  new GetRequest<>(INetConstant.NEWS, News.class, true, callback); }}

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

最后贴一张效果图:

Android,volley,封装

图片发自简书App

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

总结

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


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表