将任意文件下载到 Android 和 iOS 缓存

Download arbitrary files to Android and iOS cache

我写了如下方法:

    /**
     * Downloads an arbitrary file to the cache asynchronously, if the current
     * platform has a cache path, or to the app home; if the file was previously
     * downloaded and if it's still available on the cache, it calls the
     * onSuccess callback immediatly.More info:
     * https://www.codenameone.com/blog/cache-sorted-properties-preferences-listener.html
     *
     * @param url The URL to download.
     * @param extension you can leave it empty or null, however iOS cannot play
     * videos without extension (
     * @param onSuccess Callback invoked on successful completion (on EDT by
     * callSerially).
     * @param onFail Callback invoked on failure (on EDT by callSerially).
     */
    public static void downloadFileToCache(String url, String extension, SuccessCallback<String> onSuccess, Runnable onFail) {
        FileSystemStorage fs = FileSystemStorage.getInstance();

        if (extension == null) {
            extension = "";
        }
        if (extension.startsWith(".")) {
            extension = extension.substring(1);
        }
        String name = "cache_" + HashUtilities.sha256hash(url);
        if (!extension.isEmpty()) {
            name += "." + extension;
        }

        String filePath;
        if (fs.hasCachesDir()) {
            // this is supported by Android, iPhone and Javascript
            filePath = fs.getCachesDir() + fs.getFileSystemSeparator() + name;
        } else {
            // The current platform doesn't have a cache path (for example the Simulator)
            String homePath = fs.getAppHomePath();
            filePath = homePath + fs.getFileSystemSeparator() + name;
        }

        // Was the file previously downloaded?
        if (fs.exists(filePath)) {
            CN.callSerially(() -> onSuccess.onSucess(filePath));
        } else {
            Util.downloadUrlToFileSystemInBackground(url, filePath, (evt) -> {
                if (fs.exists(filePath)) {
                    CN.callSerially(() -> onSuccess.onSucess(filePath));
                } else {
                    CN.callSerially(onFail);
                }
            });
        }
    }

有效。它类似于 Util class, but with two main differences: the first is that the Util class provides methods only to download images to the cache, while I want to download arbitrary files; the second is that I can assume that the same url always returns the same file, so I don't need to download it again if it's still in the cache (while the Util 方法提供的一些方法总是在调用时下载文件)。

不过,我有些疑惑。

  1. 我的第一个问题是关于缓存的工作原理:目前我正在使用这种方法下载图像和视频以缓存(在聊天应用程序中),假设我不需要关心当文件不再需要时,因为 OS 会自动删除它们。是这样吗? OS 是否有可能在我使用文件时删除它们(例如在将它们存储到缓存后立即删除),或者 Android 和 iOS 只删除旧文件?

  2. 我写这个方法是为了存储任意文件。我们可以在缓存中存储的文件大小是否有合理的 MB 限制?

  3. 最后,我对方法中使用的callSerially有疑问。以前我没有使用它,但我得到了奇怪的结果:我的回调执行 UI 操作并且经常(但不总是)出现问题。我通过添加 callSerially 解决了所有回调问题,因此 callSerially 是解决方案。但为什么?奇怪的是 Util.downloadUrlToFileSystemInBackgroundActionListener 是由 ConnectionRequest 实例的 addResponseListener(callback) 在幕后调用的,所以回调已经在 EDT 中调用了(根据javadoc)。可以肯定的是,我在回调中测试了 CN.isEdt() 而没有添加 callSerially,它返回了 true,所以理论上 callSerially 不是必需的,但实际上是。我的推理有什么问题?

感谢您的解释。

  1. 据我所知,缓存目录只是OS如果需要space可以删除的目录。我认为它不会为活动的前台应用程序删除它,但这可能会有所不同。

  2. 除了存储之外没有其他限制。但是您仍然需要考虑 OS 不会只为您清理该目录。它只会在存储空间非常低时刷新它,即使那样也不总是。所以你还是需要负责任的存储数据。

  3. 我觉得只有第一个callSeially有影响。它将结果推迟到下一个 EDT 循环,而不是在现有线程中继续。