首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android中volley封装实践记录

Android中volley封装实践记录

作者头像
砸漏
发布2020-11-04 10:28:19
5110
发布2020-11-04 10:28:19
举报
文章被收录于专栏:恩蓝脚本恩蓝脚本

前言

在项目中一般使用使用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(本地下载))。

总结

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档