欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android中volley封装实践记录

程序员文章站 2022-03-08 17:17:29
前言 在项目中一般使用使用volley方式如下,用起来给人一种很乱的感觉,于是一种盘它的想法油然而生。 public void get() { strin...

前言

在项目中一般使用使用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] ( ())。

总结

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