Android中volley封装实践记录

作者:一朵喇叭花呜拉呜拉 时间:2021-07-05 19:55:03 

前言

在项目中一般使用使用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配置支持缓存,并设定好缓存时间,否则无法生效。

最后贴一张效果图:

Android中volley封装实践记录 

图片发自简书App

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

来源:https://www.jianshu.com/p/1f09e91bee66

标签:android,volley,封装
0
投稿

猜你喜欢

  • java实现多人聊天室可视化

    2021-08-27 01:16:49
  • c#实现flv解析详解示例

    2022-06-10 22:57:21
  • Flutter 实现网易云音乐字幕的代码

    2023-04-01 11:50:45
  • C#波形图控件制作示例程序

    2022-02-24 21:50:40
  • SpringBoot整合rockerMQ消息队列详解

    2021-10-03 10:55:14
  • C#中参数个数可变的方法实例分析

    2022-12-16 20:02:32
  • 图文详解Maven工程打jar包的N种方式

    2022-12-12 21:36:23
  • C语言数据结构实现银行模拟

    2023-04-16 17:25:49
  • Kotlin浅析延迟初始化与密封类的实现方法

    2023-05-06 13:54:44
  • 详解java中String、StringBuilder、StringBuffer的区别

    2023-06-17 06:03:23
  • Java中StringUtils工具类进行String为空的判断解析

    2022-10-16 06:56:53
  • C# using()的使用方法

    2022-03-02 23:23:37
  • Android上传文件到服务端并显示进度条

    2023-06-23 07:48:33
  • Java绘制迷宫动画并显示的示例代码

    2022-04-06 22:37:45
  • c#裁剪图片后使用zxing生成二维码示例分享

    2021-09-08 17:06:58
  • C#获取U盘序列号的方法

    2023-09-15 02:09:54
  • C#设置与获取环境变量的方法详解

    2021-09-03 20:55:29
  • Android Fragment实现底部通知栏

    2023-12-25 21:01:33
  • Java中方法名称和泛型相同的用法示例

    2023-08-12 03:18:55
  • java微信公众号支付示例详解

    2023-11-15 05:52:01
  • asp之家 软件编程 m.aspxhome.com