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 (本地下载))。

总结

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

(0)

相关推荐

  • Android Volley框架使用源码分享

    过去在Android上网络通信都是使用的Xutils 因为用它可以顺道处理了图片和网络这两个方面,后来发觉Xutils里面使用的是HttpClient  而Google在6.0的版本上已经把HttpClient废除了,所以开始寻找新的网络框架,okhttp也用过,但是它是在作用在UI线程,使用起来还需要用handler 所以就先用着Volley框架了.  这里我先分析下Volley框架的简单网络请求的源码. 使用Volley请求网络数据的简单过程: RequestQueue queue = Vo

  • Android Volley框架全面解析

     Volley简介 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Android网络

  • Android中volley封装实践记录(二)

    前言 关于android的volley封装之前写过一篇文章,见链接(https://www.jb51.net/article/155875.htm).这篇文章主要是换种方式进行封装,具体步骤如下所示. 步骤如下 1.创建Request,并设置相应的参数: public class CommonJsonObjectRequest extends JsonObjectRequest { private String TAG = this.getClass().getSimpleName(); /*

  • Android Volley框架使用方法详解

    本文主要从两个方面对Android Volley框架的使用方法进行讲解,具体内容如下 一.网络请求 1.get方式请求数据 // 1 创建一个请求队列 RequestQueue requestQueue = Volley.newRequestQueue(VolleyActivity.this); // 2 创建一个请求 String url = "http://api.m.mtime.cn/PageSubArea/TrailerList.api"; StringRequest stri

  • Android 网络请求框架Volley实例详解

    Android 网络请求框架Volley实例详解 首先上效果图 Logcat日志信息on Reponse Volley特别适合数据量不大但是通信频繁的场景,像文件上传下载不适合! 首先第一步 用到的RequetQueue RequestQueue.Java RequestQueue请求队列首先得先说一下,ReuqestQueue是如何对请求进行管理的...RequestQueue是对所有的请求进行保存...然后通过自身的start()方法开启一个CacheDispatcher线程用于缓存调度,开

  • Android的HTTP类库Volley入门学习教程

    1. 什么是Volley 我们平时在开发Android应用的时候不可避免地都需要用到网络技术,而多数情况下应用程序都会使用HTTP协议来发送和接收网络数据.Android系统中主要提供了两种方式来进行HTTP通信,HttpURLConnection和HttpClient,几乎在任何项目的代码中我们都能看到这两个类的身影,使用率非常高. 不过HttpURLConnection和HttpClient的用法还是稍微有些复杂的,如果不进行适当封装的话,很容易就会写出不少重复代码.于是乎,一些Androi

  • Android 开发中Volley详解及实例

    Android 开发中Volley详解及实例 最近在做项目的时候,各种get和post.简直要疯了,我这种啥都不了解的,不知道咋办了,然后百度看了下,可以用volley进行网络请求与获取,下面就介绍下volley的用法. volley有三种方式:JsonObjectRequest,JsonArrayRequest,StringRequest.其实都是差不多了,举一反三就ok了,这里我就讲下JsonObjectRequest. 方法如下: JsonObjectRequest jsonObjectR

  • android 网络请求库volley方法详解

    使用volley进行网络请求:需先将volley包导入androidstudio中 File下的Project Structrue,点加号导包 volley网络请求步骤: 1. 创建请求队列       RequestQueue queue = Volley.newRequestQueue(this); 2.创建请求对象(3种) StringRequest request = new StringRequest("请求方法","请求的网络地址","成功的网

  • Android 中Volley二次封装并实现网络请求缓存

    Android 中Volley二次封装并实现网络请求缓存 Android目前很多同学使用Volley请求网络数据,但是Volley没有对请求过得数据进行缓存,因此需要我们自己手动缓存. 一下就是我的一种思路,仅供参考 具体使用方法为: HashMap<String,String> params = new HashMap<>(); params.put("id", "1"); params.put("user", &quo

  • Android中Volley框架下保持会话方法

    公司经理把我拉出来,死马当活马医,做一个安卓app,作为刚毕业几个月且只是培训了几个月的小白来说,这无疑是一个非常大的挑战,当然最大的挑战不是这个,最大的挑战时两个周做出来.这是最蛋疼的,说实话,对于有两三年的开发经验的人来说,两个周开发一个项目很简单,说不定还有很多时间用来干别的. 于是一上来就把自己给难住了,登陆还是很好做的,只要验证返回的信息就可以跳转,但是在接下来后面的数据接口连接的时候各种报错,整了两天,查了很多信息,还接受了公司老人的嘲讽和谩骂终于做出来了. 这个是基于session

随机推荐