val manager = PakDownloadManager(myDownloader)
val request = DownloadRequest(
uri = Uri.parse("https://cdn.example.com/content.pak"),
destinationFile = File(context.filesDir, "content.pak"),
pakName = "content",
expectedSizeBytes = 52_428_800L,
)
val handle = manager.start(request)
handle.onStateChanged = { state ->
when (state) {
is DownloadState.Downloading -> updateProgressBar(state.progress)
is DownloadState.Ready -> {
val status = PakManager.mount("content", state.file)
if (!status.isOk) handleMountError(status)
}
is DownloadState.Failed -> handleError(state.error)
else -> {}
}
}
class PakDownloadManager(downloader: PakDownloader, minimumDiskSpaceBytes: Long = 100L * 1024 * 1024, executor: Executor = defaultExecutor())
PakDownloadManager
(
downloader
, minimumDiskSpaceBytes
, executor
)
|
Signature
constructor(downloader: PakDownloader, minimumDiskSpaceBytes: Long = 100L * 1024 * 1024, executor: Executor = defaultExecutor()) Parameters minimumDiskSpaceBytes: Long
Minimum free disk space to maintain. Downloads fail with DownloadError.DiskFull if available space drops below this threshold.
executor: Executor
Executor on which downloads run. Defaults to a dedicated cached daemon-thread pool isolated from ForkJoinPool.commonPool so that long-running blocking I/O (network, disk) does not starve other application work that uses the common pool (e.g. parallel streams). Pass your own Executor to control concurrency or share a pool.
Returns PakDownloadManager |
availableDiskSpace
(
destination
)
|
Checks available disk space at the destination path.
Signature
fun availableDiskSpace(destination: File): Long? Parameters destination: FileReturns Long?
available bytes, or null if the path doesn't exist.
|
cleanup
(
pakName
, file
)
|
Deletes the file for PakDownloadManager.cleanup and removes it from tracking.
Side effects:
Safe to call even if no download is active.
Signature
fun cleanup(pakName: String, file: File): Boolean Parameters pakName: Stringfile: FileReturns Boolean
true if the destination file was deleted.
|
cleanupAndAwait
(
pakName
, file
, timeoutMs
)
|
Like PakDownloadManager.cleanup but blocks (up to PakDownloadManager.cleanupAndAwait) waiting for the cancelled download's worker thread to drain before returning. Use this when you're about to immediately re- start() a download for the same destination and want to guarantee the prior worker can no longer be holding any file handles or about to fire late callbacks.
Without this, a cleanup-then-start sequence races: the cancelled worker may still be inside downloader.download(...) when the new download begins, even though PakDownloadManager.cleanup already removed it from internal tracking. The lockedFiles map is identity-keyed so a stale removal cannot clobber a fresh lock, but having two workers racing on the same on-disk path during the brief overlap is still a footgun for filesystem behaviors like Windows file-locking.
Signature
fun cleanupAndAwait(pakName: String, file: File, timeoutMs: Long = 5000): Boolean Parameters pakName: Stringfile: FiletimeoutMs: LongReturns Boolean
true if the destination file was deleted (same semantics as PakDownloadManager.cleanup).
|
cleanupStalePartials
(
directory
, maxAgeMs
)
|
Deletes partial download files that are older than PakDownloadManager.cleanupStalePartials and not currently being written to. Call on app startup to garbage collect abandoned partials from prior sessions that crashed or were killed.
Three kinds of partial files are reaped:
Completed .pak files are NEVER deleted by this method even if they are older than PakDownloadManager.cleanupStalePartials, because completed paks may be mounted or in active use. The caller is responsible for managing the lifecycle of completed paks (e.g. via PakDownloadManager.cleanup).
Signature
fun cleanupStalePartials(directory: File, maxAgeMs: Long): Int Parameters directory: File
Directory to scan for partial files.
maxAgeMs: Long
Maximum age in milliseconds. Partial files with lastModified older than this are deleted.
Returns Int
Number of files deleted.
|
destroy
()
|
Cancels all active downloads and clears internal state.
Signature
fun destroy() |
getHandle
(
pakName
)
|
Returns the active handle for PakDownloadManager.getHandle, or null if no download is in progress.
Signature
fun getHandle(pakName: String): DownloadHandle? Parameters pakName: StringReturns DownloadHandle? |
start
(
request
)
|
Starts a download described by PakDownloadManager.start. Returns a DownloadHandle for observation/control.
If a download for the same DownloadRequest.pakName is already active, returns the existing handle (deduplication).
Pre-flight checks performed before starting:
Validation failures are surfaced through the returned handle's terminal Failed state rather than thrown, so the caller's reactive flow handles them uniformly with runtime errors. The exception is structurally invalid input (a request that can't even be tracked — blank pakName) which is rejected with IllegalArgumentException at call time so the bug surfaces at the offending caller's site, not as a mysterious failed handle later.
Signature
fun start(request: DownloadRequest): DownloadHandle Parameters request: DownloadRequestReturns DownloadHandle |
tryMount
(
pakName
, file
)
|
Convenience method that mounts a validated file via PakManager. The 3p can call PakManager.mount() directly — this adds structured logging and a pre-mount existence check.
Signature
fun tryMount(pakName: String, file: File): PakManager.MountStatus Parameters pakName: Stringfile: FileReturns PakManager.MountStatus PakManager.MountStatus — the raw result from PakManager. Returns PakManager.MountStatus.OpenFailed if the file does not exist at mount time. Note that this conflates "file genuinely missing" with native-side open failures — callers that need to distinguish should perform their own File.exists check before calling.
|
validateFile
(
request
)
|
Validates an existing file against the request constraints (size, pluggable validator) without downloading. Useful after app restart to check if a previously downloaded file is still valid before mounting.
Signature
fun validateFile(request: DownloadRequest): DownloadError? Parameters request: DownloadRequest |