Android请求网络--第三方库Android-async-http的使用(6)

前言:

       本篇如约带来以下内容:使用aah下载,使用aah上传。在此之前先把第四篇中拼header的部分再丰富一下。 本篇中上传下载使用的文件资源和服务器资源都是从demo中扒出来的。

正文:

拼Header:

        上次我们说到过,库中定义了Header接口,库中没有提供接口实现类,(这个还是比较合理的),稍微看一下源码就会知道aah库依赖了Http-core库。我们可以找到http-core库中有一个实现类,(插一句,毕竟Header约定俗成,放一个正常的库都会写成这样的接口,所以这个现象不奇怪),这个实现类:BasicHeader,我们看一下源码:
public class BasicHeader implements Header, Cloneable, Serializable {
    private static final long serialVersionUID = -5427236326487562174L;
    private final String name;
    private final String value;

    public BasicHeader(String name, String value) {
        this.name = (String)Args.notNull(name, "Name");
        this.value = value;
    }

    public String getName() {
        return this.name;
    }

    public String getValue() {
        return this.value;
    }

    public String toString() {
        return BasicLineFormatter.INSTANCE.formatHeader((CharArrayBuffer)null, this).toString();
    }

    public HeaderElement[] getElements() throws ParseException {
        return this.value != null?BasicHeaderValueParser.parseElements(this.value, (HeaderValueParser)null):new HeaderElement[0];
    }

    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
很清晰的代码,可以直接构建Header中的“键值对”,想来是没有什么特殊需求的,就不在赘述了。

下载:

测试文件:https://httpbin.org/robots.txt
使用函数原型:
public RequestHandle get(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler)
        正如你猜测的,重点在于回调接口,显然一个优秀的库会在内部进行处理,按照接口标准进行输出,这里我们使用库中提供的接口实现类:FileAsyncHttpResponseHandler,继承自AsyncHttpResponseHandler类(该类实现了ResponseHandlerInterface接口)。
我认为此刻非常有必要看一下这个接口实现类,这样能很快的了解如何使用,(如果要详细了解需要去看一些业务处理类)。
鉴于之前没有带着大家看源码,且FileAsyncHttpResponseHandler这个类有点长,我们按照一个正常的顺序扒一扒。先看接口:
public interface ResponseHandlerInterface {
    void sendResponseMessage(HttpResponse var1) throws IOException;

    void sendStartMessage();

    void sendFinishMessage();

    void sendProgressMessage(long var1, long var3);

    void sendCancelMessage();

    void sendSuccessMessage(int var1, Header[] var2, byte[] var3);

    void sendFailureMessage(int var1, Header[] var2, byte[] var3, Throwable var4);

    void sendRetryMessage(int var1);

    URI getRequestURI();

    Header[] getRequestHeaders();

    void setRequestURI(URI var1);

    void setRequestHeaders(Header[] var1);

    void setUseSynchronousMode(boolean var1);

    boolean getUseSynchronousMode();

    void setUsePoolThread(boolean var1);

    boolean getUsePoolThread();

    void onPreProcessResponse(ResponseHandlerInterface var1, HttpResponse var2);

    void onPostProcessResponse(ResponseHandlerInterface var1, HttpResponse var2);

    void setTag(Object var1);

    Object getTag();
}
我们应当知道这套接口包含两部分内容
1.使用模式
2.线程通信
我们发觉和我们之前使用的实现类“要关心的东西”差异比较大,没错,这个接口更多的是库内部所关心的,所以一定有一个抽象的实现类实现了库内部关心的内容,并且定义了我们使用时需要关心的事情(抽象方法),aah到了固定的阶段进行回调。
看一下这个类:AsyncHttpResponseHandler
(它会很长,我们抛开线程通信的具体内容[说白了就是handler]、已经实现的内容不看[那一定是库内部关心的,不是我们现在想要了解的]),将抽象方法和可能关心的内容扒出来:
public abstract class AsyncHttpResponseHandler implements ResponseHandlerInterface {
    //...
    public AsyncHttpResponseHandler() {
        this((Looper)null);
    }
    public AsyncHttpResponseHandler(Looper looper) {
        //...
    }
    public AsyncHttpResponseHandler(boolean usePoolThread) {
        //...
    }

  //...

    public void onProgress(long bytesWritten, long totalSize) {
        AsyncHttpClient.log.v("AsyncHttpRH", String.format("Progress %d from %d (%2.0f%%)", new Object[]{Long.valueOf(bytesWritten), Long.valueOf(totalSize), Double.valueOf(totalSize > 0L?(double)bytesWritten * 1.0D / (double)totalSize * 100.0D:-1.0D)}));
    }

    public void onStart() {
    }

    public void onFinish() {
    }

    public void onPreProcessResponse(ResponseHandlerInterface instance, HttpResponse response) {
    }

    public void onPostProcessResponse(ResponseHandlerInterface instance, HttpResponse response) {
    }

    public abstract void onSuccess(int var1, Header[] var2, byte[] var3);

    public abstract void onFailure(int var1, Header[] var2, byte[] var3, Throwable var4);

    public void onRetry(int retryNo) {
        AsyncHttpClient.log.d("AsyncHttpRH", String.format("Request retry no. %d", new Object[]{Integer.valueOf(retryNo)}));
    }

    public void onCancel() {
        AsyncHttpClient.log.d("AsyncHttpRH", "Request got cancelled");
    }
   
   //...
}
如你所猜测的一样,这些“外部关心的内容”会在handler收到相应的消息是被回调执行。

这时候我们看下面这个类应该比较轻松,默认在系统给应用创建的cache目录中进行缓存,有特殊需求的可以继承该抽象类重写一定的内容。
public abstract class FileAsyncHttpResponseHandler extends AsyncHttpResponseHandler {
    protected final File file;
    protected final boolean append;
    protected final boolean renameIfExists;
    protected File frontendFile;
    private static final String LOG_TAG = "FileAsyncHttpRH";

    public FileAsyncHttpResponseHandler(File file) {
        this(file, false);
    }

    public FileAsyncHttpResponseHandler(File file, boolean append) {
        this(file, append, false);
    }

    public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists) {
        Utils.asserts(file != null, "File passed into FileAsyncHttpResponseHandler constructor must not be null");
        if(!file.isDirectory() && !file.getParentFile().isDirectory()) {
            Utils.asserts(file.getParentFile().mkdirs(), "Cannot create parent directories for requested File location");
        }

        if(file.isDirectory() && !file.mkdirs()) {
            AsyncHttpClient.log.d("FileAsyncHttpRH", "Cannot create directories for requested Directory location, might not be a problem");
        }

        this.file = file;
        this.append = append;
        this.renameIfExists = renameTargetFileIfExists;
    }

    public FileAsyncHttpResponseHandler(Context context) {
        this.file = this.getTemporaryFile(context);
        this.append = false;
        this.renameIfExists = false;
    }

    public boolean deleteTargetFile() {
        return this.getTargetFile() != null && this.getTargetFile().delete();
    }

    protected File getTemporaryFile(Context context) {
        Utils.asserts(context != null, "Tried creating temporary file without having Context");

        try {
            return File.createTempFile("temp_", "_handled", context.getCacheDir());
        } catch (IOException var3) {
            AsyncHttpClient.log.e("FileAsyncHttpRH", "Cannot create temporary file", var3);
            return null;
        }
    }

    protected File getOriginalFile() {
        Utils.asserts(this.file != null, "Target file is null, fatal!");
        return this.file;
    }

    public File getTargetFile() {
        if(this.frontendFile == null) {
            this.frontendFile = this.getOriginalFile().isDirectory()?this.getTargetFileByParsingURL():this.getOriginalFile();
        }

        return this.frontendFile;
    }

    protected File getTargetFileByParsingURL() {
        Utils.asserts(this.getOriginalFile().isDirectory(), "Target file is not a directory, cannot proceed");
        Utils.asserts(this.getRequestURI() != null, "RequestURI is null, cannot proceed");
        String requestURL = this.getRequestURI().toString();
        String filename = requestURL.substring(requestURL.lastIndexOf(47) + 1, requestURL.length());
        File targetFileRtn = new File(this.getOriginalFile(), filename);
        if(targetFileRtn.exists() && this.renameIfExists) {
            String format;
            if(!filename.contains(".")) {
                format = filename + " (%d)";
            } else {
                format = filename.substring(0, filename.lastIndexOf(46)) + " (%d)" + filename.substring(filename.lastIndexOf(46), filename.length());
            }

            int index = 0;

            while(true) {
                targetFileRtn = new File(this.getOriginalFile(), String.format(format, new Object[]{Integer.valueOf(index)}));
                if(!targetFileRtn.exists()) {
                    return targetFileRtn;
                }

                ++index;
            }
        } else {
            return targetFileRtn;
        }
    }

    public final void onFailure(int statusCode, Header[] headers, byte[] responseBytes, Throwable throwable) {
        this.onFailure(statusCode, headers, throwable, this.getTargetFile());
    }

    public abstract void onFailure(int var1, Header[] var2, Throwable var3, File var4);

    public final void onSuccess(int statusCode, Header[] headers, byte[] responseBytes) {
        this.onSuccess(statusCode, headers, this.getTargetFile());
    }

    public abstract void onSuccess(int var1, Header[] var2, File var3);

    protected byte[] getResponseData(HttpEntity entity) throws IOException {
        if(entity != null) {
            InputStream instream = entity.getContent();
            long contentLength = entity.getContentLength();
            FileOutputStream buffer = new FileOutputStream(this.getTargetFile(), this.append);
            if(instream != null) {
                try {
                    byte[] tmp = new byte[4096];
                    int count = 0;

                    int l;
                    while((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {
                        count += l;
                        buffer.write(tmp, 0, l);
                        this.sendProgressMessage((long)count, contentLength);
                    }
                } finally {
                    AsyncHttpClient.silentCloseInputStream(instream);
                    buffer.flush();
                    AsyncHttpClient.silentCloseOutputStream(buffer);
                }
            }
        }

        return null;
    }
}

啰啰嗦嗦的插一句,写成这样都不想插demo了。

上传:

这里就不详细的扒源码了,介绍一下如何使用。我们应该还记得一直出现的RequestParams这个类,库中用它干了很多事情。这里也不例外,我们看一下其中put方法,提供了一套重载
 public void put(String key, String value) {
        if(key != null && value != null) {
            this.urlParams.put(key, value);
        }

    }

    public void put(String key, File[] files) throws FileNotFoundException {
        this.put(key, (File[])files, (String)null, (String)null);
    }

    public void put(String key, File[] files, String contentType, String customFileName) throws FileNotFoundException {
        if(key != null) {
            ArrayList fileWrappers = new ArrayList();
            File[] var6 = files;
            int var7 = files.length;
            int var8 = 0;

            while(true) {
                if(var8 >= var7) {
                    this.fileArrayParams.put(key, fileWrappers);
                    break;
                }

                File file = var6[var8];
                if(file == null || !file.exists()) {
                    throw new FileNotFoundException();
                }

                fileWrappers.add(new RequestParams.FileWrapper(file, contentType, customFileName));
                ++var8;
            }
        }

    }

    public void put(String key, File file) throws FileNotFoundException {
        this.put(key, (File)file, (String)null, (String)null);
    }

    public void put(String key, String customFileName, File file) throws FileNotFoundException {
        this.put(key, (File)file, (String)null, customFileName);
    }

    public void put(String key, File file, String contentType) throws FileNotFoundException {
        this.put(key, (File)file, contentType, (String)null);
    }

    public void put(String key, File file, String contentType, String customFileName) throws FileNotFoundException {
        if(file != null && file.exists()) {
            if(key != null) {
                this.fileParams.put(key, new RequestParams.FileWrapper(file, contentType, customFileName));
            }

        } else {
            throw new FileNotFoundException();
        }
    }

    public void put(String key, InputStream stream) {
        this.put(key, (InputStream)stream, (String)null);
    }

    public void put(String key, InputStream stream, String name) {
        this.put(key, (InputStream)stream, name, (String)null);
    }

    public void put(String key, InputStream stream, String name, String contentType) {
        this.put(key, stream, name, contentType, this.autoCloseInputStreams);
    }

    public void put(String key, InputStream stream, String name, String contentType, boolean autoClose) {
        if(key != null && stream != null) {
            this.streamParams.put(key, RequestParams.StreamWrapper.newInstance(stream, name, contentType, autoClose));
        }

    }

    public void put(String key, Object value) {
        if(key != null && value != null) {
            this.urlParamsWithObjects.put(key, value);
        }

    }

    public void put(String key, int value) {
        if(key != null) {
            this.urlParams.put(key, String.valueOf(value));
        }

    }

    public void put(String key, long value) {
        if(key != null) {
            this.urlParams.put(key, String.valueOf(value));
        }

    }
一看就知道一些和文件相关的了,你可以使用流,也可以直接上File,值得说道的是后端在设计的时候考虑到失败、秒传机制、负载均衡等等情况,可能会在将文件切片的基础上进行设计、也就是说不是一次上传一个大文件、而是按照碎片上传,然后再拼接,例如百度提供的一套机制,这种情况下aah还是可以胜任的。

后记:

关键还是要熟悉啊,现在有些同学追那种很火的框架,那种契合函数响应式编程的确很棒,上手也快,也意味着内部更复杂,不吃透一个东西简直是简直了,仅凭着那种函数响应式编程的风格就妄加评论过于武断了,完全可以自己实现一套注解,把aah加一层封装。
关键还是要熟悉!关键还是要吃透!
想想当初没仔细看aah源码就去写一个复杂的上传模块也是醉了,做了很多没必要的工作,把好些内部已有的东西又变着花样实现了一套。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值