SteadyFetch
Reliable Kotlin download SDK for Android: fast, resumable, parallel downloads with foreground service, storage validation, and checksum protection.
Install / Use
/learn @void-memories/SteadyFetchREADME
SteadyFetch
Reliable parallel + resumable downloads for Android, written in Kotlin.
SteadyFetch is a Kotlin SDK for Android that provides reliable, resumable downloads. It handles chunking, storage checks, notifications, and foreground service requirements so your app can focus on product logic instead of download plumbing.
Table of Contents
- Feature Highlights
- Quick Facts
- Installation
- Quickstart (5 minutes)
- Usage Example
- API Reference
- How It Works (High Level)
- FAQ
- Roadmap
- Contributing
- License
Feature Highlights
- Parallel chunk downloads – Splits files into chunks and fetches them concurrently for faster throughput.
- Foreground-friendly execution – Keeps long transfers alive via a dedicated foreground service + notification flow.
- Resume support – Persists progress and resumes exactly where a transfer stopped (app kill, process death, or network drop).
- Checksum + storage validation – Validates remote metadata and ensures sufficient storage before writing.
- Well-tested core – Unit tests cover the controller, networking layer, chunk math, and error propagation.
Demo
Download surviving app kills + network changes with chunk-level progress:
https://github.com/user-attachments/assets/2b9f9384-eac8-4a22-932f-2f8728a2870b
Quick Facts
- Use case – Resumable downloads with chunk-level progress and safe foreground execution.
- Tech stack – Kotlin, OkHttp, Android Service + Notification APIs.
- Minimum OS – Android 9 (API 28); builds against SDK 36.
- Distribution – Published via JitPack under
dev.namn.steady-fetch:steady-fetch. - Status – Early-stage, actively evolving. APIs may change before
1.0.0.
Installation
Add JitPack once in your root settings.gradle.kts:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven(url = "https://jitpack.io")
}
}
Pull in the dependency in your app module:
dependencies {
implementation("dev.namn.steady-fetch:steady-fetch:<version>")
}
🔎 Latest versions are listed on JitPack:
https://jitpack.io/#void-memories/SteadyFetch
Quickstart (5 minutes)
1. Initialize in your Application
class SteadyFetchApp : Application() {
override fun onCreate() {
super.onCreate()
SteadyFetch.initialize(this)
}
}
2. Queue a download
val downloadId = SteadyFetch.queueDownload(
request = DownloadRequest(
url = "https://files.example.com/iso/latest.iso",
fileName = "latest.iso",
downloadDir = File(context.filesDir, "downloads")
),
callback = object : SteadyFetchCallback {
override fun onSuccess() {
// Update UI, notify user, etc.
}
override fun onUpdate(progress: DownloadProgress) {
// E.g. show progress in a notification or Compose UI
}
override fun onError(error: DownloadError) {
// Log & show an error state
}
}
)
3. Cancel if needed
SteadyFetch.cancelDownload(downloadId)
That’s enough to get a resilient, resumable download up and running.
Usage Example
SteadyFetch is designed to be called from your UI or domain layer, while the heavy lifting runs in a dedicated service.
A typical flow:
- User taps a “Download” button.
- You call
SteadyFetch.queueDownload()with aDownloadRequest. - You observe
SteadyFetchCallback.onUpdate()and update your UI. - On success or error, you update local state / DB accordingly.
Example with a simple wrapper:
fun startIsoDownload(context: Context) {
val downloadsDir = File(context.filesDir, "downloads")
val request = DownloadRequest(
url = "https://files.example.com/iso/latest.iso",
fileName = "latest.iso",
downloadDir = downloadsDir
)
val callback = object : SteadyFetchCallback {
override fun onSuccess() {
// Maybe emit an event to your ViewModel or show a snackbar
}
override fun onUpdate(progress: DownloadProgress) {
// progress.percent, progress.bytesDownloaded, etc.
}
override fun onError(error: DownloadError) {
// Map error to something user-friendly
}
}
SteadyFetch.queueDownload(request, callback)
}
All public entry points live under the dev.namn.steady_fetch package and are backed by unit tests (MockK, Robolectric, MockWebServer).
API Reference
Public APIs
SteadyFetch.initialize(application: Application)
Initializes the SteadyFetch SDK. Must be called once before using any other APIs, typically in your Application.onCreate().
Parameters:
application: Application- Your application instance
Throws: IllegalStateException if initialization fails
SteadyFetch.queueDownload(request: DownloadRequest, callback: SteadyFetchCallback): Long
Enqueues a new download and returns a unique download ID. The download runs asynchronously in a foreground service.
Parameters:
request: DownloadRequest- Download configuration (see Data Models)callback: SteadyFetchCallback- Callback interface for progress updates and completion
Returns: Long - Unique download ID that can be used to cancel the download
Throws: IllegalStateException if SteadyFetch is not initialized
SteadyFetch.cancelDownload(downloadId: Long): Boolean
Cancels an active download by its ID.
Parameters:
downloadId: Long- The download ID returned fromqueueDownload()
Returns: Boolean - true if a download was found and cancelled, false otherwise
Data Models
DownloadRequest
Configuration object for a download request.
data class DownloadRequest(
val url: String, // Required: URL of the file to download
val headers: Map<String, String> = emptyMap(), // Optional: Custom HTTP headers (e.g., auth tokens)
val maxParallelDownloads: Int = 4, // Optional: Max concurrent chunks (1-25, default 4)
val downloadDir: File, // Required: Directory where the file will be saved
val fileName: String, // Required: Name for the final downloaded file
)
Fields:
url- The HTTP/HTTPS URL of the file to downloadheaders- Optional map of HTTP headers (useful for authentication, custom user agents, etc.)maxParallelDownloads- Maximum number of chunks to download concurrently. Must be between 1 and 25. Higher values may improve speed on fast connections but can overwhelm slower networks.downloadDir- The directory where the file will be saved. Must be writable. The directory will be created if it doesn't exist.fileName- The name of the final file after download completes (e.g.,"video.mp4","document.pdf")
SteadyFetchCallback
Interface for receiving download progress updates and completion notifications.
interface SteadyFetchCallback {
fun onSuccess()
fun onUpdate(progress: DownloadProgress)
fun onError(error: DownloadError)
}
Methods:
onSuccess()- Called when the download completes successfully. The file is available atdownloadDir/fileName.onUpdate(progress: DownloadProgress)- Called periodically with progress updates. SeeDownloadProgressbelow for details.onError(error: DownloadError)- Called when the download fails. SeeDownloadErrorbelow for details.
DownloadProgress
Snapshot of the current download state, including overall progress and per-chunk status.
data class DownloadProgress(
val status: DownloadStatus, // Current overall download status
val progress: Float, // Overall progress (0.0 to 1.0)
val chunkProgress: List<DownloadChunkProgress> // Per-chunk progress details
)
Fields:
status- Current status of the download (seeDownloadStatusenum below)progress- Overall progress as a float between 0.0 (0%) and 1.0 (100%)chunkProgress- List of progress information for each chunk (empty for non-chunked downloads)
DownloadChunkProgress
Progress information for a single chunk.
data class DownloadChunkProgress(
val status: DownloadStatus, // Status of this chunk
val name: String, // Chunk filename (e.g., "file.iso.part01")
val progress: Float // Chunk progress (0.0 to 1.0)
)
Fields:
status- Current status of this chunkname- The temporary filename of this chunk (useful for debugging)progress- Progress of this chunk as a float between 0.0 and 1.0
DownloadError
Error information when a download fails.
data class DownloadError(
val code: Int, // Error code
