简体   繁体   中英

Error in Listening exoDownloadManager : Another SimpleCache instance uses the folder

Recently i have succeded to implement downloading media of exoplayer. Here is the picture that illustrates needed classes to implement downloading media using exoplayer.

在此处输入图像描述

And according to suggestion of a fellow developer in medium and i quote:

In order to make the DownloadManager be shared across all components of the app you can initialize it a custom application class. Exoplayer also advised that ExoDatabaseProvider should be a singleton and that download cache should not evict media.

Everything is working the way they should. Here Is my code in full..

DownloadService class:

public class ExoDownloadService extends DownloadService {
private final String TAG = "serviceTAG";


public ExoDownloadService() {
    super(
            1,
            DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL,
            "download_channel",
            R.string.app_name, 0);
}


@Override
protected DownloadManager getDownloadManager() {
    final DownloadManager downloadManager = downloadManager();

    downloadManager.addListener(
            new TerminalStateNotificationHelper(
                    this, getDownloadNotificationHelper(this), 2));
    return downloadManager;
}

@Inject
DownCache downCache;


private DownloadManager downloadManager() {
    return ((HiltApplication) getApplication()).downloadMngContainer.getDownloadManager();
}


DownloadNotificationHelper downloadNotificationHelper;

public DownloadNotificationHelper getDownloadNotificationHelper(Context context) {
    if (downloadNotificationHelper == null) {
        downloadNotificationHelper =
                new DownloadNotificationHelper(context, "download_channel");
    }
    return downloadNotificationHelper;
}

@Nullable
@Override
protected Scheduler getScheduler() {
    return Util.SDK_INT >= 21 ? new PlatformScheduler(this, 1) : null;
}

@Override
protected Notification getForegroundNotification(List<Download> downloads, int notMetRequirements) {
    final DownloadNotificationHelper downloadNotificationHelper =
            new DownloadNotificationHelper(this, "download_channel");
    return downloadNotificationHelper.buildProgressNotification(
            this,
            R.drawable.ic_download,
            null,
            null,
            downloads,
            notMetRequirements);
}

private static final class TerminalStateNotificationHelper implements DownloadManager.Listener {

    private final Context context;
    private final DownloadNotificationHelper notificationHelper;

    private int nextNotificationId;

    public TerminalStateNotificationHelper(
            Context context, DownloadNotificationHelper notificationHelper, int firstNotificationId) {
        this.context = context.getApplicationContext();
        this.notificationHelper = notificationHelper;
        nextNotificationId = firstNotificationId;
    }

    @Override
    public void onDownloadChanged(DownloadManager downloadManager, Download download, @Nullable Exception finalException) {
        Notification notification;
        if (download.state == Download.STATE_COMPLETED) {
            notification =
                    notificationHelper.buildDownloadCompletedNotification(
                            context,
                            R.drawable.ic_download_done,
                            /* contentIntent= */ null,
                            Util.fromUtf8Bytes(download.request.data));

        } else if (download.state == Download.STATE_FAILED) {
            notification =
                    notificationHelper.buildDownloadFailedNotification(
                            context,
                            R.drawable.ic_download_done,
                            /* contentIntent= */ null,
                            Util.fromUtf8Bytes(download.request.data));
        } else {
            return;
        }
        NotificationUtil.setNotification(context, nextNotificationId++, notification);
    }
}

}

DownloadManager in application..

public class HiltApplication extends Application {
public DownloadManagerContainer downloadMngContainer;
private final String TAG = "hiltTAG";

@Override
public void onCreate() {
    super.onCreate();
    downloadMngContainer = new DownloadManagerContainer(getApplicationContext());
   }
 }

DownloadManagerContainer class code..

public class DownloadManagerContainer {
private final Context context;
private DataSource.Factory httpDataSourceFactory;
private DownloadManager downloadManager;

public DownloadManagerContainer(final Context context) {
    this.context = context;
}

public DownloadManager getDownloadManager() {
    if (downloadManager == null) {
        downloadManager = new DownloadManager(
                context,
                getDatabaseProvider(context),
                DownCache.newInstance(context),
                getHttpDataSourceFactory(), //upstreamFactory
                Executors.newFixedThreadPool(6));
    }
    return downloadManager;
}

private DatabaseProvider getDatabaseProvider(Context context) {
    return new StandaloneDatabaseProvider(context);
}

public DataSource.Factory getHttpDataSourceFactory() {
    if (httpDataSourceFactory == null) {
        @Nullable CronetEngine cronetEngine = CronetUtil.buildCronetEngine(context);
        if (cronetEngine != null) {
            httpDataSourceFactory = new CronetDataSource.Factory(cronetEngine, Executors.newSingleThreadExecutor());
        }
        if (httpDataSourceFactory == null) {
            final CookieManager cookieManager = new CookieManager();
            cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
            CookieHandler.setDefault(cookieManager);
            httpDataSourceFactory = new DefaultHttpDataSource.Factory();
        }
    }
    return httpDataSourceFactory;
}

}

DownCache class as singleton is:

public class DownCache {
private static Cache downCache;

public static Cache newInstance(final Context context) {
    if (downCache == null) {
        final File downloadContentDirectory = new File(getDownloadDirectory(context), "downloads");
        downCache = new SimpleCache(downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider(context));
    }
    return downCache;
}


private static File getDownloadDirectory(Context context) {
    File downloadDirectory = context.getExternalFilesDir(null);
    if (downloadDirectory == null) {
        downloadDirectory = context.getFilesDir();
    }
    return downloadDirectory;
}

private static DatabaseProvider getDatabaseProvider(Context context) {
    return new StandaloneDatabaseProvider(context);
}

}

The Problem:

When in my fragment that i trigger download i also listen to downloadmanager to update UI: Here is my fragment code..

private void downloadMedia() {

    final Requirements requirements = new Requirements(Requirements.DEVICE_STORAGE_NOT_LOW);
    final boolean isRequired = requirements.checkRequirements(requireContext());
    DownloadService.sendSetRequirements(
            requireContext(),
            ExoDownloadService.class,
            requirements,
            isForeGround
    );
    Log.d(TAG, "Download Requirement Status Is : " + isRequired);
    if (isRequired) {
        try {
            final DownloadRequest request = new DownloadRequest.Builder(downloadID, Uri.parse(vidUrl)).build();
            DownloadService.sendAddDownload(requireContext(), ExoDownloadService.class, request, true);
        } catch (Exception e) {
            Log.d(TAG, "downloadMedia Error Is : " + e.getMessage());
        }
    }
    downloadListener();
}


private boolean isDownloading = true;

private void downloadListener() {
    final DownloadManager downloadManager = ((HiltApplication) requireActivity().getApplication()).downloadMngContainer.getDownloadManager();
    displayDownloadProgress(downloadManager);

    downloadManager.addListener(new DownloadManager.Listener() {
        @Override
        public void onInitialized(DownloadManager downloadManager) {
            DownloadManager.Listener.super.onInitialized(downloadManager);
            Log.d(TAG, "This Is onInitialized()");
        }

        @Override
        public void onDownloadsPausedChanged(DownloadManager downloadManager, boolean downloadsPaused) {
            DownloadManager.Listener.super.onDownloadsPausedChanged(downloadManager, downloadsPaused);
            Log.d(TAG, "This Is onDownloadsPausedChanged()");
        }

        @Override
        public void onDownloadChanged(DownloadManager downloadManager, Download download, @Nullable Exception finalException) {
            DownloadManager.Listener.super.onDownloadChanged(downloadManager, download, finalException);
            Log.d(TAG, "This Is onDownloadChanged()");
        }

        @Override
        public void onDownloadRemoved(DownloadManager downloadManager, Download download) {
            DownloadManager.Listener.super.onDownloadRemoved(downloadManager, download);
            Log.d(TAG, "This Is onDownloadRemoved()");
        }

        @Override
        public void onIdle(DownloadManager downloadManager) {
            DownloadManager.Listener.super.onIdle(downloadManager);
            Log.d(TAG, "This Is onIdle()");
            isDownloading = false;
        }

        @Override
        public void onRequirementsStateChanged(DownloadManager downloadManager, Requirements requirements, int notMetRequirements) {
            DownloadManager.Listener.super.onRequirementsStateChanged(downloadManager, requirements, notMetRequirements);
            Log.d(TAG, "This Is onRequirementsStateChanged()");
        }

        @Override
        public void onWaitingForRequirementsChanged(DownloadManager downloadManager, boolean waitingForRequirements) {
            DownloadManager.Listener.super.onWaitingForRequirementsChanged(downloadManager, waitingForRequirements);
            Log.d(TAG, "This Is onWaitingForRequirementsChanged()");
        }
    });
}

private void displayDownloadProgress(DownloadManager downloadManager) {
    final ExecutorService thread = Executors.newSingleThreadExecutor();
    final Handler handler = new Handler(Looper.getMainLooper());

    thread.execute(() -> {
        final int size = downloadManager.getCurrentDownloads().size();
        Log.d(TAG, "Download List Is : " + downloadManager.getCurrentDownloads().size());
        while (isDownloading) {
            try {
                final float percent = downloadManager.getCurrentDownloads().get(size - 1).getPercentDownloaded();
                final String percentStr = Float.toString(percent);
                handler.post(() -> {
                    binding.tvDownPercent.setText(percentStr);
                    Log.d(TAG, "Percentage Is : " + percentStr);
                });

            } catch (Exception e) {
                Log.d(TAG, "displayDownloadProgress Error Is : " + e.getMessage());
            }

        }
    });
}

i face this ERROR which says: java.lang.IllegalStateException: Another SimpleCache instance uses the folder:* poiting to this line of code in "DownCache.class"

downCache = new SimpleCache(downloadContentDirectory, new NoOpCacheEvictor(), getDatabaseProvider(context));

Here it says we should use SimpleCache as singleton, i did the precise instruction and yet facing the same Error. Where did i do wrong??

I have faced the same problem before.
My situation is when I tried to download the same audio second times, the simpleCache used at same file directory path is not released.

The answer that using SimpleCache as singleton you mentioned about, there is create a cache file path with getCacheDir() , not a specified file location.

If you want to use a specified file path, you'll need to release the SimpleCache object you used before.

simpleCache.release();

In my case, I create a array to save the downloading simpleCache...

private static SparseArray<SimpleCache> mediaCaches;

//Need to release the Cache that use in the same file path.
SimpleCache simpleCache = CacheUtil.getMediaCache(downloadMediaId);
if (simpleCache != null) {
    simpleCache.release();
}

simpleCache = new SimpleCache(file, new NoOpCacheEvictor(), new ExoDatabaseProvider(context));

//when I download a new audio episode, save with key:episodeId & value:simpleCahe
mediaCaches.put(downloadMediaId, mediaCache);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM