Xutil框架相信很多Android开发程序员都不陌生,Xutil集注解、网络、图片加载、数据库操作与一身的Android端框架,更新的Xutil3更简单的方便,在这里不针对Xutil的中心内容进行讲解,主要讲在使用Xutil中发现的问题。
由于公司需要,对于文件下载、上传等需要对0B文件也做到兼容,但是后期测试发现Xutil3在下载0B文件后直接抛出了异常,具体的抛错地方如下。
如果文件下载正常的话,我们下载一个文件的执行顺序如下(正常情况下):onWaiting-->onStarted-->onLoading-->onSuccess-->onFinished。正常的情况下是这样的,但是在下载0B文件时,意想不到的是执行完onStarted后,直接进入了onError方法,错误如下,竟然提示没有Cache file not found???
对于0B文件下载的出错的处理,这里我偷了个懒,没有深入研究为什么抛出这个异常,而是在它回调到onError时,再进行进一步的判断,使用最基础的HttpURLConnection的方法上传,暂时处理一下这个问题(待深入-_-),具体代码如下。
/**
* 从网络Url中下载文件
*
* @param urlStr
* @param savePath
* @throws IOException
*/
private void downLoadFromUrl(String urlStr, String savePath) {
new Thread(){
@Override
public void run() {
super.run();
FileOutputStream fos = null;
InputStream inputStream = null;
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置超时间为3秒
conn.setConnectTimeout(3 * 1000);
//防止屏蔽程序抓取而返回403错误
conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
//得到输入流
inputStream = conn.getInputStream();
//获取自己数组
byte[] getData = readInputStream(inputStream);
//文件保存位置
File saveDir = new File(savePath);
if (!saveDir.getParentFile().exists()) {
saveDir.getParentFile().mkdir();
}
File file = new File(savePath);
fos = new FileOutputStream(file);
fos.write(getData);
//TODO: 这里实现你上传成功需要处理的逻辑
LogUtil.i("info:" + url + " download success");
}catch (IOException ex){
ex.printStackTrace();
LogUtil.e(ex.getMessage());
//TODO:这里是一个简单错误日志异常上报
new LogApi().uploadLog("downLoadFromUrl"
, DownloadCallback.class.getSimpleName()
, "try catch;" + ex.toString()
+ "ex.message:" + ex.getMessage() + ";"
+ "downloadInfo:" + downloadInfo.toString() + ";"
, 1);
//TODO: 这里实现你上传失败需要处理的逻辑
}finally {
try {
if (fos != null) {
fos.close();
}
if (inputStream != null) {
inputStream.close();
}
}catch (IOException ex){
ex.printStackTrace();
LogUtil.e(ex.getMessage());
new LogApi().uploadLog("downLoadFromUrl"
, DownloadCallback.class.getSimpleName()
, "try catch finish;" + ex.toString()
+ "ex.message:" + ex.getMessage() + ";"
+ "downloadInfo:" + downloadInfo.toString() + ";"
, 1);
//TODO: 这里实现你上传失败需要处理的逻辑
}
}
}
}.start();
}
/**
* 从输入流中获取字节数组
*
* @param inputStream
* @return
* @throws IOException
*/
public byte[] readInputStream(InputStream inputStream) throws IOException {
byte[] buffer = new byte[1024];
int len = 0;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while ((len = inputStream.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.close();
return bos.toByteArray();
}
上一个问题,发现0B文件无法下载,测试上传时???,竟然直接出现红点,说上传错误,经过作者不懈努力的查找,终于发现了问题所在。原来Xutil3在添加上传文件的Body时使用到的MultipartBody中,初始化其中的CounterOutputStream时,调用其中的方法addStream,具体如下。>0????,猜到同仁们知道怎么改了吧,改成>=0,就可以上传了。修改的具体代码在MultipartBody-->CounterOutputStream-->addStream
public void addStream(InputStream inputStream) {
if (total.get() == -1L) return;
long length = InputStreamBody.getInputStreamLength(inputStream);
//没错,就是这里>0?????????
if (length > 0) {
total.addAndGet(length);
} else {
total.set(-1L);
}
}
此问题,具体表现,如下在下载2G文件时,它的onLoading竟然不调用。。。但是它一直在使用你的网络,如果你重新暂停以后再开始他又会调用了,但是进度会出现问题,一直百分之百。。。这个问题引发的原因,作者找了下,2G这个间隔是不是有点熟悉?没错,就是int类型的最大的取值范围,在框架内的具体体现,看官们请看下面具体代码。
上面几张图循序渐进,图一是针对于contentLength初始化,进入到图二、图三是具体调用的方法,看到图三,发现没有2G以上这里默认,返回的是-1,但是图四是整体控制onLoading的方法,>-1才执行(忘记具体代码,但是-1是绝对不会继续执行的)???,修改方案看官们也懂了吧?>=-1,让它继续执行下去;这里解决了第一次不会执行onLoading的方法,但是之后又有回调了,这是为什么呢?原来使我们设置了断点续传,会读取本地下载的长度,这个时候current的长度不为0,所以会执行onLoading的方法,但是由于那个长度溢出了,最后下载的时候onLoading中的total和current是一直相等的,这个时候如果你需要设置进度的话,你最好使用后台返回的文件大小进行设置,要不然会导致进度出现问题(如果还想改更底层,就只能对URLConnection了,由于系统已经完善,不能更改太多,暂时如此处理),如果修改了问题三的bug,这个时候你要注意total在第一次返回的时候会是0,如果有使用到就对他进行大于0的判断。
public int getContentLength() {
long l = getContentLengthLong();
if (l > Integer.MAX_VALUE)
return -1;
return (int) l;
}
需要修改的代码HttpTask-->updateProgress。
这不能说是bug,只能说是使用问题,关于设置重定向问题,默认提示Xutil3是自动支持重定向的,过于相信没管它,但是测试后发现它不会自动重定向,怎么做呢?下面这样做就好。这里我是在自定义的请求中设置的,如果你只是一个实例,直接调用内部的setRedirectHandler方法就好,这里Location是需要重定向的地址。
/**
* 自定义网络请求Header,携带及保存cookie
* Created by yzy on 2018/4/8 10:15
*/
public class NetParams extends RequestParams {
public NetParams(String uri) {
super(uri);
setRedirectHandler(uriRequest -> {
RequestParams requestParams = uriRequest.getParams();
String location = uriRequest.getResponseHeader("Location"); //协定的重定向地址
if(!StringUtils.isEmptyOrNull(location)){
requestParams.setUri(location); //重新设置url地址
}
return requestParams;
}); //重定向
setMaxRetryCount(5); //重定向次数
}
}