├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── encodings.xml ├── gradle.xml ├── runConfigurations.xml └── vcs.xml ├── .travis.yml ├── CHANGELOG ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tonyodev │ │ └── dispatcherapp │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tonyodev │ │ │ └── dispatcherapp │ │ │ ├── ActivityTwo.java │ │ │ ├── App.kt │ │ │ ├── MainActivity.kt │ │ │ ├── TestJsonData.kt │ │ │ └── TestService.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_two.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── network_security_config.xml │ └── test │ └── java │ └── com │ └── tonyodev │ └── dispatcherapp │ └── ExampleUnitTest.kt ├── build.gradle ├── dispatch ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tonyodev │ │ └── dispatch │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tonyodev │ │ │ └── dispatch │ │ │ ├── DispatchQueue.kt │ │ │ ├── DispatchQueueError.kt │ │ │ ├── DispatchQueueErrorCallback.java │ │ │ ├── DispatchQueueObservable.kt │ │ │ ├── DispatchQueueObserver.kt │ │ │ ├── Settings.kt │ │ │ ├── ThreadType.kt │ │ │ ├── internals │ │ │ ├── DispatchQueueImpl.kt │ │ │ └── DispatchQueueInfo.kt │ │ │ ├── queuecontroller │ │ │ ├── CancelType.kt │ │ │ ├── DispatchQueueController.kt │ │ │ └── LifecycleDispatchQueueController.kt │ │ │ ├── thread │ │ │ ├── DefaultThreadHandler.kt │ │ │ ├── DefaultThreadHandlerFactory.kt │ │ │ ├── DefaultThreadHandlerUtils.kt │ │ │ ├── TestThreadHandler.kt │ │ │ ├── ThreadHandler.kt │ │ │ ├── ThreadHandlerFactory.kt │ │ │ └── ThreadHandlerQueueItem.kt │ │ │ └── utils │ │ │ ├── Consts.kt │ │ │ ├── DefaultLogger.kt │ │ │ ├── InvalidResult.kt │ │ │ ├── Logger.kt │ │ │ ├── Threader.kt │ │ │ └── Utils.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── tonyodev │ └── dispatch │ ├── DefaultThreadHandlerFactoryTest.kt │ ├── DefaultThreadHandlerTest.kt │ ├── DispatchQueueImplTest.kt │ ├── DispatchQueueInfoTest.kt │ ├── DispatchQueueObservableTest.kt │ ├── QueueControllerTest.kt │ ├── SettingsTest.kt │ ├── ThreadHandlerQueueItemTest.kt │ └── ThreaderTest.kt ├── dispatchandroid ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tonyodev │ │ └── dispatchandroid │ │ ├── AndroidThreadHandlerFactoryTest.kt │ │ ├── AndroidThreadHandlerTest.kt │ │ ├── DispatchQueueAndroidTest.kt │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tonyodev │ │ │ └── dispatchandroid │ │ │ ├── DispatchAndroidExtentions.kt │ │ │ ├── queueController │ │ │ └── ActivityDispatchQueueController.kt │ │ │ ├── thread │ │ │ ├── AndroidThreadHandler.kt │ │ │ └── AndroidThreadHandlerFactory.kt │ │ │ └── utils │ │ │ └── AndroidLogger.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── tonyodev │ └── dispatchandroid │ └── ExampleUnitTest.java ├── dispatchretrofit ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── tonyodev │ │ └── dispatchretrofit │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── tonyodev │ │ │ └── dispatchretrofit │ │ │ ├── DispatchQueueCallAdapterFactory.kt │ │ │ └── OnErrorCallback.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── tonyodev │ └── dispatchretrofit │ └── DispatchQueueCallFactoryTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | /.idea/misc.xml 15 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | 3 | jdk: 4 | - oraclejdk8 5 | 6 | android: 7 | components: 8 | # Uncomment the lines below if you want to 9 | # use the latest revision of Android SDK Tools 10 | - tools 11 | - platform-tools 12 | - tools 13 | # The BuildTools version used by your project 14 | - build-tools-28.0.3 15 | - build-tools-27.0.3 16 | - build-tools-26.0.2 17 | # The SDK version used to compile your project 18 | - android-28 19 | - android-27 20 | - android-26 21 | - android-21 22 | # Additional components 23 | - extra-android-m2repository 24 | - add-on 25 | - extra 26 | # Specify at least one system image, 27 | # if you need to run emulator(s) during your tests 28 | - sys-img-armeabi-v7a-android-21 29 | licenses: 30 | - 'android-sdk-preview-license-52d11cd2' 31 | - 'android-sdk-preview-license-.+' 32 | - 'android-sdk-license-.+' 33 | - 'google-gdk-license-.+' 34 | 35 | before_install: 36 | # Install SDK license so Android Gradle plugin can install deps. 37 | - yes | sdkmanager "platforms;android-28" 38 | - yes | sdkmanager "platforms;android-27" 39 | - mkdir "$ANDROID_HOME/licenses" || true 40 | - echo "8933bad161af4178b1185d1a37fbf41ea5269c55" > "$ANDROID_HOME/licenses/android-sdk-license" 41 | 42 | script: 43 | - ./gradlew clean assemble 44 | - ./gradlew test 45 | - ./gradlew assembleAndroidTest 46 | 47 | 48 | # Emulator Management: Create, Start and Wait 49 | before_script: 50 | - echo no | android create avd --force -n test -t android-21 --abi armeabi-v7a 51 | - emulator -avd test -no-window & 52 | - android-wait-for-emulator 53 | - adb shell input keyevent 82 & 54 | 55 | before_cache: 56 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 57 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 58 | 59 | cache: 60 | directories: 61 | - $HOME/.gradle/caches/ 62 | - $HOME/.gradle/wrapper/ 63 | - $HOME/.android/build-cache 64 | 65 | notifications: 66 | email: false 67 | 68 | sudo: false 69 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Version 1.4.10 2 | - Android init method renamed to initAndroidDispatchQueues() 3 | 4 | Version 1.4.9 5 | - Added new FlatMap methods 6 | - Memory and cpu performance improvements 7 | - Api improvements 8 | 9 | Version 1.4.8 10 | - Remove ambiguous start method 11 | 12 | Version 1.4.7 13 | - Fixed issue where a cancelled queue would throw exceptions 14 | 15 | Version 1.4.6 16 | - Added new zip method that takes in a list of dispatchQueues. 17 | - Added new async and post methods on DispatchQueue interface for direct executions. 18 | - Added new computation queue. 19 | - Added new start method on DispatchQueue 20 | 21 | Version 1.4.5 22 | - Memory, performance improvements and bug fixes. 23 | 24 | Version 1.4.4 25 | - Initialize android dispatch queues on app launch via DispatchQueueAndroid.initAndroidDispatchQueues() 26 | 27 | Version 1.4.3 28 | - Proguard fixes 29 | - Fixed thread not sleeping issues when using DefaultThreadHandler class. 30 | 31 | Version 1.4.2 32 | - Thread bug fix. Better logging. 33 | 34 | Version 1.4.1 35 | - Memory, performance improvements and bug fixes. 36 | 37 | Version 1.4.0 38 | - Refactored library. See readme 39 | - Memory improvements and bug fixes. 40 | 41 | Version 1.3.1 42 | - Added methods 43 | 44 | Version 1.3.0 45 | - Renamed Dispatch to DispatchQueue 46 | - Renamed DispatchObserver and DispatchObservable to DispatchQueueObserver and DispatchQueueObservable 47 | - Performance improvements. Document updates 48 | 49 | Version 1.2.1 50 | - Newly created queues now return Void? instead of kotlin.Unit 51 | - DispatchQueues are now synchronized. 52 | 53 | Version 1.2.0 54 | - Android Specific Classes are now in a separate package and will require the com.tonyodev.dispatch:android:x.x.x" to be added to build files. 55 | - Improvements to using DispatchObservable 56 | - Removed renamed zipWith methods on Dispatch to zip 57 | - Removed zipAny method from Dispatch interface. 58 | - Lots of Kotlin friendly goodies. 59 | - Performance and Memory Improvements. 60 | 61 | Version 1.0.9 62 | - Improvements to using DispatchObservable 63 | 64 | Version 1.0.8 65 | - Added ErrorHandler for DispatchCallAdapterFactory 66 | 67 | Version 1.0.7 68 | - Renamed transform method to map 69 | 70 | Version 1.0.6 71 | - Cancel fixes 72 | 73 | Version 1.0.5 74 | - Added new transform method on Dispatch 75 | - Added new class LifecycleDispatchQueueController 76 | 77 | Version 1.0.4 78 | - Bug Fixes 79 | - Renamed many of the methods. 80 | - Updated Documentation 81 | - Performance improvements 82 | 83 | Version 1.0.3 84 | - Added new static methods for Dispatcher with namedDispatches. 85 | - Added new ThreadType BACKGROUND1 secondary background handler 86 | 87 | Version 1.0.2 88 | - Small Fixes 89 | - Added cancel on complete method 90 | - Added DispatchObserver 91 | 92 | Version 1.0.1 93 | - Combine method renamed to zipWith. 94 | - Added new ZipWithAny method. 95 | - Bug Fixes and performance improvements. 96 | 97 | Version 1.0.0 98 | - Initial Version -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [ ![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) 2 | [![Build Status](https://travis-ci.org/tonyofrancis/Dispatcher.svg?branch=v1)](https://travis-ci.org/tonyofrancis/Dispatcher) 3 | [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/tonyofrancis/Fetch/blob/master/LICENSE) 4 | # DispatchQueue: A simple work scheduler for Java, Kotlin and Android 5 | 6 | DispatchQueue is a simple and flexible work scheduler that schedulers work on a background or main thread in the form of a dispatch queue. 7 | 8 | ```java 9 | DispatchQueue.background 10 | .async { 11 | //do background work here 12 | val sb = StringBuilder() 13 | for (i in 0..100) { 14 | sb.append(i) 15 | .append(" ") 16 | } 17 | sb.toString() 18 | } 19 | .post { data -> 20 | //do ui work here 21 | println(data) 22 | } 23 | .start() 24 | ``` 25 | DispatchQueue 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! 26 | 27 | One 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. 28 | Android Activity Example: 29 | ```java 30 | class SimpleActivity: AppCompatActivity() { 31 | 32 | override fun onCreate(savedInstanceState: Bundle?) { 33 | super.onCreate(savedInstanceState) 34 | DispatchQueue.io 35 | .managedBy(this) 36 | .async { 37 | //do work in background 38 | } 39 | .post { 40 | //handle results on ui 41 | } 42 | .start() 43 | } 44 | 45 | } 46 | ``` 47 | In 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: 48 | ```java 49 | class SimpleActivity: AppCompatActivity() { 50 | 51 | override fun onResume() { 52 | super.onResume() 53 | DispatchQueue.io 54 | .managedBy(this, CancelType.PAUSED) 55 | .async { 56 | //do work in background 57 | } 58 | .post { 59 | //handle results on ui 60 | } 61 | .start() 62 | } 63 | 64 | } 65 | ``` 66 | In 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. 67 | 68 | DispatchQueue 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. 69 | ```java 70 | class SimpleActivity: AppCompatActivity() { 71 | 72 | private val dispatchQueueController = DispatchQueueController() 73 | 74 | override fun onCreate(savedInstanceState: Bundle?) { 75 | super.onCreate(savedInstanceState) 76 | DispatchQueue.background 77 | .managedBy(dispatchQueueController) 78 | .async { 79 | //do work in background 80 | } 81 | .post { 82 | //handle results on ui 83 | } 84 | .start() 85 | } 86 | 87 | override fun onDestroy() { 88 | super.onDestroy() 89 | dispatchQueueController.cancelAllDispatchQueues() 90 | } 91 | 92 | } 93 | ``` 94 | Managing queues could not be easier. 95 | ### Queue Types 96 | 97 | DispatchQueue comes with many pre-exiting queues: 98 | ```java 99 | DispatchQueue.background 100 | 101 | DispatchQueue.io 102 | 103 | DispatchQueue.network 104 | 105 | DispatchQueue.test - // Used specifically for testing 106 | 107 | ``` 108 | These 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. 109 | ```java 110 | DispatchQueue.createDispatchQueue() 111 | 112 | DispatchQueue.createDispatchQueue(ThreadType.NEW) 113 | 114 | DispatchQueue.createIntervalDispatchQueue(delayInMillis = 1_000) 115 | 116 | DispatchQueue.createTimerDispatchQueue(delayInMillis = 10_000) 117 | ``` 118 | These are just some of the queues you can create. 119 | ### Network Queues with Retrofit 120 | 121 | We 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. 122 | 123 | *TestJsonData.kt* 124 | ```java 125 | class TestJsonData { 126 | 127 | var id: Int = 0 128 | 129 | var nm: String = "" 130 | 131 | var cty: String = "" 132 | 133 | override fun toString(): String { 134 | return "TestJsonData(id=$id, nm='$nm', cty='$cty')" 135 | } 136 | 137 | } 138 | ``` 139 | *TestService.kt* 140 | ```java 141 | interface TestService { 142 | 143 | @GET("/api/data?list=englishmonarchs&format=json") 144 | fun getSampleJson(): DispatchQueue> 145 | 146 | } 147 | ``` 148 | *SimpleActivity.kt* 149 | ```java 150 | class SimpleActivity: AppCompatActivity() { 151 | 152 | private lateinit var retrofit: Retrofit 153 | private lateinit var service: TestService 154 | 155 | override fun onCreate(savedInstanceState: Bundle?) { 156 | super.onCreate(savedInstanceState) 157 | 158 | val dispatchQueueCallAdapterFactory = DispatchQueueCallAdapterFactory.create() 159 | 160 | retrofit = Retrofit.Builder() 161 | .addConverterFactory(GsonConverterFactory.create()) 162 | .addCallAdapterFactory(dispatchQueueCallAdapterFactory) 163 | .baseUrl("http://mysafeinfo.com") 164 | .build() 165 | service = retrofit.create(TestService::class.java) 166 | 167 | runTestService() 168 | } 169 | 170 | private fun runTestService() { 171 | service.getSampleJson() 172 | .managedBy(this) 173 | .post { data -> 174 | for (testJsonData in data) { 175 | println(testJsonData) 176 | } 177 | } 178 | .start() 179 | } 180 | 181 | } 182 | ``` 183 | See 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. 184 | ### Zipping Queues 185 | 186 | There will be times when you would like to combine the results of two or more queues. Call the zip methods to do this. 187 | ```java 188 | class SimpleActivity: AppCompatActivity() { 189 | 190 | private val lifecycleDispatchQueueController = LifecycleDispatchQueueController() 191 | 192 | override fun onResume() { 193 | super.onResume() 194 | DispatchQueue.background 195 | .managedBy(lifecycleDispatchQueueController, CancelType.PAUSED) 196 | .async { 197 | mapOf(0 to "cat", 1 to "bat") 198 | } 199 | .zip(getDataDispatchQueue()) // combine two queue results 200 | .async { results -> 201 | for ((key, value) in results.first) { 202 | println("$key:$value") 203 | } 204 | for (string in results.second) { 205 | println(string) 206 | } 207 | } 208 | .start() 209 | } 210 | 211 | private fun getDataDispatchQueue(): DispatchQueue> { 212 | return Dispatcher.background 213 | .async { 214 | listOf("hat", "sat") 215 | } 216 | } 217 | 218 | override fun onPause() { 219 | super.onPause() 220 | lifecycleDispatchQueueController.cancelAllPaused() 221 | } 222 | 223 | } 224 | ``` 225 | ### Handling Errors 226 | 227 | DispatchQueue 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. 228 | ```java 229 | DispatchQueue.createDispatchQueue() 230 | .async { 231 | //do work 232 | val number = 66 233 | throw Exception("silly exception") 234 | number 235 | } 236 | .post { number -> 237 | println("number is $number") 238 | } 239 | .start(DispatchQueueErrorCallback { error -> 240 | //handle queue error here. 241 | Log.e("errorTest", 242 | "queue with id ${error.dispatchQueue.id} throw error:", error.throwable) 243 | }) 244 | ``` 245 | **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. 246 | 247 | Another 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. 248 | ```java 249 | DispatchQueue.createDispatchQueue() 250 | .async { 251 | //do work 252 | val number = 66 253 | throw Exception("silly exception") 254 | number 255 | } 256 | .doOnError { throwable -> 257 | if (throwable.message == "silly exception") { 258 | 100 259 | } else { 260 | 0 261 | } 262 | } 263 | .post { number -> 264 | println("number is $number") 265 | } 266 | .start() 267 | ``` 268 | In 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. 269 | 270 | If 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: 271 | ```java 272 | DispatchQueue.globalSettings.dispatchQueueErrorCallback = DispatchQueueErrorCallback { error -> 273 | //handle errors 274 | } 275 | ``` 276 | 277 | ### Debugging 278 | 279 | Figuring 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. 280 | ```java 281 | class SimpleActivity: AppCompatActivity() { 282 | 283 | override fun onCreate(savedInstanceState: Bundle?) { 284 | super.onCreate(savedInstanceState) 285 | DispatchQueue.background 286 | .managedBy(this) 287 | .async { 288 | //do work 289 | val number = 66 290 | throw Exception("silly exception") 291 | number 292 | } 293 | .setBlockLabel("numberAsync") 294 | .post { number -> 295 | println("number is $number") 296 | } 297 | .setBlockLabel("printAsync") 298 | .start(DispatchQueueErrorCallback { error -> 299 | if (error.blockLabel == "numberAsync") { 300 | //error occurred in first async block. 301 | } 302 | }) 303 | } 304 | 305 | } 306 | ``` 307 | You can also enable logging in the library. This will warm you when you forget to manage a queue with a `DispatchQueueController`. 308 | ```java 309 | DispatchQueue.globalSettings.enableLogWarnings = true 310 | ``` 311 | ### Dispatch Queue Observers 312 | 313 | Hey kids! Here! Have more ice-cream! 314 | 315 | DispatchQueue 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. 316 | ```java 317 | class SimpleActivity: AppCompatActivity() { 318 | 319 | private var n = 0 320 | 321 | private val dispatchQueueObserver = object: DispatchQueueObserver { 322 | override fun onChanged(data: Int) { 323 | print("Factorial of $n is: $data") 324 | } 325 | } 326 | 327 | override fun onCreate(savedInstanceState: Bundle?) { 328 | super.onCreate(savedInstanceState) 329 | n = 16 330 | 331 | DispatchQueue.background 332 | .managedBy(this) 333 | .async { 334 | factorial(n) 335 | } 336 | .addObserver(dispatchQueueObserver) 337 | .start() 338 | } 339 | 340 | private fun factorial(n: Int): Int { 341 | if (n == 0) return 1 342 | return n * factorial(n - 1) 343 | } 344 | 345 | } 346 | ``` 347 | ### Delays 348 | 349 | Both the async and post methods allow you to specify a delay in milliseconds before the block is executed. 350 | ```java 351 | DispatchQueue.background 352 | .post(5000) { 353 | //will be called after a 5 second delay 354 | }.start() 355 | ``` 356 | ### Thread Handlers 357 | 358 | Sometimes 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. 359 | ```java 360 | val threadHandler = DefaultThreadHandler("MyThreadHandler") 361 | val androidThreadHandler = AndroidThreadHandler("MyAndroidThreadHandler") 362 | 363 | DispatchQueue.createDispatchQueue(threadHandler) 364 | .async { 365 | //do work on my own thread 366 | } 367 | .start() 368 | ``` 369 | ### Understanding how DispatchQueue Works 370 | 371 | Now 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. 372 | 373 | ![alt text](https://cdn-images-1.medium.com/max/800/1*C8xQEB-0U35MbDQ1W6Pq5g.png "Simple Dispatch Diagram") 374 | 375 | 376 | The 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. 377 | 378 | **Note**: Once a Dispatch queue has completed its work, it is then cancelled and cannot be reused. 379 | ### Using Dispatch 380 | 381 | To use the DispatchQueue library in your project, add the following code to your project’s build.gradle file. 382 | ```java 383 | implementation "com.tonyodev.dispatch:dispatch:1.4.10" 384 | ``` 385 | For Android also add: 386 | ```java 387 | implementation "com.tonyodev.dispatch:dispatch-android:1.4.10" 388 | ``` 389 | To use Dispatch with Retrofit, add: 390 | ```java 391 | implementation "com.tonyodev.dispatch:dispatch-retrofit2-adapter:1.4.10" 392 | ``` 393 | 394 | Android users also have to initialize the AndroidDispatchQueues on application launch. 395 | ```java 396 | import com.tonyodev.dispatchandroid.initAndroidDispatchQueues 397 | 398 | class App: Application() { 399 | 400 | override fun onCreate() { 401 | super.onCreate() 402 | initAndroidDispatchQueues() // Java DispatchQueueAndroid.initAndroidDispatchQueues() 403 | } 404 | 405 | } 406 | ``` 407 | 408 | Contribute 409 | ---------- 410 | 411 | DispatchQueue can only get better if you make code contributions. Found a bug? Report it. 412 | Have a feature idea you'd love to see in DispatchQueue? Contribute to the project! 413 | 414 | 415 | License 416 | ------- 417 | 418 | ``` 419 | Copyright (C) 2017 Tonyo Francis. 420 | 421 | Licensed under the Apache License, Version 2.0 (the "License"); 422 | you may not use this file except in compliance with the License. 423 | You may obtain a copy of the License at 424 | 425 | http://www.apache.org/licenses/LICENSE-2.0 426 | 427 | Unless required by applicable law or agreed to in writing, software 428 | distributed under the License is distributed on an "AS IS" BASIS, 429 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 430 | See the License for the specific language governing permissions and 431 | limitations under the License. 432 | ``` 433 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 28 9 | defaultConfig { 10 | applicationId "com.tonyodev.dispatcherapp" 11 | minSdkVersion 14 12 | targetSdkVersion 28 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_1_8 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | } 27 | 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 33 | implementation 'androidx.appcompat:appcompat:1.0.2' 34 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 35 | implementation "com.squareup.retrofit2:retrofit:$retrofit_version" 36 | implementation "com.squareup.retrofit2:converter-gson:$retrofit_version" 37 | implementation "com.google.code.gson:gson:$gson_version" 38 | implementation project(':dispatch') 39 | implementation project(':dispatchretrofit') 40 | implementation project(':dispatchandroid') 41 | 42 | testImplementation 'junit:junit:4.12' 43 | androidTestImplementation 'androidx.test:runner:1.2.0-beta01' 44 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01' 45 | } 46 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/tonyodev/dispatcherapp/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.tonyodev.dispatcherapp 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.tonyodev.dispatcherapp", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/tonyodev/dispatcherapp/ActivityTwo.java: -------------------------------------------------------------------------------- 1 | package com.tonyodev.dispatcherapp; 2 | 3 | import android.os.Bundle; 4 | import android.util.Log; 5 | import android.widget.TextView; 6 | import androidx.annotation.Nullable; 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import com.tonyodev.dispatch.DispatchQueue; 9 | import com.tonyodev.dispatch.queuecontroller.CancelType; 10 | import com.tonyodev.dispatchandroid.queueController.ActivityDispatchQueueController; 11 | import kotlin.jvm.functions.Function1; 12 | 13 | 14 | public class ActivityTwo extends AppCompatActivity { 15 | 16 | private TextView textView; 17 | private ActivityDispatchQueueController activityDispatchQueueController; 18 | 19 | @Override 20 | protected void onCreate(@Nullable Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_two); 23 | textView = findViewById(R.id.text_view); 24 | activityDispatchQueueController = ActivityDispatchQueueController.getInstance(this); 25 | } 26 | 27 | @Override 28 | protected void onResume() { 29 | super.onResume(); 30 | runIntervalTask(); 31 | } 32 | 33 | private void runIntervalTask() { 34 | DispatchQueue.Queue.createIntervalDispatchQueue(10000) 35 | .managedBy(activityDispatchQueueController, CancelType.PAUSED) 36 | .async(aVoid -> 55) 37 | .async(2000, integer -> { 38 | Log.d("dispatchTest", "interval break"); 39 | return "hello world"; 40 | }).post((Function1) s -> { 41 | textView.setText(s); 42 | Log.d("dispatchTest", "main thread break:" + s); 43 | return null; 44 | }).async((Function1) aVoid -> { 45 | Log.d("dispatchTest", "void method called"); 46 | return null; 47 | }).start(dispatchQueueError -> Log.d("dispatchTest", "error:" + dispatchQueueError.getThrowable().getMessage())); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/tonyodev/dispatcherapp/App.kt: -------------------------------------------------------------------------------- 1 | package com.tonyodev.dispatcherapp 2 | 3 | import android.app.Application 4 | import com.tonyodev.dispatchandroid.initAndroidDispatchQueues 5 | 6 | class App: Application() { 7 | 8 | override fun onCreate() { 9 | super.onCreate() 10 | initAndroidDispatchQueues() 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/tonyodev/dispatcherapp/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.tonyodev.dispatcherapp 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.util.Log 6 | import android.widget.Button 7 | import androidx.appcompat.app.AppCompatActivity 8 | import com.tonyodev.dispatch.DispatchQueue 9 | import com.tonyodev.dispatch.DispatchQueueErrorCallback 10 | import com.tonyodev.dispatchandroid.managedBy 11 | import com.tonyodev.dispatchretrofit.DispatchQueueCallAdapterFactory 12 | import retrofit2.Retrofit 13 | import retrofit2.converter.gson.GsonConverterFactory 14 | 15 | class MainActivity : AppCompatActivity() { 16 | 17 | private lateinit var retrofit: Retrofit 18 | private lateinit var service: TestService 19 | 20 | override fun onCreate(savedInstanceState: Bundle?) { 21 | super.onCreate(savedInstanceState) 22 | setContentView(R.layout.activity_main) 23 | findViewById