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上,应用程序必须始终保持活动状态和前景。
ConnectionRequest.retry()
,在下载到达的位置重新启动,而不是从头开始重新启动它吗这在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));
}
});
}