use of com.codename1.util.EasyThread in project CodenameOne by codenameone.
the class Util method downloadUrlSafely.
/**
* <p>
* Safely download the given URL to the Storage or to the FileSystemStorage:
* this method is resistant to network errors and capable of resume the
* download as soon as network conditions allow and in a completely
* transparent way for the user; note that in the global network error
* handling, there must be an automatic
* <pre>.retry()</pre>, as in the code example below.</p>
* <p>
* This method is useful if the server correctly returns Content-Length and
* if it supports partial downloads: if not, it works like a normal
* download.</p>
* <p>
* Pros: always allows you to complete downloads, even if very heavy (e.g.
* 100MB), even if the connection is unstable (network errors) and even if
* the app goes temporarily in the background (on some platforms the
* download will continue in the background, on others it will be
* temporarily suspended).</p>
* <p>
* Cons: since this method is based on splitting the download into small
* parts (512kbytes is the default), this approach causes many GET requests
* that slightly slow down the download and cause more traffic than normally
* needed.</p>
* <p>
* Usage example:</p>
* <script src="https://gist.github.com/jsfan3/554590a12c3102a3d77e17533e7eca98.js"></script>
*
* @param url
* @param fileName must be a valid Storage file name or FileSystemStorage
* file path
* @param percentageCallback invoked (in EDT) during the download to notify
* the progress (from 0 to 100); it can be null if you are not interested in
* monitoring the progress
* @param filesavedCallback invoked (in EDT) only when the download is
* finished; if null, no action is taken
* @throws IOException
*/
public static void downloadUrlSafely(String url, final String fileName, final OnComplete<Integer> percentageCallback, final OnComplete<String> filesavedCallback) throws IOException {
// Code discussion here: https://stackoverflow.com/a/62137379/1277576
String partialDownloadsDir = FileSystemStorage.getInstance().getAppHomePath() + FileSystemStorage.getInstance().getFileSystemSeparator() + "partialDownloads";
if (!FileSystemStorage.getInstance().isDirectory(partialDownloadsDir)) {
FileSystemStorage.getInstance().mkdir(partialDownloadsDir);
}
// do its best to be unique if there are parallel downloads
final String uniqueId = url.hashCode() + "" + downloadUrlSafelyRandom.nextInt();
final String partialDownloadPath = partialDownloadsDir + FileSystemStorage.getInstance().getFileSystemSeparator() + uniqueId;
// as discussed here: https://stackoverflow.com/a/57984257
final boolean isStorage = fileName.indexOf("/") < 0;
// total expected download size, with a check partial download support
final long fileSize = getFileSizeWithoutDownload(url, true);
// 512 kbyte, size of each small download
final int splittingSize = 512 * 1024;
final Wrapper<Long> downloadedTotalBytes = new Wrapper<Long>(0l);
final OutputStream out;
if (isStorage) {
// leave it open to append partial downloads
out = Storage.getInstance().createOutputStream(fileName);
} else {
out = FileSystemStorage.getInstance().openOutputStream(fileName);
}
// Codename One thread that supports crash protection and similar Codename One features.
final EasyThread mergeFilesThread = EasyThread.start("mergeFilesThread");
final 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.setDestinationFile(partialDownloadPath);
} else {
Util.cleanup(out);
if (isStorage) {
cr.setDestinationStorage(fileName);
} else {
cr.setDestinationFile(fileName);
}
}
cr.addResponseListener(new ActionListener<NetworkEvent>() {
@Override
public void actionPerformed(NetworkEvent evt) {
mergeFilesThread.run(new Runnable() {
@Override
public void run() {
try {
// We append the just saved partial download to the fileName, if it exists
if (FileSystemStorage.getInstance().exists(partialDownloadPath)) {
InputStream in = FileSystemStorage.getInstance().openInputStream(partialDownloadPath);
Util.copyNoClose(in, out, 8192);
Util.cleanup(in);
// before deleting the file, we check and update how much data we have actually downloaded
downloadedTotalBytes.set(downloadedTotalBytes.get() + FileSystemStorage.getInstance().getLength(partialDownloadPath));
FileSystemStorage.getInstance().delete(partialDownloadPath);
}
// Is the download finished?
if (downloadedTotalBytes.get() > fileSize) {
throw new IllegalStateException("More content has been downloaded than the file length, check the code.");
}
if (fileSize <= 0 || downloadedTotalBytes.get() == fileSize) {
// yes, download finished
Util.cleanup(out);
if (filesavedCallback != null) {
CN.callSerially(new Runnable() {
@Override
public void run() {
filesavedCallback.completed(fileName);
}
});
}
} else {
// no, it's not finished, we repeat the request after updating the "Range" header
cr.addRequestHeader("Range", "bytes=" + downloadedTotalBytes.get() + "-" + (Math.min(downloadedTotalBytes.get() + splittingSize, fileSize)));
NetworkManager.getInstance().addToQueue(cr);
}
} catch (IOException ex) {
Log.p("Error in appending splitted file to output file", Log.ERROR);
Log.e(ex);
Log.sendLogAsync();
}
}
});
}
});
NetworkManager.getInstance().addToQueue(cr);
NetworkManager.getInstance().addProgressListener(new ActionListener<NetworkEvent>() {
@Override
public void actionPerformed(NetworkEvent evt) {
if (cr == evt.getConnectionRequest() && fileSize > 0) {
// the following casting to long is necessary when the file is bigger than 21MB, otherwise the result of the calculation is wrong
if (percentageCallback != null) {
CN.callSerially(new Runnable() {
@Override
public void run() {
percentageCallback.completed((int) ((long) downloadedTotalBytes.get() * 100 / fileSize));
}
});
}
}
}
});
}
use of com.codename1.util.EasyThread in project CodenameOne by codenameone.
the class SynchronizedTest method run.
public void run() {
EasyThread thread = EasyThread.start("Thread1");
thread.run(() -> runTest1());
runTest1();
Log.p("TEST 1 COMPLETE");
thread.run(() -> runTest2());
runTest2();
Log.p("TEST 2 COMPLETE");
thread.run(() -> runTest3());
runTest3();
Log.p("TEST 3 COMPLETE");
thread.run(() -> runTest4());
runTest4();
Log.p("TEST 4 COMPLETE");
thread.run(() -> runTest5());
runTest5();
Log.p("TEST 5 COMPLETE");
}
Aggregations