Codenameone 如何改进ConnectionRequest的行为?

Codenameone 如何改进ConnectionRequest的行为?,codenameone,Codenameone,我写了这段代码: public static Slider downloadStorageFile(String url, OnComplete<Integer> percentageCallback, OnComplete<String> filesavedCallback) { final String filename = getNewStorageFilename(); ConnectionRequest cr = Rest.g

我写了这段代码:

public static Slider downloadStorageFile(String url, OnComplete<Integer> percentageCallback, OnComplete<String> filesavedCallback) {

        final String filename = getNewStorageFilename();

        ConnectionRequest cr = Rest.get(url).fetchAsBytes(response -> CN.callSerially(() -> filesavedCallback.completed(filename)));
        cr.setDestinationStorage(filename);

        Slider slider = new Slider();
        slider.addDataChangedListener((int type, int index) -> {
            CN.callSerially(() -> percentageCallback.completed(slider.getProgress()));
        });
        sliderBridgeMap.put(cr, slider);
        SliderBridge.bindProgress(cr, slider);

        return slider;
    }
publicstaticsliderdownloadstoragefile(字符串url、OnComplete percentageCallback、OnComplete filesavedCallback){
最终字符串文件名=getNewStorageFilename();
ConnectionRequest cr=Rest.get(url.fetchAsBytes(response->CN.callSerially(()->filesavedCallback.completed(filename));
cr.setDestinationStorage(文件名);
滑块=新滑块();
slider.addDataChangedListener((int类型,int索引)->{
CN.callSerially(()->percentageCallback.completed(slider.getProgress());
});
滑块桥映射放置(cr,滑块);
SliderBridge.bindProgress(cr,slider);
返回滑块;
}
基本上,正如您所猜测的,它异步下载一个文件,它提供两个将在EDT上运行的回调,并立即返回一个跟踪下载进度的滑块

在Android上,即使应用程序处于后台,下载也会继续并结束。另一方面,在iOS上,应用程序必须始终保持活动状态和前景。

  • 当应用程序进入后台时,是否可以在iOS上完成下载

  • 或者,我可以让我的网络错误处理程序在应用程序在iOS上返回前台时自动调用
    ConnectionRequest.retry()
    ,在下载到达的位置重新启动,而不是从头开始重新启动它吗

  • 更好的是,我能同时得到第1点和第2点吗


  • 这在Android上也是个问题,因为当系统受到限制时,操作系统可能会突然停止您正在进行的下载。例如,在开发者工具中,只要立即打开kill活动,你的下载就会在你最小化应用的那一刻消失

    大多数设备不会这样做,但如果设备在节电模式下运行,可能会发生这种情况

    解决方案是在进入后台时使用后台提取。问题是,这并不能提供常规前台下载所能获得的良好UI,因此您需要选择使用哪一种

    请参见此处类的JavaDoc:


    关于这个主题的一篇稍微过时的博文:

    我想设计一个系统来下载大小文件,这个系统非常健壮,即能够抵抗网络错误,并且能够在网络条件允许的情况下尽快恢复下载,并且对用户完全透明

    然而,这与跨平台方法施加的某些限制相冲突。我不确定后台抓取是下载大量多媒体内容的最合适的解决方案,我也不知道后台抓取中的网络错误是否被通用错误处理程序捕获。也许我会调查一下

    我已经详细阐述了一个有利弊的解决方案,它绕过了这个问题

    优点:始终允许您完成下载,即使下载量非常大(例如100MB),即使连接不稳定(网络错误),即使应用程序暂时在后台运行

    缺点:由于我的想法是将下载分成几个小部分,因此这种方法会导致许多GET请求稍微减慢下载速度,并导致比正常需要更多的流量

    前提条件1:在全局网络错误处理中,必须有一个自动的
    。重试()
    ,如下代码所示:

    先决条件2:有关
    getFileSizeWithoutDownload(字符串url)
    实现和
    Wrapper
    实现,请参阅:

    说明:代码应该是自解释的。基本上,它一次下载512k字节,然后将其与输出合并。如果发生网络错误(如果在iOS上应用程序在后台运行),则已下载的所有内容都不会丢失(最多只会丢失最后的512kbyte片段)。下载每个片段时,ConnectionRequest会调用自身,更改部分下载的标头。
    filesavedCallback
    回调仅在所有下载完成时调用

    代码:

    publicstaticvoiddownloadtostorage(字符串url、OnComplete percentageCallback、OnComplete filesavedCallback)引发IOException{
    最终字符串输出=getNewStorageFilename();//获取新的随机可用存储文件名
    最终长文件大小=getFileSizeWithoutDownload(url);//预期的总下载大小
    final int splittingSize=512*1024;//512 KB,每个小下载的大小
    Wrapper downloadedTotalBytes=新包装(0);
    OutputStream out=Storage.getInstance().createOutputStream(output);//保持打开状态以追加部分下载
    包装器completedPartialDownload=新包装器(0);
    ConnectionRequest cr=new GZConnectionRequest();
    cr.setUrl(url);
    cr.setPost(假);
    如果(文件大小>拆分大小){
    //下载应从哪个字节开始?
    cr.addRequestHeader(“范围”,“字节=0-”+拆分大小);
    cr.setDestinationStorage(“拆分-”+输出);
    }否则{
    使用清理(out);
    cr.setDestinationStorage(输出);
    }
    cr.addResponseListener(a->{
    CN.callSerially(()->{
    试一试{
    //我们将刚刚保存的部分下载附加到输出(如果存在)
    如果(Storage.getInstance()存在(“拆分-”+输出)){
    InputStream in=Storage.getInstance().createInputStream(“拆分-”+输出);
    Util.copyNoClose(输入、输出、8192);
    使用清理(in);
    Storage.getInstance().deleteStorageFile(“拆分-”+输出);
    completedPartialDownload.set(completedPartialDownload.get()+1);
    }
    //下载完成了吗?
    如果(fileSize=fileSize | | d
    
        public static void downloadToStorage(String url, OnComplete<Integer> percentageCallback, OnComplete<String> filesavedCallback) throws IOException {
    
            final String output = getNewStorageFilename(); // get a new random available Storage file name
            final long fileSize = getFileSizeWithoutDownload(url); // total expected download size
            final int splittingSize = 512 * 1024; // 512 kbyte, size of each small download
            Wrapper<Integer> downloadedTotalBytes = new Wrapper<>(0);
            OutputStream out = Storage.getInstance().createOutputStream(output); // leave it open to append partial downloads
            Wrapper<Integer> completedPartialDownload = new Wrapper<>(0);
    
            ConnectionRequest cr = new GZConnectionRequest();
            cr.setUrl(url);
            cr.setPost(false);
            if (fileSize > splittingSize) {
                // Which byte should the download start from?
                cr.addRequestHeader("Range", "bytes=0-"  + splittingSize);
                cr.setDestinationStorage("split-" + output);
            } else {
                Util.cleanup(out);
                cr.setDestinationStorage(output);
            }
            cr.addResponseListener(a -> {
                CN.callSerially(() -> {
                    try {
                        // We append the just saved partial download to the output, if it exists
                        if (Storage.getInstance().exists("split-" + output)) {
                            InputStream in = Storage.getInstance().createInputStream("split-" + output);
                            Util.copyNoClose(in, out, 8192);
                            Util.cleanup(in);
                            Storage.getInstance().deleteStorageFile("split-" + output);
                            completedPartialDownload.set(completedPartialDownload.get() + 1);
                        }
                        // Is the download finished?
                        if (fileSize <= 0 || completedPartialDownload.get() * splittingSize >= fileSize || downloadedTotalBytes.get() >= fileSize) {
                            // yes, download finished
                            Util.cleanup(out);
                            filesavedCallback.completed(output);
                        } else {
                            // no, it's not finished, we repeat the request after updating the "Range" header
                            cr.addRequestHeader("Range", "bytes=" + downloadedTotalBytes.get() + "-" + (downloadedTotalBytes.get() + splittingSize));
                            NetworkManager.getInstance().addToQueue(cr);
                        }
                    } catch (IOException ex) {
                        Log.p("Error in appending splitted file to output file", Log.ERROR);
                        Log.e(ex);
                        Server.sendLogAsync();
                    }
                });
            });
            NetworkManager.getInstance().addToQueue(cr);
            NetworkManager.getInstance().addProgressListener((NetworkEvent evt) -> {
                if (cr == evt.getConnectionRequest() && fileSize > 0) {
                    downloadedTotalBytes.set(completedPartialDownload.get() * splittingSize + evt.getSentReceived());
                    // the following casting to long is necessary when the file is bigger than 21MB, otherwise the result of the calculation is wrong
                    percentageCallback.completed((int) ((long) downloadedTotalBytes.get() * 100 / fileSize));
                }
            });
        }