{"id":16558192,"url":"https://github.com/tonyofrancis/dispatcher","last_synced_at":"2025-07-30T13:33:50.670Z","repository":{"id":87788436,"uuid":"175063060","full_name":"tonyofrancis/Dispatcher","owner":"tonyofrancis","description":"Dispatcher is a simple and flexible work scheduler that schedulers work on a background or UI thread correctly in the form of Dispatch using the android.os.Handler class.","archived":false,"fork":false,"pushed_at":"2019-07-11T00:53:35.000Z","size":525,"stargazers_count":20,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"v1","last_synced_at":"2025-04-08T23:27:54.274Z","etag":null,"topics":["android","androidlibrary","api","downloader","downloadmanager","handlerthread","java","kotlin","kotlin-android","observable","okhttp","retrofit2","rxandroid","rxjava","rxjava2","simple","threading"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tonyofrancis.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-11T18:43:44.000Z","updated_at":"2024-11-16T07:50:05.000Z","dependencies_parsed_at":"2023-07-21T20:06:26.229Z","dependency_job_id":null,"html_url":"https://github.com/tonyofrancis/Dispatcher","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/tonyofrancis/Dispatcher","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyofrancis%2FDispatcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyofrancis%2FDispatcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyofrancis%2FDispatcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyofrancis%2FDispatcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tonyofrancis","download_url":"https://codeload.github.com/tonyofrancis/Dispatcher/tar.gz/refs/heads/v1","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tonyofrancis%2FDispatcher/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267875633,"owners_count":24158781,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","androidlibrary","api","downloader","downloadmanager","handlerthread","java","kotlin","kotlin-android","observable","okhttp","retrofit2","rxandroid","rxjava","rxjava2","simple","threading"],"created_at":"2024-10-11T20:09:52.279Z","updated_at":"2025-07-30T13:33:50.628Z","avatar_url":"https://github.com/tonyofrancis.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"[ ![Download](https://api.bintray.com/packages/tonyofrancis/maven/dispatch/images/download.svg?version=1.4.10) ](https://bintray.com/tonyofrancis/maven/dispatch/1.4.10/link)\n[![Build Status](https://travis-ci.org/tonyofrancis/Dispatcher.svg?branch=v1)](https://travis-ci.org/tonyofrancis/Dispatcher)\n[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/tonyofrancis/Fetch/blob/master/LICENSE)\n# DispatchQueue: A simple work scheduler for Java, Kotlin and Android\n\nDispatchQueue is a simple and flexible work scheduler that schedulers work on a background or main thread in the form of a dispatch queue.\n\n```java\nDispatchQueue.background\n    .async {\n        //do background work here\n        val sb = StringBuilder()\n        for (i in 0..100) {\n            sb.append(i)\n              .append(\" \")\n        }\n        sb.toString()\n    }\n    .post { data -\u003e\n        //do ui work here\n        println(data)\n    }\n    .start()\n```\nDispatchQueue makes it very clear which thread your code is running on. An Async block will always run on a background thread. A post block will always run on the ui thread. Like what you see? Read on!\n\nOne of the many problems with offloading work to a background thread in Java and Android programming, is knowing the right time to cancel the work when it is no longer needed. DispatchQueue makes it very easy to cancel a dispatch queue. Simply call the `cancel()` method on the queue. If that is not good enough, allow your component's lifecycle to manage this for you.\nAndroid Activity Example:\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        DispatchQueue.io\n            .managedBy(this)\n            .async {\n                //do work in background\n            }\n            .post {\n                //handle results on ui\n            }\n            .start()\n    }\n\n}\n```\nIn the above example, the queue is managed by the Activity’s life cycle, and it will be cancelled when the Activity is destroyed. What if you want to control the cancellation of the queue when the Activity pauses or stops? Sure you can! Like so:\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    override fun onResume() {\n        super.onResume()\n        DispatchQueue.io\n            .managedBy(this, CancelType.PAUSED)\n            .async {\n                //do work in background\n            }\n            .post {\n                //handle results on ui\n            }\n            .start()\n    }\n\n}\n```\nIn this example, the queue is canceled when the Activity’s onPause method is called. There is no need to store the queue in a variable and cancel in manually in a callback method. You can if you want. The choice is yours.\n\nDispatchQueue uses a `DispatchQueueController` to manage when a queue is canceled. There are many variations of the DispatchQueueController : `LifecycleDispatchQueueController` and `ActivityDispatchQueueController`. You can extend any of those classes to create your own queue controllers and set them on a queue.\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    private val dispatchQueueController = DispatchQueueController()\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        DispatchQueue.background\n            .managedBy(dispatchQueueController)\n            .async {\n                //do work in background\n            }\n            .post {\n                //handle results on ui\n            }\n            .start()\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        dispatchQueueController.cancelAllDispatchQueues()\n    }\n\n}\n```\nManaging queues could not be easier.\n### Queue Types\n\nDispatchQueue comes with many pre-exiting queues:\n```java\nDispatchQueue.background\n\nDispatchQueue.io\n\nDispatchQueue.network\n\nDispatchQueue.test - // Used specifically for testing\n\n```\nThese queues are generated only when you need/access them. You can also create you own dispatch queues via the many static create methods on the DispatchQueue.Queue class. If using Kotlin, you can call `DispatchQueue.create` directly.\n```java\nDispatchQueue.createDispatchQueue()\n\nDispatchQueue.createDispatchQueue(ThreadType.NEW)\n\nDispatchQueue.createIntervalDispatchQueue(delayInMillis = 1_000)\n\nDispatchQueue.createTimerDispatchQueue(delayInMillis = 10_000)\n```\nThese are just some of the queues you can create.\n### Network Queues with Retrofit\n\nWe all know and love the [Retrofit](https://square.github.io/retrofit/) library created by the wonderful people at [Square](https://squareup.com/us/en). DispatchQueue works seamlessly with your Retrofit code! Let’s walkthrough a simple service example.\n\n*TestJsonData.kt*\n```java\nclass TestJsonData {\n\n    var id: Int = 0\n\n    var nm: String = \"\"\n\n    var cty: String = \"\"\n\n    override fun toString(): String {\n        return \"TestJsonData(id=$id, nm='$nm', cty='$cty')\"\n    }\n\n}\n```\n*TestService.kt*\n```java\ninterface TestService {\n\n    @GET(\"/api/data?list=englishmonarchs\u0026format=json\")\n    fun getSampleJson(): DispatchQueue\u003cList\u003cTestJsonData\u003e\u003e\n\n}\n```\n*SimpleActivity.kt*\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    private lateinit var retrofit: Retrofit\n    private lateinit var service: TestService\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        val dispatchQueueCallAdapterFactory = DispatchQueueCallAdapterFactory.create()\n\n        retrofit = Retrofit.Builder()\n            .addConverterFactory(GsonConverterFactory.create())\n            .addCallAdapterFactory(dispatchQueueCallAdapterFactory)\n            .baseUrl(\"http://mysafeinfo.com\")\n            .build()\n        service = retrofit.create(TestService::class.java)\n\n        runTestService()\n    }\n\n    private fun runTestService() {\n        service.getSampleJson()\n            .managedBy(this)\n            .post { data -\u003e\n                for (testJsonData in data) {\n                    println(testJsonData)\n                }\n            }\n            .start()\n    }\n\n}\n```\nSee how super easy it is to integrate DispatchQueue with Retrofit? All you need is a `DispatchQueueCallAdapterFactory` instance, and set your Service methods to return the data wrapped in a DispatchQueue object.\n### Zipping Queues\n\nThere will be times when you would like to combine the results of two or more queues. Call the zip methods to do this.\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    private val lifecycleDispatchQueueController = LifecycleDispatchQueueController()\n\n    override fun onResume() {\n        super.onResume()\n        DispatchQueue.background\n            .managedBy(lifecycleDispatchQueueController, CancelType.PAUSED)\n            .async {\n                mapOf(0 to \"cat\", 1 to \"bat\")\n            }\n            .zip(getDataDispatchQueue()) // combine two queue results\n            .async { results -\u003e\n                for ((key, value) in results.first) {\n                    println(\"$key:$value\")\n                }\n                for (string in results.second) {\n                    println(string)\n                }\n            }\n            .start()\n    }\n\n    private fun getDataDispatchQueue(): DispatchQueue\u003cList\u003cString\u003e\u003e {\n        return Dispatcher.background\n            .async {\n                listOf(\"hat\", \"sat\")\n            }\n    }\n\n    override fun onPause() {\n        super.onPause()\n        lifecycleDispatchQueueController.cancelAllPaused()\n    }\n\n}\n```\n### Handling Errors\n\nDispatchQueue allows you to handles errors in many ways. One way is setting an error handler for the queue by passing it to the start method.\n```java\nDispatchQueue.createDispatchQueue()\n    .async {\n        //do work\n        val number = 66\n        throw Exception(\"silly exception\")\n        number\n    }\n    .post { number -\u003e\n        println(\"number is $number\")\n    }\n    .start(DispatchQueueErrorCallback { error -\u003e\n        //handle queue error here.\n        Log.e(\"errorTest\",\n            \"queue with id ${error.dispatchQueue.id} throw error:\", error.throwable)\n    })\n```\n**Note** in this example the post block is never executed. It can’t because the async block was not able to provide it with the data need. So the queue calls the error handler and then cancels.\n\nAnother way to handle errors more elegantly, is to provide a `doOnError` block that can return a default or valid data for the preceding async or post block and allowing the execution of the following async or post blocks.\n```java\nDispatchQueue.createDispatchQueue()\n    .async {\n        //do work\n        val number = 66\n        throw Exception(\"silly exception\")\n        number\n    }\n    .doOnError { throwable -\u003e\n        if (throwable.message == \"silly exception\") {\n            100\n        } else {\n            0\n        }\n    }\n    .post { number -\u003e\n        println(\"number is $number\")\n    }\n    .start()\n```\nIn the above example, the `doOnError` block handles the exception for the preceding async block allowing the post block to be called and the queue terminates normally. It is always good practice to provide a queue with an error handler via the start method to handle errors that were not caught in the `doOnError` blocks.\n\nIf an error handler is not provided for the queue, the exception will be thrown causing the application to crash. To prevent this, the library allows you to provide a global error handler that will catch all exceptions thrown when using any dispatch queue. It is best practice to handle errors locally close to the location where they originated. Set the global error handler like this:\n```java\n DispatchQueue.globalSettings.dispatchQueueErrorCallback = DispatchQueueErrorCallback { error -\u003e\n            //handle errors\n}\n```\n\n### Debugging\n\nFiguring out where an error occurred is not always easy. This is one of the areas DispatchQueue shines. DispatchQueue allows you to set the block label for each post and async block via the `setBlockLabel(label)` method. With this information, you can check the dispatch queue id and know exactly where the issue occurred. The following example shows how this is done.\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        DispatchQueue.background\n            .managedBy(this)\n            .async {\n                //do work\n                val number = 66\n                throw Exception(\"silly exception\")\n                number\n            }\n            .setBlockLabel(\"numberAsync\")\n            .post { number -\u003e\n                println(\"number is $number\")\n            }\n            .setBlockLabel(\"printAsync\")\n            .start(DispatchQueueErrorCallback { error -\u003e\n                  if (error.blockLabel == \"numberAsync\") {\n                    //error occurred in first async block.\n                 }\n            })\n    }\n\n}\n```\nYou can also enable logging in the library. This will warm you when you forget to manage a queue with a `DispatchQueueController`.\n```java\nDispatchQueue.globalSettings.enableLogWarnings = true\n```\n### Dispatch Queue Observers\n\nHey kids! Here! Have more ice-cream!\n\nDispatchQueue does not stop at solving threading problems. Introducing `DispatchQueueObserver`! Every now and then you would like a callback from the dispatch queue object that returns a result without it being directly available. That’s where DispatchQueueObservers come into play. You can attach a `DispatchQueueObserver` to a dispatch queue object and get a callback when the return value is available.\n```java\nclass SimpleActivity: AppCompatActivity() {\n\n    private var n = 0\n\n    private val dispatchQueueObserver = object: DispatchQueueObserver\u003cInt\u003e {\n        override fun onChanged(data: Int) {\n            print(\"Factorial of $n is: $data\")\n        }\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        n = 16\n\n        DispatchQueue.background\n            .managedBy(this)\n            .async {\n                factorial(n)\n            }\n            .addObserver(dispatchQueueObserver)\n            .start()\n    }\n\n    private fun factorial(n: Int): Int {\n        if (n == 0) return 1\n        return n * factorial(n - 1)\n    }\n\n}\n```\n### Delays\n\nBoth the async and post methods allow you to specify a delay in milliseconds before the block is executed.\n```java\nDispatchQueue.background\n    .post(5000) {\n        //will be called after a 5 second delay\n    }.start()\n```\n### Thread Handlers\n\nSometimes you would like to dictate the thread that DispatchQueue uses to process blocks in the background. The library allows you to do so by providing your own Thread Handlers. Simply extend the ThreadHandler class, or use the DefaultThreadHandler and AndroidThreadHandler classes.\n```java\nval threadHandler = DefaultThreadHandler(\"MyThreadHandler\")\nval androidThreadHandler = AndroidThreadHandler(\"MyAndroidThreadHandler\")\n\nDispatchQueue.createDispatchQueue(threadHandler)\n    .async {\n        //do work on my own thread\n    }\n    .start()\n```\n### Understanding how DispatchQueue Works\n\nNow that you have seen many of library’s features, it is time to give you a short summary about how it really works. You can skip this section and head to the following section on how to add DispatchQueue to your Android and Java projects.\n\n![alt text](https://cdn-images-1.medium.com/max/800/1*C8xQEB-0U35MbDQ1W6Pq5g.png \"Simple Dispatch Diagram\")\n\n\nThe above is a simple diagram on how a dispatch queue works. When you create a queue it returns a dispatch queue object. The dispatch queue object is responsible for the thread it performs its work on, performing the work, and returning the results. The async, post and map blocks each create a new dispatch queue object and adds it to the dispatch queue when called. Hence the reason you are able to chain dispatch queue objects and pass along their results to the next dispatch queue block in the queue. The overhead for creating dispatch objects are minimal. Each dispatch queue object can have its own `doOnError` handler block and manage its own DispatchQueueObservers.\n\n**Note**: Once a Dispatch queue has completed its work, it is then cancelled and cannot be reused.\n### Using Dispatch\n\nTo use the DispatchQueue library in your project, add the following code to your project’s build.gradle file.\n```java\nimplementation \"com.tonyodev.dispatch:dispatch:1.4.10\"\n```\nFor Android also add:\n```java\nimplementation \"com.tonyodev.dispatch:dispatch-android:1.4.10\"\n```\nTo use Dispatch with Retrofit, add:\n```java\nimplementation \"com.tonyodev.dispatch:dispatch-retrofit2-adapter:1.4.10\"\n```\n\nAndroid users also have to initialize the AndroidDispatchQueues on application launch.\n```java\nimport com.tonyodev.dispatchandroid.initAndroidDispatchQueues\n\nclass App: Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n        initAndroidDispatchQueues() // Java DispatchQueueAndroid.initAndroidDispatchQueues()\n    }\n\n}\n```\n\nContribute\n----------\n\nDispatchQueue can only get better if you make code contributions. Found a bug? Report it.\nHave a feature idea you'd love to see in DispatchQueue? Contribute to the project!\n\n\nLicense\n-------\n\n```\nCopyright (C) 2017 Tonyo Francis.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n\thttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonyofrancis%2Fdispatcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftonyofrancis%2Fdispatcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftonyofrancis%2Fdispatcher/lists"}