SkillAgentSearch skills...

SteadyFetch

Reliable Kotlin download SDK for Android: fast, resumable, parallel downloads with foreground service, storage validation, and checksum protection.

Install / Use

/learn @void-memories/SteadyFetch

README

SteadyFetch

Unit Tests JitPack Kotlin Android API License: MIT

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

  1. Feature Highlights
  2. Quick Facts
  3. Installation
  4. Quickstart (5 minutes)
  5. Usage Example
  6. API Reference
  7. How It Works (High Level)
  8. FAQ
  9. Roadmap
  10. Contributing
  11. 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:

  1. User taps a “Download” button.
  2. You call SteadyFetch.queueDownload() with a DownloadRequest.
  3. You observe SteadyFetchCallback.onUpdate() and update your UI.
  4. 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 from queueDownload()

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 download
  • headers - 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 at downloadDir/fileName.
  • onUpdate(progress: DownloadProgress) - Called periodically with progress updates. See DownloadProgress below for details.
  • onError(error: DownloadError) - Called when the download fails. See DownloadError below 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 (see DownloadStatus enum 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 chunk
  • name - 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
View on GitHub
GitHub Stars24
CategoryDevelopment
Updated2mo ago
Forks1

Languages

Kotlin

Security Score

95/100

Audited on Jan 24, 2026

No findings