Volley笔记-重定向的处理

时间:2021-06-24 19:45:35

一、Volley介绍


Volley是一款开源的Android异步网络请求框架,适合通信量小,通信频繁的网络操作。
Volley官方代码地址:
https://android.googlesource.com/platform/frameworks/volley
Git地址:https://github.com/mcxiaoke/android-volley
Git地址是镜像地址,而且对官方代码略有改进,跟官方代码并不完全一致,由于Volley官方源码放在谷歌服务器上,国内需要*才能访问,所以一般使用Git上的源码。
另外,还有一个项目,对Volley源码进行了一些扩展,Git地址如下:
https://github.com/DWorkS/VolleyPlus


二、Volley源码分析


Volley的源码结构和调用流程都比较清晰,详细分析可以参见这篇博客

http://blog.csdn.net/ljx19900116/article/details/43482011


三、Volley的一些扩展


Volley虽然好用,但是在实际使用的时候,总有些地方不能满足要求,当然Volley的扩展性很强,这时候就需要自己动手扩展了。
很多时候不需要改动源码,只要添加子类扩展就可以满足需要。
比如对json的解析,可以通过继承Volley的Request类,并使用Gson库实现一个GsonRequest类;
比如对http的访问,可以继承HttpStack,并使用OkHttp库实现一个OkHttpStack;
当然,还有些时候,就不得不动源码了,比如对重定向的处理,说到重定向,这里得先说下Volley的请求重试机制。

一些具体的扩展可以参考前述的VolleyPlus项目。


Volley的重试机制


Volley有个RetryPolicy接口,并且实现了默认的处理类DefaultRetryPolicy,可以实现RetryPolicy接口自定义重试策略。
在BasicNetwork类里处理请求的方法是:

public NetworkResponse performRequest(Request request)

 

该方法的方法体是放在while(true)里,这就是处理重试请求的核心机制,当请求正常返回return的时候,该方法就执行结束,当请求抛出异常的时候,该方法也执行结束。
重试请求的处理就是,当捕获到异常的时候,暂时不抛出,而是根据重试策略判断是否需要再次请求,需要则不抛异常,重新执行while(true),如果不需要再次请求,则抛出异常。

private static void attemptRetryOnException(String logPrefix,Request request,VolleyError exception) 
throwsVolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
intoldTimeout = request.getTimeoutMs();
try{
retryPolicy.retry(exception);//不抛异常则重新请求
}catch(VolleyError e) {
request.addMarker( String.format("%s-timeout-giveup [timeout=%s]",logPrefix,oldTimeout));
throw e;//抛出异常 则不再次请求
}
request.addMarker(String.format("%s-retry [timeout=%s]",logPrefix,oldTimeout));
}

 

Volley处理重定向


Volley对重定向并没有额外处理,据说HttpClient本身是支持的,当然前提是你使用的HttpClientStack,同时还得改动HttpClientStack添加重定向的代码,当然这个方案我没有验证过。
我要说的是Git地址

https://github.com/mcxiaoke/android-volley的代码对重定向的处理,处理详情见BasicNetwork类。
在该类里,当程序发现响应返回的状态码是301或302时,则改动原请求的请求URL为重定向URL,然后抛出异常。


 

if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
String newUrl = responseHeaders.get("Location");
request.setRedirectUrl(newUrl);//原请求指定重定向的URL
}


由于Volley有重试机制,对于响应失败的请求,会根据重试策略再次进行请求,此时,在while(true)里重新执行上次返回302的请求,则完成了对重定向的处理。
异常捕获的时候,对重定向状态单独处理,代码如下:

else if(statusCode==HttpStatus.SC_MOVED_PERMANENTLY||
statusCode==HttpStatus.SC_MOVED_TEMPORARILY) {
attemptRetryOnException("redirect",//attemptRetryOnException方法见前述
request,newRedirectError(networkResponse));//返回302则抛出重定向异常
}


 

但是这种对重定向的解决方案并不能满足一些场景,比如:
    1、原请求是POST请求,但是重定向一般是GET请求,这样只改变原请求的访问地址,并不一定能请求成功。
    2、重定向请求需要带的头信息与原请求不一致的,比如重定向是跨域的,可能需要对cookie处理。

除此之外,这种解决方案需要指定重试策略,但Volley默认是不重试的,而且我认为,大多数场景下,系统并不需要自动重试,而是由用户自己重新发起请求。这样一来,为了重定向需要单独指定重试策略,对代码的统一处理略有不便。

既然这种方案不能满足需求,那就需要改动源码了。
重定向的处理应该在访问请求的最底层处理比较合适,可以在HurlStack里处理。
自己处理重定向需要对connection加句代码:

connection.setInstanceFollowRedirects(false)

处理重定向的时候需要满足递归性,也就是说,A地址重定向到B地址,B地址又重定向到C地址,无限重定向,要能持续处理,并且原路返回结果。
HurlStack处理请求的方法是

public HttpResponse performRequest(Request request,Map additionalHeaders)


这里的入参是request对象,改动原request对象重新请求是比较麻烦的,而且由于是子线程里运行的,容易出现很多难解的问题。
理想的方式是构造新的请求,但是不能是构造新的Request对象,因为我们还要通过入参的request对象原路返回结果。
这时需要将performRequest方法里执行请求的代码提炼出一个方法来

private HttpResponse executeRequest(int method,String url,Map headers, byte[] postBody,
int timeoutMs,String contentType)


并且对重定向的处理如下

if(responseStatus.getStatusCode() == HttpStatus.SC_MOVED_PERMANENTLY||
responseStatus.getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) {
String redirectUrl = connection.getHeaderField("Location");
if(redirectUrl !=null&& redirectUrl.length() >0) {
return executeRequest(Method.GET,redirectUrl,headers, null,timeoutMs,contentType);
}
}


相应的,本类的一些其他的被调用方法由于入参类型问题也需要相应的变动,不过整体来说,改动还是很简单的。

另外可以增加两个方法onPrepareRequest、onReponseFinished;onPrepareRequest方法在请求调用之前执行,onReponseFinished可以在请求调用之后,重定向之前执行。空实现即可,需要的时候,在子类里处理即可,可以用于打印日志或者处理头信息等。
虽然改动量相对来说,略多,但是对一些场景来说,还是需要的。当然,如果重试机制的处理方式即可满足需求,那就无需动源码了。