├── .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 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
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 | [  ](https://bintray.com/tonyofrancis/maven/dispatch/1.4.10/link)
2 | [](https://travis-ci.org/tonyofrancis/Dispatcher)
3 | [](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 | 
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(R.id.button).setOnClickListener {
24 | val intent = Intent(this, ActivityTwo::class.java)
25 | startActivity(intent)
26 | }
27 | retrofit = Retrofit.Builder()
28 | .addConverterFactory(GsonConverterFactory.create())
29 | .addCallAdapterFactory(DispatchQueueCallAdapterFactory.create())
30 | .baseUrl("https://jsonplaceholder.typicode.com")
31 | .build()
32 | service = retrofit.create(TestService::class.java)
33 | runTestService()
34 | runTestTimer()
35 | runFlatMap()
36 | }
37 |
38 | private fun runTestService() {
39 | service.getSampleJson()
40 | .managedBy(this)
41 | .async { data ->
42 | Log.d("dispatchTest", "data size is:${data.size}")
43 | }
44 | .start()
45 | //or
46 | DispatchQueue.background
47 | .managedBy(this)
48 | .async {
49 | "66"
50 | }
51 | .zip(service.getSampleJson())
52 | .async {
53 | it.second
54 | }
55 | .post {
56 | Log.d("dispatchTest", "data size is:${it.size}")
57 | }
58 | .start(DispatchQueueErrorCallback {
59 |
60 | })
61 | }
62 |
63 | private fun runTestTimer() {
64 | DispatchQueue.createTimerDispatchQueue(5000)
65 | .managedBy(this)
66 | .async {
67 | "do work here"
68 | }
69 | .post {
70 | "do work here"
71 | }
72 | .async {
73 | "do more work here"
74 | }
75 | .post {
76 | Log.d("dispatchTest","Test timer after 5000 millis. data is $it")
77 | }
78 | .start(DispatchQueueErrorCallback {
79 |
80 | })
81 | }
82 |
83 | private fun runFlatMap() {
84 | DispatchQueue.background
85 | .async {
86 | "a quick brown fox"
87 | }
88 | .async {
89 | Pair(it, " over the lazy dogs")
90 | }
91 | .flatMap { labelPair ->
92 | service.getSampleJson().async { Pair(it, labelPair) }
93 | }
94 | .async {
95 | Log.d("dispatchTest", "label is ${it.second.first + it.second.second}")
96 | it.first
97 | }
98 | .map { it.toSet() }
99 | .flatMap { set -> DispatchQueue.io.async { Pair(set, set.size) } }
100 | .post {
101 | Log.d("dispatchTest", "set size is ${it.second}")
102 | for (testJsonData in it.first) {
103 | // Log.d("dispatchTest", testJsonData.toString())
104 | }
105 | }
106 | .start(DispatchQueueErrorCallback {
107 |
108 | })
109 | }
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/com/tonyodev/dispatcherapp/TestJsonData.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatcherapp
2 |
3 | class TestJsonData {
4 |
5 | var userId: Int = 0
6 |
7 | var id: Int = 0
8 |
9 | var title: String = ""
10 |
11 | var completed: String = ""
12 |
13 | override fun toString(): String {
14 | return "TestJsonData(userId=$userId, id=$id, title='$title', completed='$completed')"
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/tonyodev/dispatcherapp/TestService.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatcherapp
2 |
3 | import com.tonyodev.dispatch.DispatchQueue
4 | import retrofit2.http.GET
5 |
6 | interface TestService {
7 |
8 | @GET("/todos")
9 | fun getSampleJson(): DispatchQueue>
10 |
11 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
10 |
12 |
14 |
16 |
18 |
20 |
22 |
24 |
26 |
28 |
30 |
32 |
34 |
36 |
38 |
40 |
42 |
44 |
46 |
48 |
50 |
52 |
54 |
56 |
58 |
60 |
62 |
64 |
66 |
68 |
70 |
72 |
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_two.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Dispatcher App
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/app/src/test/java/com/tonyodev/dispatcherapp/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatcherapp
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.41'
5 | ext.library_version = '1.4.10'
6 | ext.library_version_code = 26
7 | ext.retrofit_version = '2.5.0'
8 | ext.gson_version = '2.8.5'
9 | ext.novoda_bintray_version = '0.9'
10 | repositories {
11 | google()
12 | jcenter()
13 |
14 | }
15 | dependencies {
16 | classpath 'com.android.tools.build:gradle:3.4.2'
17 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
18 | classpath "com.novoda:bintray-release:$novoda_bintray_version"
19 | // NOTE: Do not place your application dependencies here; they belong
20 | // in the individual module build.gradle files
21 | }
22 | }
23 |
24 | allprojects {
25 | repositories {
26 | google()
27 | jcenter()
28 |
29 | }
30 | }
31 |
32 | task clean(type: Delete) {
33 | delete rootProject.buildDir
34 | }
35 |
--------------------------------------------------------------------------------
/dispatch/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/dispatch/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 |
3 | apply plugin: 'kotlin'
4 |
5 | apply plugin: 'com.novoda.bintray-release'
6 |
7 | sourceCompatibility = "7"
8 | targetCompatibility = "7"
9 |
10 | dependencies {
11 | implementation fileTree(dir: 'libs', include: ['*.jar'])
12 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
13 | testImplementation 'junit:junit:4.12'
14 | }
15 |
16 | publish {
17 | userOrg = 'tonyofrancis'
18 | groupId = 'com.tonyodev.dispatch'
19 | artifactId = 'dispatch'
20 | publishVersion = library_version
21 | desc = 'Dispatch is a simple and flexible work scheduler that schedulers work on a background or UI thread in the form of Tracks using android.os.Handler.'
22 | website = 'https://github.com/tonyofrancis/dispatcher'
23 | }
24 |
25 | tasks.withType(Javadoc).all {
26 | enabled = false
27 | }
28 |
--------------------------------------------------------------------------------
/dispatch/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 |
--------------------------------------------------------------------------------
/dispatch/src/androidTest/java/com/tonyodev/dispatch/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.tonyodev.dispatch.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dispatch/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/DispatchQueueError.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 |
4 | /**
5 | * This class holds the error, dispatch queue and the label of the block in the queue that caused the error.
6 | * */
7 | data class DispatchQueueError(
8 | /**
9 | * The error.
10 | * */
11 | val throwable: Throwable,
12 |
13 | /**
14 | * The dispatch queue the error occurred in.
15 | * */
16 | val dispatchQueue: DispatchQueue<*>,
17 |
18 | /**
19 | * The label of the block that caused the error in the dispatch queue.
20 | * */
21 | val blockLabel: String)
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/DispatchQueueErrorCallback.java:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch;
2 |
3 |
4 | /**
5 | * Interface used by the DispatchQueue to relay errors.
6 | * */
7 | @FunctionalInterface
8 | public interface DispatchQueueErrorCallback {
9 |
10 | /**
11 | * Called when an error occurs inside a dispatchQueue.
12 | * @param dispatchQueueError the error.
13 | * */
14 | void onError(DispatchQueueError dispatchQueueError);
15 |
16 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/DispatchQueueObservable.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.ThreadHandler
4 | import com.tonyodev.dispatch.utils.INVALID_RESULT
5 | import com.tonyodev.dispatch.utils.Threader
6 | import com.tonyodev.dispatch.utils.startThreadHandlerIfNotActive
7 |
8 | /**
9 | * Class that allows for the attaching of dispatch queue observers and publishing results to them.
10 | * */
11 | class DispatchQueueObservable constructor(
12 | /**
13 | * Notifies the attached Observers on the passed in threadHandler.
14 | * */
15 | val threadHandler: ThreadHandler?) {
16 |
17 | @Volatile
18 | var shouldNotifyOnHandler: Boolean = false
19 |
20 | /**
21 | * Notifies the attached Observers on the main thread.
22 | * */
23 | constructor(): this(Threader.getHandlerThreadInfo(ThreadType.MAIN).threadHandler)
24 |
25 | private val dispatchQueueObserversSet = mutableSetOf>()
26 | private var result: Any? = INVALID_RESULT
27 |
28 | init {
29 | if (threadHandler != null) {
30 | shouldNotifyOnHandler = true
31 | }
32 | if (shouldNotifyOnHandler && threadHandler != null) {
33 | startThreadHandlerIfNotActive(threadHandler)
34 | }
35 | }
36 |
37 | /**
38 | * Adds a dispatch queue observer.
39 | * @param dispatchQueueObserver the observer.
40 | * @return the dispatchQueueObservable.
41 | * */
42 | @Suppress("UNCHECKED_CAST")
43 | fun addObserver(dispatchQueueObserver: DispatchQueueObserver): DispatchQueueObservable {
44 | synchronized(dispatchQueueObserversSet) {
45 | dispatchQueueObserversSet.add(dispatchQueueObserver)
46 | if (result != INVALID_RESULT) {
47 | val value = result as R
48 | if (shouldNotifyOnHandler && threadHandler != null) {
49 | threadHandler.post(Runnable { dispatchQueueObserver.onChanged(value) })
50 | } else {
51 | dispatchQueueObserver.onChanged(value)
52 | }
53 | }
54 | }
55 | return this
56 | }
57 |
58 | /**
59 | * Adds a list of dispatch queue observers.
60 | * @param dispatchQueueObservers the collection of observers.
61 | * @return the dispatchQueueObservable.
62 | * */
63 | @Suppress("UNCHECKED_CAST")
64 | fun addObservers(dispatchQueueObservers: Collection>): DispatchQueueObservable {
65 | synchronized(dispatchQueueObserversSet) {
66 | dispatchQueueObserversSet.addAll(dispatchQueueObservers)
67 | val result = this.result
68 | if (result != INVALID_RESULT) {
69 | val value = result as R
70 | if (shouldNotifyOnHandler && threadHandler != null) {
71 | val observers = dispatchQueueObservers.toList()
72 | threadHandler.post(Runnable {
73 | for (dispatchObserver in observers) {
74 | dispatchObserver.onChanged(value)
75 | }
76 | })
77 | } else {
78 | for (dispatchObserver in dispatchQueueObservers) {
79 | dispatchObserver.onChanged(value)
80 | }
81 | }
82 | }
83 | }
84 | return this
85 | }
86 |
87 | /**
88 | * Removes a dispatch queue observer.
89 | * @param dispatchQueueObserver the observer to be removed.
90 | * @return the dispatchQueueObservable.
91 | * */
92 | fun removeObserver(dispatchQueueObserver: DispatchQueueObserver): DispatchQueueObservable {
93 | synchronized(dispatchQueueObserversSet) {
94 | dispatchQueueObserversSet.remove(dispatchQueueObserver)
95 | }
96 | return this
97 | }
98 |
99 | /**
100 | * Remove a list of dispatch queue observers.
101 | * @param dispatchQueueObservers the list of observers to be removed.
102 | * @return the dispatchQueueObservable.
103 | * */
104 | fun removeObservers(dispatchQueueObservers: Collection>): DispatchQueueObservable {
105 | synchronized(dispatchQueueObserversSet) {
106 | dispatchQueueObserversSet.removeAll(dispatchQueueObservers)
107 | }
108 | return this
109 | }
110 |
111 | /** Removes all observers attached this observable.
112 | * @return the dispatchQueueObservable.
113 | * */
114 | fun removeObservers(): DispatchQueueObservable {
115 | synchronized(dispatchQueueObserversSet) {
116 | dispatchQueueObserversSet.clear()
117 | }
118 | return this
119 | }
120 |
121 | /** Notifies observers of the passed in result
122 | * @param result the result
123 | * @return the dispatchQueueObservable.
124 | * */
125 | fun notify(result: R): DispatchQueueObservable {
126 | if (shouldNotifyOnHandler && threadHandler != null) {
127 | threadHandler.post(Runnable { notifyObservers(result) })
128 | } else {
129 | notifyObservers(result)
130 | }
131 | return this
132 | }
133 |
134 | private fun notifyObservers(result: R) {
135 | this.result = result
136 | synchronized(dispatchQueueObserversSet) {
137 | for (dispatchQueueObserver in dispatchQueueObserversSet) {
138 | dispatchQueueObserver.onChanged(result)
139 | }
140 | }
141 | }
142 |
143 | /** Get all the observers attached to this observable*/
144 | fun getObservers(): List> {
145 | return synchronized(dispatchQueueObserversSet) { dispatchQueueObserversSet.toList() }
146 | }
147 |
148 | /**
149 | * Clears the currently stored data.
150 | * */
151 | fun resetResults() {
152 | result = INVALID_RESULT
153 | }
154 |
155 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/DispatchQueueObserver.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | /**
4 | * DispatchQueueObservers listen for data changes on a dispatch queue object.
5 | * */
6 | @FunctionalInterface
7 | interface DispatchQueueObserver {
8 |
9 | /**
10 | * Method called when the data on the observing object has changed.
11 | * Called on the thread that belongs to the dispatch queue object. If post
12 | * was called the onChanged will be called on the main thread. If async
13 | * was called the onChange is called on the thread that the dispatch queue object uses.
14 | * @param t the data.
15 | * */
16 | fun onChanged(t: T)
17 |
18 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/Settings.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.DefaultThreadHandlerFactory
4 | import com.tonyodev.dispatch.thread.ThreadHandlerFactory
5 | import com.tonyodev.dispatch.utils.DefaultLogger
6 | import com.tonyodev.dispatch.utils.Logger
7 |
8 | /**
9 | * Class that holds the DispatchQueue's global Settings.
10 | * */
11 | class Settings {
12 |
13 | /**
14 | * Enable or disable log warnings by the library.
15 | * */
16 | var enableLogWarnings = false
17 |
18 | /**
19 | * Sets the global error handler for Dispatch objects. This error handler is called only
20 | * if a dispatch queue does not handler its errors. The error handler is called on the main thread.
21 | * */
22 | var dispatchQueueErrorCallback: DispatchQueueErrorCallback? = null
23 |
24 | /**
25 | * Sets the global thread handler factory that is responsible for creating thread handlers that the dispatch queues
26 | * will use to process work in the background.
27 | * */
28 | var threadHandlerFactory: ThreadHandlerFactory = DefaultThreadHandlerFactory()
29 |
30 | /**
31 | * Sets the logger uses by the library to report warnings and messages.
32 | * */
33 | var logger: Logger = DefaultLogger()
34 |
35 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/ThreadType.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | /**
4 | * Enum that holds the different thread types.
5 | * */
6 | enum class ThreadType {
7 |
8 | /** Use default IO thread.*/
9 | IO,
10 |
11 | /** Use default Background thread.*/
12 | BACKGROUND,
13 |
14 | /** Use default Network thread.*/
15 | NETWORK,
16 |
17 | /** Use default Computation thread. Use this thread for long computation work*/
18 | COMPUTATION,
19 |
20 | /** Use a new background thread.*/
21 | NEW,
22 |
23 | /** On Android uses the user interface thread called main. On other JVMs a thread called dispatchMain is created by default.*/
24 | MAIN,
25 |
26 | /** Uses the default test thread handler*/
27 | TEST;
28 |
29 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/internals/DispatchQueueImpl.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.internals
2 |
3 | import com.tonyodev.dispatch.*
4 | import com.tonyodev.dispatch.queuecontroller.CancelType
5 | import com.tonyodev.dispatch.queuecontroller.DispatchQueueController
6 | import com.tonyodev.dispatch.queuecontroller.LifecycleDispatchQueueController
7 | import com.tonyodev.dispatch.utils.*
8 | import com.tonyodev.dispatch.utils.INVALID_RESULT
9 | import com.tonyodev.dispatch.utils.Threader
10 | import com.tonyodev.dispatch.utils.getNewDispatchId
11 | import java.lang.IllegalArgumentException
12 | import java.util.concurrent.TimeUnit
13 |
14 | internal class DispatchQueueImpl(override var blockLabel: String,
15 | private val delayInMillis: Long = 0,
16 | private var worker: ((T) -> R)?,
17 | private val dispatchQueueInfo: DispatchQueueInfo,
18 | private val threadHandlerInfo: Threader.ThreadHandlerInfo): DispatchQueue {
19 |
20 | private val dispatchSources = arrayOfNulls>(3)
21 | private var sourceCount = 0
22 | private var dispatchQueueObservable = DispatchQueueObservable(null)
23 | private var doOnErrorWorker: ((throwable: Throwable) -> R)? = null
24 | private var retryCount = 0
25 | private var retryAttempt = 0
26 | private var retryDelayInMillis = 0L
27 | private var next: DispatchQueueImpl<*, *>? = null
28 | private var results: Any? = INVALID_RESULT
29 | private var dispatcher: Runnable? = null
30 |
31 | override val id: Int
32 | get() {
33 | return dispatchQueueInfo.queueId
34 | }
35 |
36 | override val isCancelled: Boolean
37 | get() {
38 | return dispatchQueueInfo.isCancelled
39 | }
40 |
41 | override val controller: DispatchQueueController?
42 | get() {
43 | return dispatchQueueInfo.dispatchQueueController
44 | }
45 |
46 | @Suppress("UNCHECKED_CAST")
47 | private fun getDispatcher(): Runnable {
48 | return Runnable {
49 | try {
50 | if (!isCancelled) {
51 | if (worker != null && sourceCount > 0) {
52 | val result1: Any?
53 | val result2: Any?
54 | val result3: Any?
55 | val data = when (sourceCount) {
56 | 3 -> {
57 | result1 = (dispatchSources[0] as DispatchQueueImpl<*, *>).results
58 | result2 = (dispatchSources[1] as DispatchQueueImpl<*, *>).results
59 | result3 = (dispatchSources[2] as DispatchQueueImpl<*, *>).results
60 | if (hasInvalidResult(result1, result2, result3)) {
61 | INVALID_RESULT
62 | } else {
63 | Triple(result1, result2, result3)
64 | }
65 | }
66 | 2 -> {
67 | result1 = (dispatchSources[0] as DispatchQueueImpl<*, *>).results
68 | result2 = (dispatchSources[1] as DispatchQueueImpl<*, *>).results
69 | if (hasInvalidResult(result1, result2)) {
70 | INVALID_RESULT
71 | } else {
72 | Pair(result1, result2)
73 | }
74 | }
75 | else -> {
76 | result1 = (dispatchSources[0] as DispatchQueueImpl<*, *>).results
77 | if (hasInvalidResult(result1)) {
78 | INVALID_RESULT
79 | } else {
80 | result1
81 | }
82 | }
83 | }
84 | if (data != INVALID_RESULT && !isCancelled) {
85 | val func = worker
86 | if (func != null) {
87 | results = func.invoke(data as T)
88 | } else {
89 | results = INVALID_RESULT
90 | }
91 | notifyDispatchObservers()
92 | processNextDispatchQueue()
93 | }
94 | } else {
95 | results = null
96 | notifyDispatchObservers()
97 | processNextDispatchQueue()
98 | }
99 | }
100 | } catch (err: Exception) {
101 | if (retryAttempt < retryCount) {
102 | runDispatcher(true)
103 | } else {
104 | val doOnErrorWorker = doOnErrorWorker
105 | if (doOnErrorWorker != null && !isCancelled) {
106 | try {
107 | results = doOnErrorWorker.invoke(err)
108 | notifyDispatchObservers()
109 | processNextDispatchQueue()
110 | } catch (e: Exception) {
111 | handleException(e)
112 | }
113 | } else {
114 | handleException(err)
115 | }
116 | }
117 | }
118 | }
119 | }
120 |
121 | @Suppress("UNCHECKED_CAST")
122 | private fun notifyDispatchObservers() {
123 | if (!isCancelled) {
124 | dispatchQueueObservable.notify(results as R)
125 | }
126 | }
127 |
128 | private fun hasInvalidResult(vararg results: Any?): Boolean {
129 | for (result in results) {
130 | if (result == INVALID_RESULT) {
131 | return true
132 | }
133 | }
134 | return false
135 | }
136 |
137 | private fun processNextDispatchQueue() {
138 | if (!isCancelled) {
139 | val nextDispatchQueue = next
140 | when {
141 | nextDispatchQueue != null -> nextDispatchQueue.runDispatcher()
142 | dispatchQueueInfo.isIntervalDispatch -> dispatchQueueInfo.rootDispatchQueue?.runDispatcher()
143 | else -> {
144 | if (!dispatchQueueInfo.isIntervalDispatch) {
145 | cancel()
146 | }
147 | }
148 | }
149 | }
150 | }
151 |
152 | private fun runDispatcher(isRetryAttempt: Boolean = false) {
153 | if (!isCancelled) {
154 | var dispatcherRunnable = dispatcher
155 | if (dispatcherRunnable != null) {
156 | threadHandlerInfo.threadHandler.removeCallbacks(dispatcherRunnable)
157 | dispatcher = null
158 | }
159 | dispatcherRunnable = getDispatcher()
160 | dispatcher = dispatcherRunnable
161 | if (dispatchQueueInfo.dispatchQueueController == null && DispatchQueue.globalSettings.enableLogWarnings && this == dispatchQueueInfo.rootDispatchQueue) {
162 | DispatchQueue.globalSettings.logger.print(
163 | TAG, "No DispatchQueueController set for dispatch queue with id: $id. " +
164 | "Not setting a DispatchQueueController can cause memory leaks for long running tasks.")
165 | }
166 | when {
167 | isRetryAttempt -> {
168 | retryAttempt += 1
169 | threadHandlerInfo.threadHandler.postDelayed(retryDelayInMillis, dispatcherRunnable)
170 | DispatchQueue.globalSettings.logger.print(
171 | TAG, "DispatchQueue with id $id attempting retry on block with label: $blockLabel")
172 | }
173 | delayInMillis >= 1 -> threadHandlerInfo.threadHandler.postDelayed(delayInMillis, dispatcherRunnable)
174 | else -> threadHandlerInfo.threadHandler.post(dispatcherRunnable)
175 | }
176 | }
177 | }
178 |
179 | @Suppress("UNCHECKED_CAST")
180 | private fun handleException(throwable: Throwable) {
181 | val mainErrorHandler = dispatchQueueInfo.dispatchQueueErrorCallback
182 | if (mainErrorHandler != null) {
183 | Threader.getHandlerThreadInfo(ThreadType.MAIN).threadHandler.post(Runnable {
184 | mainErrorHandler.onError(DispatchQueueError(throwable, this, this.blockLabel))
185 | })
186 | cancel()
187 | return
188 | }
189 | val globalHandler = DispatchQueue.globalSettings.dispatchQueueErrorCallback
190 | if (globalHandler != null) {
191 | Threader.getHandlerThreadInfo(ThreadType.MAIN)
192 | .threadHandler.post(Runnable { globalHandler.onError(DispatchQueueError(throwable, this, this.blockLabel)) })
193 | cancel()
194 | return
195 | }
196 | cancel()
197 | throw throwable
198 | }
199 |
200 | internal fun setNext(dispatchQueueImpl: DispatchQueueImpl<*, *>) {
201 | next = dispatchQueueImpl
202 | }
203 |
204 | private fun addSource(source: DispatchQueueImpl<*, *>, performChecks: Boolean = true) {
205 | if ((!performChecks || dispatchQueueInfo.canPerformOperations()) && sourceCount < 3) {
206 | dispatchSources[sourceCount] = source
207 | sourceCount += 1
208 | }
209 | }
210 |
211 | override fun start(): DispatchQueue {
212 | return startQueue(null)
213 | }
214 |
215 | override fun start(dispatchQueueErrorCallback: DispatchQueueErrorCallback?): DispatchQueue {
216 | return startQueue(dispatchQueueErrorCallback)
217 | }
218 |
219 | private fun startQueue(errorCallback: DispatchQueueErrorCallback?): DispatchQueue {
220 | if (!isCancelled) {
221 | if (!dispatchQueueInfo.isStarted) {
222 | dispatchQueueInfo.isStarted = true
223 | dispatchQueueInfo.dispatchQueueErrorCallback = errorCallback
224 | dispatchQueueInfo.rootDispatchQueue?.runDispatcher()
225 | DispatchQueue.globalSettings.logger.print(TAG, "DispatchQueue with id ${dispatchQueueInfo.queueId} has started.")
226 | }
227 | } else {
228 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
229 | }
230 | return this
231 | }
232 |
233 | override fun cancel(): DispatchQueue {
234 | if (!isCancelled) {
235 | dispatchQueueInfo.dispatchQueueErrorCallback = CANCELLED_ERROR_CALLBACK
236 | dispatchQueueInfo.isCancelled = true
237 | val dispatchQueueController = dispatchQueueInfo.dispatchQueueController
238 | dispatchQueueInfo.dispatchQueueController = null
239 | if (dispatchQueueController is LifecycleDispatchQueueController) {
240 | dispatchQueueController.unmanage(this)
241 | } else {
242 | dispatchQueueController?.unmanage(this)
243 | }
244 | var current = dispatchQueueInfo.rootDispatchQueue
245 | var next: DispatchQueueImpl<*, *>?
246 | var sourceSize: Int
247 | while (current != null) {
248 | current.removeDispatcher()
249 | sourceSize = current.sourceCount
250 | current.sourceCount = 0
251 | for (index in 0 until sourceSize) {
252 | current.dispatchSources[index] = null
253 | }
254 | current.dispatchQueueObservable.removeObservers()
255 | current.dispatchQueueObservable.resetResults()
256 | current.results = INVALID_RESULT
257 | current.dispatcher = null
258 | current.doOnErrorWorker = null
259 | current.worker = null
260 | current.retryAttempt = 0
261 | next = current.next
262 | current.next = null
263 | current = next
264 | }
265 | dispatchQueueInfo.rootDispatchQueue = null
266 | dispatchQueueInfo.endDispatchQueue = null
267 | DispatchQueue.globalSettings.logger.print(TAG, "DispatchQueue with id ${dispatchQueueInfo.queueId} has been cancelled.")
268 | }
269 | return this
270 | }
271 |
272 | private fun removeDispatcher() {
273 | val dispatcherRunnable = dispatcher
274 | dispatcher = null
275 | if (dispatcherRunnable != null) {
276 | threadHandlerInfo.threadHandler.removeCallbacks(dispatcherRunnable)
277 | }
278 | if (threadHandlerInfo.closeThreadHandler && threadHandlerInfo.threadHandler.isActive) {
279 | threadHandlerInfo.threadHandler.quit()
280 | }
281 | }
282 |
283 | override fun doOnError(func: ((Throwable) -> R)): DispatchQueue {
284 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
285 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
286 | this.doOnErrorWorker = func
287 | return this
288 | }
289 |
290 | override fun managedBy(dispatchQueueController: DispatchQueueController?): DispatchQueue {
291 | if (dispatchQueueController is LifecycleDispatchQueueController) {
292 | managedBy(dispatchQueueController, CancelType.DESTROYED)
293 | } else {
294 | val oldDispatchQueueController = this.dispatchQueueInfo.dispatchQueueController
295 | this.dispatchQueueInfo.dispatchQueueController = null
296 | oldDispatchQueueController?.unmanage(this)
297 | this.dispatchQueueInfo.dispatchQueueController = dispatchQueueController
298 | dispatchQueueController?.manage(this)
299 | }
300 | return this
301 | }
302 |
303 | override fun managedBy(lifecycleDispatchQueueController: LifecycleDispatchQueueController, cancelType: CancelType): DispatchQueue {
304 | val oldDispatchQueueController = this.dispatchQueueInfo.dispatchQueueController
305 | this.dispatchQueueInfo.dispatchQueueController = null
306 | oldDispatchQueueController?.unmanage(this)
307 | this.dispatchQueueInfo.dispatchQueueController = lifecycleDispatchQueueController
308 | lifecycleDispatchQueueController.manage(this, cancelType)
309 | return this
310 | }
311 |
312 | override fun post(func: (R) -> U): DispatchQueue {
313 | return post(0, func)
314 | }
315 |
316 | override fun post(delayInMillis: Long, func: (R) -> U): DispatchQueue {
317 | return getNewDispatchQueue(func, delayInMillis, Threader.getHandlerThreadInfo(ThreadType.MAIN))
318 | }
319 |
320 | override fun post(timeUnit: TimeUnit, delay: Long, func: (R) -> U): DispatchQueue {
321 | return getNewDispatchQueue(func, timeUnit.toMillis(delay), Threader.getHandlerThreadInfo(ThreadType.MAIN))
322 | }
323 |
324 | override fun async(func: (R) -> U): DispatchQueue {
325 | return async(0, func)
326 | }
327 |
328 | override fun async(delayInMillis: Long, func: (R) -> U): DispatchQueue {
329 | return getNewDispatchQueue(func, delayInMillis, dispatchQueueInfo.threadHandlerInfo)
330 | }
331 |
332 | override fun async(timeUnit: TimeUnit, delay: Long, func: (R) -> U): DispatchQueue {
333 | return getNewDispatchQueue(func, timeUnit.toMillis(delay), dispatchQueueInfo.threadHandlerInfo)
334 | }
335 |
336 | override fun map(func: (R) -> T): DispatchQueue {
337 | return async(func)
338 | }
339 |
340 | private fun getNewDispatchQueue(worker: (T) -> R, delayInMillis: Long, threadHandlerInfo: Threader.ThreadHandlerInfo): DispatchQueue {
341 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
342 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
343 | val newDispatchQueue = DispatchQueueImpl(
344 | blockLabel = getNewDispatchId(),
345 | delayInMillis = delayInMillis,
346 | worker = worker,
347 | dispatchQueueInfo = dispatchQueueInfo,
348 | threadHandlerInfo = threadHandlerInfo)
349 | newDispatchQueue.addSource(this)
350 | dispatchQueueInfo.enqueue(newDispatchQueue)
351 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
352 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
353 | return newDispatchQueue
354 | }
355 |
356 | override fun zip(dispatchQueue: DispatchQueue): DispatchQueue> {
357 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
358 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
359 | val newDispatchQueue = DispatchQueueImpl, Pair>(
360 | blockLabel = getNewDispatchId(),
361 | delayInMillis = 0,
362 | worker = { it },
363 | dispatchQueueInfo = dispatchQueueInfo,
364 | threadHandlerInfo = dispatchQueueInfo.threadHandlerInfo)
365 | newDispatchQueue.addSource(this)
366 | newDispatchQueue.addSource((dispatchQueue as DispatchQueueImpl<*, *>).cloneTo(newDispatchQueueInfo = dispatchQueueInfo))
367 | dispatchQueueInfo.enqueue(newDispatchQueue)
368 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
369 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
370 | return newDispatchQueue
371 | }
372 |
373 | override fun zip(dispatchQueue: DispatchQueue, dispatchQueue2: DispatchQueue): DispatchQueue> {
374 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
375 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
376 | val newDispatchQueue = DispatchQueueImpl, Triple>(
377 | blockLabel = getNewDispatchId(),
378 | delayInMillis = 0,
379 | worker = { it },
380 | dispatchQueueInfo = dispatchQueueInfo,
381 | threadHandlerInfo = dispatchQueueInfo.threadHandlerInfo)
382 | newDispatchQueue.addSource(this)
383 | newDispatchQueue.addSource((dispatchQueue as DispatchQueueImpl<*, *>).cloneTo(newDispatchQueueInfo = dispatchQueueInfo))
384 | newDispatchQueue.addSource((dispatchQueue2 as DispatchQueueImpl<*, *>).cloneTo(newDispatchQueueInfo = dispatchQueueInfo))
385 | dispatchQueueInfo.enqueue(newDispatchQueue)
386 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
387 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
388 | return newDispatchQueue
389 | }
390 |
391 | override fun zip(collection: Collection>): DispatchQueue> {
392 | throwIllegalArgumentExceptionIfCollectionEmpty(collection)
393 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
394 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
395 | val dataList = mutableListOf()
396 | var source: DispatchQueueImpl<*, *>
397 | var queue: DispatchQueueImpl<*, *>
398 | for (dispatchQueue in collection) {
399 | source = (dispatchQueue as DispatchQueueImpl<*, *>).cloneTo(newDispatchQueueInfo = dispatchQueueInfo)
400 | queue = DispatchQueueImpl(
401 | blockLabel = getNewDispatchId(),
402 | delayInMillis = 0,
403 | worker = { dataList.add(it) },
404 | dispatchQueueInfo = dispatchQueueInfo,
405 | threadHandlerInfo = dispatchQueueInfo.threadHandlerInfo)
406 | queue.addSource(source)
407 | dispatchQueueInfo.enqueue(queue)
408 | }
409 | val newDispatchQueue = DispatchQueueImpl>(
410 | blockLabel = getNewDispatchId(),
411 | delayInMillis = 0,
412 | worker = {
413 | dataList.add(0, it)
414 | dataList
415 | },
416 | dispatchQueueInfo = dispatchQueueInfo,
417 | threadHandlerInfo = dispatchQueueInfo.threadHandlerInfo)
418 | newDispatchQueue.addSource(this)
419 | dispatchQueueInfo.enqueue(newDispatchQueue)
420 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
421 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
422 | return newDispatchQueue
423 | }
424 |
425 | private fun cloneTo(newDispatchQueueInfo: DispatchQueueInfo): DispatchQueueImpl {
426 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
427 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
428 | val threadHandlerInfo = if (threadHandlerInfo.closeThreadHandler) newDispatchQueueInfo.threadHandlerInfo else threadHandlerInfo
429 | val newDispatchQueue = DispatchQueueImpl(
430 | blockLabel = blockLabel,
431 | delayInMillis = delayInMillis,
432 | worker = worker,
433 | dispatchQueueInfo = newDispatchQueueInfo,
434 | threadHandlerInfo = threadHandlerInfo)
435 | newDispatchQueue.results = results
436 | newDispatchQueue.doOnErrorWorker = doOnErrorWorker
437 | newDispatchQueue.retryCount = retryCount
438 | newDispatchQueue.retryAttempt = 0
439 | newDispatchQueue.retryDelayInMillis = retryDelayInMillis
440 | newDispatchQueue.dispatchQueueObservable.addObservers(dispatchQueueObservable.getObservers())
441 | var source: DispatchQueueImpl<*, *>?
442 | for (dispatchSource in dispatchSources) {
443 | source = dispatchSource
444 | if (source != null) {
445 | newDispatchQueue.addSource(source.cloneTo(newDispatchQueueInfo = newDispatchQueueInfo))
446 | }
447 | }
448 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
449 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
450 | newDispatchQueueInfo.enqueue(newDispatchQueue)
451 | throwIllegalStateExceptionIfStarted(newDispatchQueueInfo)
452 | throwIllegalStateExceptionIfCancelled(newDispatchQueueInfo)
453 | return newDispatchQueue
454 | }
455 |
456 | override fun addObserver(dispatchQueueObserver: DispatchQueueObserver): DispatchQueue {
457 | dispatchQueueObservable.addObserver(dispatchQueueObserver)
458 | return this
459 | }
460 |
461 | override fun addObservers(dispatchQueueObservers: Collection>): DispatchQueue {
462 | dispatchQueueObservable.addObservers(dispatchQueueObservers)
463 | return this
464 | }
465 |
466 | override fun removeObserver(dispatchQueueObserver: DispatchQueueObserver): DispatchQueue {
467 | dispatchQueueObservable.removeObserver(dispatchQueueObserver)
468 | return this
469 | }
470 |
471 | override fun removeObservers(dispatchQueueObservers: Collection>): DispatchQueue {
472 | dispatchQueueObservable.removeObservers(dispatchQueueObservers)
473 | return this
474 | }
475 |
476 | override fun removeObservers(): DispatchQueue {
477 | dispatchQueueObservable.removeObservers()
478 | return this
479 | }
480 |
481 | override fun getDispatchQueueObservable(): DispatchQueueObservable {
482 | return dispatchQueueObservable
483 | }
484 |
485 | override fun setBlockLabel(blockLabel: String): DispatchQueue {
486 | this.blockLabel = blockLabel
487 | return this
488 | }
489 |
490 | override fun retry(count: Int, delayInMillis: Long): DispatchQueue {
491 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
492 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
493 | if (count < 0) {
494 | throw IllegalArgumentException("Count cannot be less than zero")
495 | }
496 | if (delayInMillis < 0) {
497 | throw IllegalArgumentException("Delay cannot be less than zero")
498 | }
499 | this.retryCount = count
500 | this.retryDelayInMillis = delayInMillis
501 | return this
502 | }
503 |
504 | override fun retry(count: Int): DispatchQueue {
505 | return retry(count, 0)
506 | }
507 |
508 | override fun retry(count: Int, timeUnit: TimeUnit, delay: Long): DispatchQueue {
509 | return retry(count, timeUnit.toMillis(delay))
510 | }
511 |
512 | override fun flatMap(func: (R) -> DispatchQueue): DispatchQueue {
513 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
514 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
515 | val newDispatchQueue = DispatchQueueImpl(
516 | blockLabel = getNewDispatchId(),
517 | delayInMillis = 0,
518 | worker = { it },
519 | dispatchQueueInfo = dispatchQueueInfo,
520 | threadHandlerInfo = threadHandlerInfo)
521 | var queue: DispatchQueue? = null
522 | queue = async(func).async { flatMapDispatchQueue ->
523 | val cloneQueueList = mutableListOf>()
524 | (flatMapDispatchQueue as DispatchQueueImpl<*, *>).cloneQueue(dispatchQueueInfo, cloneQueueList)
525 | cloneQueueList.zipWithNext { first, second -> first.next = second }
526 | if (!isCancelled) {
527 | newDispatchQueue.addSource((cloneQueueList.last()), false)
528 | (queue as DispatchQueueImpl<*, *>).next = cloneQueueList.first()
529 | cloneQueueList.last().next = newDispatchQueue
530 | }
531 | }
532 | dispatchQueueInfo.enqueue(newDispatchQueue)
533 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
534 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
535 | return newDispatchQueue
536 | }
537 |
538 | override fun flatMapCollection(func: (R) -> Collection>): DispatchQueue> {
539 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
540 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
541 | val dataList = mutableListOf()
542 | val newDispatchQueue = DispatchQueueImpl>(
543 | blockLabel = getNewDispatchId(),
544 | delayInMillis = 0,
545 | worker = {
546 | dataList
547 | },
548 | dispatchQueueInfo = dispatchQueueInfo,
549 | threadHandlerInfo = threadHandlerInfo)
550 | var queue: DispatchQueue? = null
551 | queue = async(func).async { queueCollection ->
552 | throwIllegalArgumentExceptionIfCollectionEmpty(queueCollection)
553 | val cloneQueueList = mutableListOf>()
554 | for (flatMapDispatchQueue in queueCollection) {
555 | (flatMapDispatchQueue as DispatchQueueImpl<*, *>).cloneQueue(dispatchQueueInfo, cloneQueueList)
556 | val resultDispatchQueue = DispatchQueueImpl(
557 | blockLabel = getNewDispatchId(),
558 | delayInMillis = 0,
559 | worker = { dataList.add(it) },
560 | dispatchQueueInfo = dispatchQueueInfo,
561 | threadHandlerInfo = threadHandlerInfo)
562 | resultDispatchQueue.addSource(cloneQueueList.last(), false)
563 | cloneQueueList.add(resultDispatchQueue)
564 | }
565 | cloneQueueList.zipWithNext { first, second -> first.next = second }
566 | if (!isCancelled) {
567 | newDispatchQueue.addSource((cloneQueueList.last()), false)
568 | (queue as DispatchQueueImpl<*, *>).next = cloneQueueList.first()
569 | cloneQueueList.last().next = newDispatchQueue
570 | }
571 | }
572 | dispatchQueueInfo.enqueue(newDispatchQueue)
573 | throwIllegalStateExceptionIfStarted(dispatchQueueInfo)
574 | throwIllegalStateExceptionIfCancelled(dispatchQueueInfo)
575 | return newDispatchQueue
576 | }
577 |
578 | private fun cloneQueue(newDispatchQueueInfo: DispatchQueueInfo, queueList: MutableList>): DispatchQueueImpl<*, *> {
579 | val threadHandlerInfo = if (threadHandlerInfo.closeThreadHandler) newDispatchQueueInfo.threadHandlerInfo else threadHandlerInfo
580 | val newDispatchQueue = DispatchQueueImpl(
581 | blockLabel = blockLabel,
582 | delayInMillis = delayInMillis,
583 | worker = worker,
584 | dispatchQueueInfo = newDispatchQueueInfo,
585 | threadHandlerInfo = threadHandlerInfo)
586 | newDispatchQueue.results = results
587 | newDispatchQueue.doOnErrorWorker = doOnErrorWorker
588 | newDispatchQueue.retryCount = retryCount
589 | newDispatchQueue.retryAttempt = 0
590 | newDispatchQueue.retryDelayInMillis = retryDelayInMillis
591 | newDispatchQueue.dispatchQueueObservable.addObservers(dispatchQueueObservable.getObservers())
592 | var source: DispatchQueueImpl<*, *>?
593 | for (dispatchSource in dispatchSources) {
594 | source = dispatchSource
595 | if (source != null) {
596 | newDispatchQueue.addSource(source.cloneQueue(newDispatchQueueInfo, queueList), false)
597 | }
598 | }
599 | queueList.add(newDispatchQueue)
600 | return newDispatchQueue
601 | }
602 |
603 | override fun toString(): String {
604 | return "DispatchQueue(blockLabel='$blockLabel', id='${dispatchQueueInfo.queueId}')"
605 | }
606 |
607 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/internals/DispatchQueueInfo.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.internals
2 |
3 | import com.tonyodev.dispatch.DispatchQueueErrorCallback
4 | import com.tonyodev.dispatch.queuecontroller.DispatchQueueController
5 | import com.tonyodev.dispatch.utils.Threader
6 |
7 | internal class DispatchQueueInfo(val queueId: Int,
8 | val isIntervalDispatch: Boolean = false,
9 | val threadHandlerInfo: Threader.ThreadHandlerInfo) {
10 | @Volatile
11 | var isCancelled = false
12 | @Volatile
13 | var isStarted = false
14 | var dispatchQueueErrorCallback: DispatchQueueErrorCallback? = null
15 | var dispatchQueueController: DispatchQueueController? = null
16 | var rootDispatchQueue: DispatchQueueImpl<*, *>? = null
17 | var endDispatchQueue: DispatchQueueImpl<*, *>? = null
18 |
19 | fun canPerformOperations(): Boolean {
20 | return !isStarted && !isCancelled
21 | }
22 |
23 | fun enqueue(dispatchQueueImpl: DispatchQueueImpl<*, *>) {
24 | synchronized(this) {
25 | if (canPerformOperations()) {
26 | if (rootDispatchQueue == null) {
27 | rootDispatchQueue = dispatchQueueImpl
28 | endDispatchQueue = dispatchQueueImpl
29 | } else {
30 | endDispatchQueue?.setNext(dispatchQueueImpl)
31 | endDispatchQueue = dispatchQueueImpl
32 | }
33 | }
34 | }
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/queuecontroller/CancelType.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.queuecontroller
2 |
3 | /**
4 | * Cancel Type used with a Dispatch queues
5 | * that are being managed by a LifecycleDispatchQueueController
6 | * */
7 | enum class CancelType {
8 |
9 | /**
10 | * Reflects the paused state.
11 | * */
12 | PAUSED,
13 |
14 | /**
15 | * Reflects the stopped state.
16 | * */
17 | STOPPED,
18 |
19 | /**
20 | * Reflects the destroyed state.
21 | * */
22 | DESTROYED;
23 |
24 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/queuecontroller/DispatchQueueController.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.queuecontroller
2 |
3 | import com.tonyodev.dispatch.DispatchQueue
4 |
5 | /**
6 | * A DispatchQueueController manages dispatch queues and is
7 | * responsible for cancelling a dispatch queue at the appropriate time.
8 | * */
9 | open class DispatchQueueController {
10 |
11 | private val dispatchQueueMap = mutableMapOf>()
12 |
13 | /**
14 | * Set this dispatch queue controller to manage the passed in dispatch queue.
15 | * @param dispatchQueue the dispatchQueue who's queue will be managed.
16 | * */
17 | open fun manage(dispatchQueue: DispatchQueue<*>) {
18 | synchronized(dispatchQueueMap) {
19 | dispatchQueueMap[dispatchQueue.id] = dispatchQueue
20 | }
21 | }
22 |
23 | /**
24 | * Set this dispatch queue controller to manage the passed in dispatch queue.
25 | * @param dispatchQueueList a list of dispatch who's queue will be managed.
26 | * */
27 | open fun manage(dispatchQueueList: List>) {
28 | synchronized(dispatchQueueMap) {
29 | for (dispatchQueue in dispatchQueueList) {
30 | dispatchQueueMap[dispatchQueue.id] = dispatchQueue
31 | }
32 | }
33 | }
34 |
35 | /**
36 | * Stop managing the passed in dispatch queue.
37 | * @param dispatchQueue the dispatch queue to unmanage.
38 | * */
39 | open fun unmanage(dispatchQueue: DispatchQueue<*>) {
40 | synchronized(dispatchQueueMap) {
41 | dispatchQueueMap.remove(dispatchQueue.id)
42 | }
43 | }
44 |
45 | /**
46 | * Stop managing the passed in list dispatch queues.
47 | * @param dispatchQueueList the list of dispatch queues to unmanage.
48 | * */
49 | open fun unmanage(dispatchQueueList: List>) {
50 | synchronized(dispatchQueueMap) {
51 | for (dispatchQueue in dispatchQueueList) {
52 | dispatchQueueMap.remove(dispatchQueue.id)
53 | }
54 | }
55 | }
56 |
57 | /**
58 | * Cancels all the dispatch queues that are being managed by this
59 | * instance of DispatchQueueController. All the dispatch queues will then be unmanaged.
60 | * */
61 | open fun cancelAllDispatchQueues() {
62 | synchronized(dispatchQueueMap) {
63 | val iterator = dispatchQueueMap.iterator()
64 | var dispatchQueue: DispatchQueue<*>
65 | while (iterator.hasNext()) {
66 | dispatchQueue = iterator.next().value
67 | iterator.remove()
68 | dispatchQueue.cancel()
69 | }
70 | }
71 | }
72 |
73 | /**
74 | * Cancels all dispatch queues being managed by this
75 | * instance of DispatchQueueController if it is in the passed in array.
76 | * The dispatch queue will no longer be managed.
77 | * @param arrayOfDispatchQueues dispatch queue objects.
78 | * */
79 | open fun cancelDispatchQueues(vararg arrayOfDispatchQueues: DispatchQueue<*>) {
80 | cancelDispatchQueues(arrayOfDispatchQueues.map { it })
81 | }
82 |
83 | /**
84 | * Cancels all dispatch queues being managed by this
85 | * instance of DispatchQueueController if it is in the passed in collection.
86 | * The dispatch queue will no longer be managed.
87 | * @param dispatchQueueCollection dispatch queues.
88 | * */
89 | open fun cancelDispatchQueues(dispatchQueueCollection: Collection>) {
90 | synchronized(dispatchQueueMap) {
91 | for (dispatchQueue in dispatchQueueCollection) {
92 | dispatchQueueMap.remove(dispatchQueue.id)?.cancel()
93 | }
94 | }
95 | }
96 |
97 | /**
98 | * Cancels dispatch queues being managed by this
99 | * instance of DispatchQueueController if it's queue id is in the passed in list.
100 | * The dispatch queue will no longer be managed.
101 | * @param dispatchQueueIds the dispatch queue ids.
102 | * */
103 | open fun cancelDispatchQueues(dispatchQueueIds: List) {
104 | synchronized(dispatchQueueMap) {
105 | for (dispatchQueueId in dispatchQueueIds) {
106 | dispatchQueueMap.remove(dispatchQueueId)?.cancel()
107 | }
108 | }
109 | }
110 |
111 | /**
112 | * Cancels the dispatch queue being managed by this
113 | * instance of DispatchQueueController if it's queue id is in the passed in array.
114 | * The dispatch queues will no longer be managed.
115 | * @param arrayOfDispatchQueueId the dispatch objects queue ids.
116 | * */
117 | open fun cancelDispatchQueues(vararg arrayOfDispatchQueueId: Int) {
118 | cancelDispatchQueues(arrayOfDispatchQueueId.toList())
119 | }
120 |
121 | /**
122 | * Gets a copy of the managed queues.
123 | * @return copy of the manged queues in a set.
124 | * */
125 | fun getManagedDispatchQueues(): Set> {
126 | return synchronized(dispatchQueueMap) { dispatchQueueMap.values.toSet() }
127 | }
128 |
129 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/queuecontroller/LifecycleDispatchQueueController.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.queuecontroller
2 |
3 | import com.tonyodev.dispatch.DispatchQueue
4 |
5 | /**
6 | * A DispatchQueueController that uses lifecycle events to manage
7 | * Dispatch queues. It is a class that must be extended
8 | * and controlled by a lifecycle aware component.
9 | * */
10 | open class LifecycleDispatchQueueController: DispatchQueueController() {
11 |
12 | private val queueMap = mutableMapOf, CancelType>>()
13 |
14 | /**
15 | * Cancels all dispatch queues that are being managed by this
16 | * DispatchQueueController with a cancel type of CancelType.PAUSED.
17 | * */
18 | open fun cancelAllPaused() {
19 | synchronized(queueMap) {
20 | val pausedList = queueMap.asSequence()
21 | .filter { it.value.second == CancelType.PAUSED }
22 | .map { it.value.first }
23 | .toList()
24 | super.cancelDispatchQueues(pausedList)
25 | for (dispatchQueue in pausedList) {
26 | queueMap.remove(dispatchQueue.id)
27 | }
28 | }
29 | }
30 |
31 | /**
32 | * Cancels all dispatch queues that are being managed by this
33 | * DispatchQueueController with a cancel type of CancelType.STOPPED.
34 | * */
35 | open fun cancelAllStopped() {
36 | synchronized(queueMap) {
37 | val stoppedList = queueMap.asSequence()
38 | .filter { it.value.second == CancelType.STOPPED }
39 | .map { it.value.first }
40 | .toList()
41 | super.cancelDispatchQueues(stoppedList)
42 | for (dispatchQueue in stoppedList) {
43 | queueMap.remove(dispatchQueue.id)
44 | }
45 | }
46 | }
47 |
48 | /**
49 | * Cancels all dispatch queues that are being managed by this
50 | * DispatchQueueController with a cancel type of CancelType.DESTROYED.
51 | * */
52 | open fun cancelAllDestroyed() {
53 | synchronized(queueMap) {
54 | super.cancelAllDispatchQueues()
55 | queueMap.clear()
56 | }
57 | }
58 |
59 | override fun unmanage(dispatchQueue: DispatchQueue<*>) {
60 | synchronized(queueMap) {
61 | super.unmanage(dispatchQueue)
62 | queueMap.remove(dispatchQueue.id)
63 | }
64 | }
65 |
66 | override fun unmanage(dispatchQueueList: List>) {
67 | synchronized(queueMap) {
68 | super.unmanage(dispatchQueueList)
69 | for (dispatch in dispatchQueueList) {
70 | queueMap.remove(dispatch.id)
71 | }
72 | }
73 | }
74 |
75 | override fun manage(dispatchQueue: DispatchQueue<*>) {
76 | manage(dispatchQueue, CancelType.DESTROYED)
77 | }
78 |
79 | /**
80 | * Set this dispatch queue controller to manage the passed in dispatch queue with
81 | * the passed in cancelType.
82 | * @param dispatchQueue the dispatchQueue who's queue will be managed.
83 | * @param cancelType the cancel type.
84 | * */
85 | fun manage(dispatchQueue: DispatchQueue<*>, cancelType: CancelType) {
86 | synchronized(queueMap) {
87 | super.manage(dispatchQueue)
88 | queueMap[dispatchQueue.id] = Pair(dispatchQueue, cancelType)
89 | }
90 | }
91 |
92 | override fun manage(dispatchQueueList: List>) {
93 | synchronized(queueMap) {
94 | super.manage(dispatchQueueList)
95 | for (dispatchQueue in dispatchQueueList) {
96 | queueMap[dispatchQueue.id] = Pair(dispatchQueue, CancelType.DESTROYED)
97 | }
98 | }
99 | }
100 |
101 | override fun cancelAllDispatchQueues() {
102 | cancelAllDestroyed()
103 | }
104 |
105 | override fun cancelDispatchQueues(vararg arrayOfDispatchQueues: DispatchQueue<*>) {
106 | synchronized(queueMap) {
107 | super.cancelDispatchQueues(arrayOfDispatchQueues.toList())
108 | for (dispatch in arrayOfDispatchQueues) {
109 | queueMap.remove(dispatch.id)
110 | }
111 | }
112 | }
113 |
114 | override fun cancelDispatchQueues(dispatchQueueIds: List) {
115 | synchronized(queueMap) {
116 | super.cancelDispatchQueues(dispatchQueueIds)
117 | for (id in dispatchQueueIds) {
118 | queueMap.remove(id)
119 | }
120 | }
121 | }
122 |
123 | override fun cancelDispatchQueues(vararg arrayOfDispatchQueueId: Int) {
124 | cancelDispatchQueues(arrayOfDispatchQueueId.toList())
125 | }
126 |
127 | override fun cancelDispatchQueues(dispatchQueueCollection: Collection>) {
128 | synchronized(queueMap) {
129 | super.cancelDispatchQueues(dispatchQueueCollection)
130 | for (dispatchQueue in dispatchQueueCollection) {
131 | queueMap.remove(dispatchQueue.id)
132 | }
133 | }
134 | }
135 |
136 | /**
137 | * Gets a copy of the paused managed queues.
138 | * @return copy of the paused manged queues in a set.
139 | * */
140 | fun getManagedPausedDispatchQueues(): Set> {
141 | return synchronized(queueMap) {
142 | queueMap.asSequence()
143 | .filter { it.value.second == CancelType.PAUSED }
144 | .map { it.value.first }
145 | .toSet()
146 | }
147 | }
148 |
149 | /**
150 | * Gets a copy of the stopped managed queues.
151 | * @return copy of the stopped manged queues in a set.
152 | * */
153 | fun getManagedStoppedDispatchQueues(): Set> {
154 | return synchronized(queueMap) {
155 | queueMap.asSequence()
156 | .filter { it.value.second == CancelType.STOPPED }
157 | .map { it.value.first }
158 | .toSet()
159 | }
160 | }
161 |
162 | /**
163 | * Gets a copy of the destroyed managed queues.
164 | * @return copy of the destroyed manged queues in a set.
165 | * */
166 | fun getManagedDestroyedDispatchQueues(): Set> {
167 | return synchronized(queueMap) {
168 | queueMap.asSequence()
169 | .map { it.value.first }
170 | .toSet()
171 | }
172 | }
173 |
174 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/DefaultThreadHandler.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | import java.lang.IllegalStateException
4 |
5 | /**
6 | * The default Thread Handler. Performs it works on a plain old thread.
7 | * */
8 | class DefaultThreadHandler(override val threadName: String): Thread(), ThreadHandler {
9 |
10 | private val queue = mutableListOf()
11 | private val minQueueIndexPair = MinQueueIndexPair()
12 | private val queueItemPool = ThreadHandlerQueueItem.Pool()
13 | private val defaultSleepTime = 250L
14 |
15 | @Volatile
16 | private var isCancelled = false
17 | @Volatile
18 | private var isSleeping = false
19 | private var queueIndex = 0
20 | private var queueItem: ThreadHandlerQueueItem? = null
21 | private var isQueueEmpty = true
22 | private var threadSleepMillis = defaultSleepTime
23 |
24 | override val isActive: Boolean
25 | get() {
26 | if (isCancelled) {
27 | return false
28 | }
29 | return isAlive
30 | }
31 |
32 | init {
33 | name = threadName
34 | }
35 |
36 | override fun run() {
37 | while (!isCancelled) {
38 | synchronized(queue) {
39 | isQueueEmpty = queue.isEmpty()
40 | }
41 | if(isQueueEmpty) {
42 | sleep()
43 | } else {
44 | if (!isCancelled) {
45 | synchronized(queue) {
46 | if (queueIndex < queue.size) {
47 | queueItem = queue[queueIndex]
48 | }
49 | }
50 | if (!isCancelled) {
51 | if (queueItem != null) {
52 | if (queueItem!!.isReady) {
53 | if (!isCancelled) {
54 | synchronized(queue) {
55 | queueItem!!.isProcessing = true
56 | }
57 | queueItem!!.runnable?.run()
58 | }
59 | val item = queueItem
60 | if (item != null) {
61 | if (!isCancelled) {
62 | synchronized(queue) {
63 | queue.remove(item)
64 | }
65 | }
66 | queueItemPool.recycle(item)
67 | }
68 | queueIndex = 0
69 | } else {
70 | val minQueuePair1 = findQueueMinIndex(queue, minQueueIndexPair)
71 | queueIndex = if (minQueuePair1.index > -1) minQueuePair1.index else 0
72 | if (minQueuePair1.waitTime > 0) {
73 | threadSleepMillis = minQueuePair1.waitTime
74 | sleep(false)
75 | }
76 | }
77 | queueItem = null
78 | } else {
79 | queueIndex = 0
80 | threadSleepMillis = defaultSleepTime
81 | sleep()
82 | }
83 | } else {
84 | val item = queueItem
85 | if (item != null) {
86 | queueItemPool.recycle(item)
87 | }
88 | queueItem = null
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
95 | private fun sleep(backOffSleepTime: Boolean = true) {
96 | if (!isCancelled) {
97 | try {
98 | isSleeping = true
99 | sleep(threadSleepMillis)
100 | if (backOffSleepTime) {
101 | threadSleepMillis *= 2
102 | } else {
103 | threadSleepMillis = defaultSleepTime
104 | }
105 | isSleeping = false
106 | } catch (e: InterruptedException) {
107 | threadSleepMillis = defaultSleepTime
108 | isSleeping = false
109 | }
110 | }
111 | }
112 |
113 | override fun post(runnable: Runnable) {
114 | postDelayed(0, runnable)
115 | }
116 |
117 | override fun postDelayed(delayInMilliseconds: Long, runnable: Runnable) {
118 | if (isCancelled) {
119 | throw IllegalStateException("Cannot post runnable because quit() was already called.")
120 | }
121 | val queueItem = queueItemPool.obtain(delayInMilliseconds, runnable)
122 | synchronized(queue) {
123 | queue.add(queueItem)
124 | }
125 | if (isSleeping) {
126 | interrupt()
127 | }
128 | }
129 |
130 | override fun removeCallbacks(runnable: Runnable) {
131 | synchronized(queue) {
132 | val iterator = queue.iterator()
133 | var queueItem: ThreadHandlerQueueItem
134 | while (iterator.hasNext()) {
135 | queueItem = iterator.next()
136 | if (queueItem.runnable == runnable) {
137 | iterator.remove()
138 | if (!queueItem.isProcessing) {
139 | queueItemPool.recycle(queueItem)
140 | }
141 | }
142 | }
143 | }
144 | }
145 |
146 | override fun quit() {
147 | if (!isCancelled) {
148 | isCancelled = true
149 | if (isSleeping) {
150 | interrupt()
151 | }
152 | isSleeping = false
153 | synchronized(queue) {
154 | val iterator = queue.iterator()
155 | var queueItem: ThreadHandlerQueueItem
156 | while (iterator.hasNext()) {
157 | queueItem = iterator.next()
158 | iterator.remove()
159 | if (!queueItem.isProcessing) {
160 | queueItemPool.recycle(queueItem)
161 | }
162 | }
163 | }
164 | isQueueEmpty = true
165 | queueIndex = 0
166 | threadSleepMillis = defaultSleepTime
167 | minQueueIndexPair.waitTime = -1
168 | minQueueIndexPair.index = -1
169 | queueItemPool.release()
170 | }
171 | }
172 |
173 | override fun start() {
174 | super.start()
175 | }
176 |
177 | class MinQueueIndexPair {
178 | var index = -1
179 | var waitTime = -1L
180 | }
181 |
182 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/DefaultThreadHandlerFactory.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | import com.tonyodev.dispatch.ThreadType
4 | import com.tonyodev.dispatch.utils.*
5 |
6 | /**
7 | * The default ThreadHandler Factory used by the library.
8 | * */
9 | class DefaultThreadHandlerFactory: ThreadHandlerFactory {
10 |
11 | @Volatile
12 | private var newThreadCount = 0
13 |
14 | override fun create(threadType: ThreadType): ThreadHandler {
15 | val threadHandler = when(threadType) {
16 | ThreadType.BACKGROUND -> DefaultThreadHandler(THREAD_BACKGROUND)
17 | ThreadType.IO -> DefaultThreadHandler(THREAD_IO)
18 | ThreadType.NETWORK -> DefaultThreadHandler(THREAD_NETWORK)
19 | ThreadType.COMPUTATION -> DefaultThreadHandler(THREAD_COMPUTATION)
20 | ThreadType.NEW -> getNewThreadHandler()
21 | ThreadType.MAIN -> DefaultThreadHandler(THREAD_MAIN_NO_UI)
22 | ThreadType.TEST -> TestThreadHandler(THREAD_TEST)
23 | }
24 | startThreadHandlerIfNotActive(threadHandler)
25 | return threadHandler
26 | }
27 |
28 | override fun create(threadName: String?): ThreadHandler {
29 | return getNewThreadHandler(threadName)
30 | }
31 |
32 | private fun getNewThreadHandler(name: String? = null): ThreadHandler {
33 | val threadName = if (name == null || name.isEmpty()) {
34 | "DispatchQueue${++newThreadCount}"
35 | } else {
36 | name
37 | }
38 | val threadHandler = DefaultThreadHandler(threadName)
39 | startThreadHandlerIfNotActive(threadHandler)
40 | return threadHandler
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/DefaultThreadHandlerUtils.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | fun findQueueMinIndex(queue: List, minQueueIndexPair: DefaultThreadHandler.MinQueueIndexPair): DefaultThreadHandler.MinQueueIndexPair {
4 | return synchronized(queue) {
5 | minQueueIndexPair.index = -1
6 | minQueueIndexPair.waitTime = -1
7 | var minDelay = -1L
8 | var counter = 0
9 | var queueItem: ThreadHandlerQueueItem
10 | while (counter < queue.size) {
11 | queueItem = queue[counter]
12 | if (minDelay == -1L || queueItem.delay < minDelay) {
13 | minQueueIndexPair.index = counter
14 | minDelay = queueItem.delay
15 | minQueueIndexPair.waitTime = queueItem.delay - queueItem.waitTime
16 | if (minQueueIndexPair.waitTime < 0) {
17 | minQueueIndexPair.waitTime = 0
18 | }
19 | }
20 | counter++
21 | }
22 | minQueueIndexPair
23 | }
24 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/TestThreadHandler.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | /** The default test thread handler */
4 | class TestThreadHandler(override val threadName: String): ThreadHandler {
5 |
6 | override fun post(runnable: Runnable) {
7 | postDelayed(0, runnable)
8 | }
9 |
10 | override fun postDelayed(delayInMilliseconds: Long, runnable: Runnable) {
11 | synchronized(this) {
12 | if (isActive) {
13 | runnable.run()
14 | }
15 | }
16 | }
17 |
18 | override fun removeCallbacks(runnable: Runnable) {
19 |
20 | }
21 |
22 | override fun quit() {
23 | synchronized(this) {
24 | isActive = false
25 | }
26 | }
27 |
28 | override var isActive: Boolean = false
29 |
30 | override fun start() {
31 | synchronized(this) {
32 | isActive = true
33 | }
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/ThreadHandler.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | /**
4 | * Wrapper interface used to post work on the underlining thread. The thread can be any class such
5 | * as Thread, Executor or android.os.Handler. Classes extending this interface needs to handle
6 | * synchronous access.
7 | * */
8 | interface ThreadHandler {
9 |
10 | /**
11 | * The thread name.
12 | * */
13 | val threadName: String
14 |
15 | /**
16 | * Checks if this threadHandler is alive.
17 | * */
18 | val isActive: Boolean
19 |
20 | /**
21 | * Posts the runnable to the thread.
22 | * @param runnable the runnable to post.
23 | * */
24 | fun post(runnable: Runnable)
25 |
26 | /**
27 | * Posts the runnable to the thread delayed
28 | * @param delayInMilliseconds the delay in milliseconds
29 | * @param runnable the runnable to post.
30 | * */
31 | fun postDelayed(delayInMilliseconds: Long, runnable: Runnable)
32 |
33 | /**
34 | * Removes the runnable from the threads processing queue.
35 | * @param runnable the runnable to remove.
36 | * */
37 | fun removeCallbacks(runnable: Runnable)
38 |
39 | /**
40 | * Terminate the underlining thread that is handling the runnable work.
41 | * */
42 | fun quit()
43 |
44 | /**
45 | * Starts the thread handler if it is not already started.
46 | * */
47 | fun start()
48 |
49 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/ThreadHandlerFactory.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | import com.tonyodev.dispatch.ThreadType
4 |
5 | /**
6 | * Interface that can be extend to create a Factory that creates thread handlers.
7 | * */
8 | interface ThreadHandlerFactory {
9 |
10 | /**
11 | * Creates a thread Handler.
12 | * @param threadType the threadType.
13 | * @return thread handler.
14 | * */
15 | fun create(threadType: ThreadType): ThreadHandler
16 |
17 | /**
18 | * Creates a thread Handler with the passed in threadName.
19 | * @param threadName the thread name. If null, a name will be automatically generated.
20 | * @return thread handler.
21 | * */
22 | fun create(threadName: String?): ThreadHandler
23 |
24 |
25 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/thread/ThreadHandlerQueueItem.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.thread
2 |
3 | /**
4 | * Class used to queue runnables inside of a DefaultThreadHandler.
5 | * The ThreadHandlerQueueItem also keeps a record of how much time
6 | * it has waited in the queue.
7 | * */
8 | class ThreadHandlerQueueItem private constructor() {
9 |
10 | /**The delay before this queue item is removed from the default thread handler queue and executed.*/
11 | var delay: Long = 0
12 | private set
13 |
14 | /** The runnable that will be executed by the default thread handler.*/
15 | var runnable: Runnable? = null
16 | private set
17 |
18 | /** Check is the queue item is currently being processed by a default thread handler.*/
19 | var isProcessing = false
20 |
21 | /** Checks how long in milliseconds a queue item has been waiting in a default thread handler queue.*/
22 | val waitTime: Long
23 | get() {
24 | return if (startWaitTime == 0L) 0 else System.currentTimeMillis() - startWaitTime
25 | }
26 |
27 | /** Checks if a queue item is ready to be queued by the default thread handler.*/
28 | val isReady: Boolean
29 | get() {
30 | return (delay < 1 || waitTime >= delay) && !isRecycled
31 | }
32 |
33 | private var startWaitTime = 0L
34 | private var next: ThreadHandlerQueueItem? = null
35 | private var isRecycled = false
36 |
37 | /**
38 | * Class used to maintain an ThreadHandlerQueueItem pool.
39 | * */
40 | class Pool(
41 | /**
42 | * The max pool size. The default size is 50
43 | * */
44 | val maxPoolSize: Int = 50) {
45 |
46 | private var pool: ThreadHandlerQueueItem? = null
47 |
48 | /**
49 | * The pool size
50 | * */
51 | var size = 0
52 | private set
53 |
54 | /**
55 | * Obtains a ThreadHandlerQueueItem from the pool.
56 | * @param delayInMilliseconds the delay in milliseconds before the runnable runs.
57 | * @param runnable the runnable that will be executed by the thread handler.
58 | * @return a ThreadHandlerQueueItem instance.
59 | * */
60 | fun obtain(delayInMilliseconds: Long, runnable: Runnable): ThreadHandlerQueueItem {
61 | return synchronized(this) {
62 | val queueItem: ThreadHandlerQueueItem
63 | if (pool == null) {
64 | queueItem = ThreadHandlerQueueItem()
65 | } else {
66 | queueItem = pool!!
67 | pool = queueItem.next
68 | queueItem.next = null
69 | queueItem.isRecycled = false
70 | size--
71 | }
72 | queueItem.delay = delayInMilliseconds
73 | queueItem.runnable = runnable
74 | queueItem.startWaitTime = System.currentTimeMillis()
75 | queueItem
76 | }
77 | }
78 |
79 | /**
80 | * Recycles a ThreadHandlerQueueItem. The ThreadHandlerQueueItem is added to this pool
81 | * if the max pool size is not reached.
82 | * */
83 | fun recycle(threadHandlerQueueItem: ThreadHandlerQueueItem) {
84 | synchronized(this) {
85 | if (!threadHandlerQueueItem.isRecycled) {
86 | threadHandlerQueueItem.startWaitTime = 0
87 | threadHandlerQueueItem.delay = 0
88 | threadHandlerQueueItem.runnable = null
89 | threadHandlerQueueItem.isRecycled = true
90 | threadHandlerQueueItem.isProcessing = false
91 | if (size <= maxPoolSize) {
92 | threadHandlerQueueItem.next = pool
93 | pool = threadHandlerQueueItem
94 | size++
95 | }
96 | }
97 | }
98 | }
99 |
100 | /**
101 | * Release pool
102 | * */
103 | fun release() {
104 | synchronized(this) {
105 | var current = pool
106 | while (current != null) {
107 | pool = current.next
108 | current.next = null
109 | current = pool
110 | }
111 | pool = null
112 | size = 0
113 | }
114 | }
115 |
116 | }
117 |
118 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/utils/Consts.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.utils
2 |
3 | import com.tonyodev.dispatch.DispatchQueueErrorCallback
4 |
5 | const val TAG = "DispatchQueue"
6 | internal val INVALID_RESULT = InvalidResult()
7 | internal val CANCELLED_ERROR_CALLBACK = DispatchQueueErrorCallback { }
8 | const val THREAD_BACKGROUND = "DispatchQueueBackground"
9 | const val THREAD_IO = "DispatchQueueIO"
10 | const val THREAD_TEST = "DispatchQueueTest"
11 | const val THREAD_MAIN_NO_UI = "DispatchQueueMain"
12 | const val THREAD_NETWORK = "DispatchQueueNetwork"
13 | const val THREAD_COMPUTATION = "DispatchQueueComputation"
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/utils/DefaultLogger.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.utils
2 |
3 | class DefaultLogger: Logger {
4 |
5 | override fun print(tag: String, message: String) {
6 | println("$tag: $message")
7 | }
8 |
9 | override fun print(tag: String, throwable: Throwable) {
10 | println("$tag: ${throwable.message}: $throwable")
11 | }
12 |
13 | override fun print(tag: String, any: Any?) {
14 | println("$tag: $any")
15 | }
16 |
17 | override fun print(message: String) {
18 | println("$TAG: $message")
19 | }
20 |
21 | override fun print(throwable: Throwable) {
22 | println("$TAG: ${throwable.message}: $throwable")
23 | }
24 |
25 | override fun print(any: Any?) {
26 | println("$TAG: $any")
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/utils/InvalidResult.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.utils
2 |
3 | internal class InvalidResult
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/utils/Logger.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.utils
2 |
3 | /**
4 | * Logger interface used by the library. Can be used to created custom loggers.
5 | * */
6 | interface Logger {
7 |
8 | /**
9 | * log tag and message.
10 | * @param tag the tag.
11 | * @param message the message.
12 | * */
13 | fun print(tag: String, message: String)
14 |
15 | /**
16 | * log tag and throwable.
17 | * @param tag the tag.
18 | * @param throwable the throwable.
19 | * */
20 | fun print(tag: String, throwable: Throwable)
21 |
22 | /**
23 | * log tag and message.
24 | * @param tag the tag.
25 | * @param any the object.
26 | * */
27 | fun print(tag: String, any: Any?)
28 |
29 | /**
30 | * log message.
31 | * @param message the message.
32 | * */
33 | fun print(message: String)
34 |
35 | /**
36 | * log throwable.
37 | * @param throwable the throwable.
38 | * */
39 | fun print(throwable: Throwable)
40 |
41 | /**
42 | * log object.
43 | * @param any the object.
44 | * */
45 | fun print(any: Any?)
46 |
47 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/utils/Threader.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.utils
2 |
3 |
4 | import com.tonyodev.dispatch.DispatchQueue
5 | import com.tonyodev.dispatch.ThreadType
6 | import com.tonyodev.dispatch.thread.ThreadHandler
7 |
8 | internal object Threader {
9 |
10 | private val backgroundThreadHandlerInfo by lazy { ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.BACKGROUND), false) }
11 |
12 | private val uiThreadHandlerInfo by lazy { ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.MAIN), false) }
13 |
14 | private val networkThreadHandlerInfo by lazy { ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.NETWORK), false) }
15 |
16 | private val ioThreadHandlerInfo by lazy { ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.IO), false) }
17 |
18 | private val computationThreadHandlerInfo by lazy { ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.COMPUTATION), false) }
19 |
20 | private val testThreadHandlerInfo by lazy { ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.TEST), false) }
21 |
22 | fun getHandlerThreadInfo(threadType: ThreadType): ThreadHandlerInfo {
23 | return when(threadType) {
24 | ThreadType.BACKGROUND -> backgroundThreadHandlerInfo
25 | ThreadType.IO -> ioThreadHandlerInfo
26 | ThreadType.NETWORK -> networkThreadHandlerInfo
27 | ThreadType.COMPUTATION -> computationThreadHandlerInfo
28 | ThreadType.MAIN -> uiThreadHandlerInfo
29 | ThreadType.TEST -> testThreadHandlerInfo
30 | ThreadType.NEW -> ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.NEW), true)
31 | }
32 | }
33 |
34 | fun getHandlerThreadInfo(threadName: String): ThreadHandlerInfo {
35 | return ThreadHandlerInfo(DispatchQueue.globalSettings.threadHandlerFactory.create(threadName), true)
36 | }
37 |
38 | data class ThreadHandlerInfo(val threadHandler: ThreadHandler, val closeThreadHandler: Boolean) {
39 | val threadName: String
40 | get() {
41 | return threadHandler.threadName
42 | }
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/dispatch/src/main/java/com/tonyodev/dispatch/utils/Utils.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch.utils
2 |
3 | import com.tonyodev.dispatch.ThreadType
4 | import com.tonyodev.dispatch.internals.DispatchQueueInfo
5 | import com.tonyodev.dispatch.thread.ThreadHandler
6 | import java.lang.IllegalArgumentException
7 | import java.lang.IllegalStateException
8 | import java.util.*
9 |
10 | internal fun getNewQueueId(): Int {
11 | return UUID.randomUUID().hashCode()
12 | }
13 |
14 | internal fun getNewDispatchId(): String {
15 | return UUID.randomUUID().toString()
16 | }
17 |
18 | internal fun throwIfUsesMainThreadForBackgroundWork(threadHandler: ThreadHandler?) {
19 | if (threadHandler != null && threadHandler.threadName == Threader.getHandlerThreadInfo(ThreadType.MAIN).threadName) {
20 | throw IllegalArgumentException("DispatchQueue cannot use the main threadHandler to perform background work." +
21 | "Pass in a threadHandler that uses a different thread.")
22 | }
23 | }
24 |
25 | internal fun throwIfUsesMainThreadForBackgroundWork(threadType: ThreadType) {
26 | if (threadType == ThreadType.MAIN) {
27 | throw IllegalArgumentException("DispatchQueue cannot use the main threadHandler to perform background work." +
28 | "Pass in a threadHandler that uses a different thread.")
29 | }
30 | }
31 |
32 | internal fun throwIllegalStateExceptionIfCancelled(dispatchQueueInfo: DispatchQueueInfo) {
33 | if (dispatchQueueInfo.isCancelled) {
34 | throw IllegalStateException("DispatchQueue with id: ${dispatchQueueInfo.queueId} has already been cancelled. Cannot perform new operations.")
35 | }
36 | }
37 |
38 | internal fun throwIllegalStateExceptionIfStarted(dispatchQueueInfo: DispatchQueueInfo) {
39 | if (dispatchQueueInfo.isStarted) {
40 | throw IllegalStateException("DispatchQueue with id: ${dispatchQueueInfo.queueId} has already been started. Cannot add new operations.")
41 | }
42 | }
43 |
44 | internal fun throwIllegalArgumentExceptionIfCollectionEmpty(collection: Collection) {
45 | if (collection.isEmpty()) {
46 | throw IllegalArgumentException("Cannot pass in an empty collection.")
47 | }
48 | }
49 |
50 | internal fun startThreadHandlerIfNotActive(threadHandler: ThreadHandler) {
51 | if (!threadHandler.isActive) {
52 | threadHandler.start()
53 | }
54 | }
--------------------------------------------------------------------------------
/dispatch/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Dispatch
3 |
4 |
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/DefaultThreadHandlerFactoryTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.DefaultThreadHandlerFactory
4 | import com.tonyodev.dispatch.utils.THREAD_BACKGROUND
5 | import com.tonyodev.dispatch.utils.THREAD_MAIN_NO_UI
6 | import org.junit.Test
7 |
8 | class DefaultThreadHandlerFactoryTest {
9 |
10 | private val defaultThreadHandlerFactory = DefaultThreadHandlerFactory()
11 |
12 | @Test
13 | fun testCreateBackgroundThread() {
14 | val thread = defaultThreadHandlerFactory.create(ThreadType.BACKGROUND)
15 | assert(thread.threadName == THREAD_BACKGROUND)
16 | thread.quit()
17 | assert(!thread.isActive)
18 | }
19 |
20 | @Test
21 | fun testCreateMainThread() {
22 | val thread = defaultThreadHandlerFactory.create(ThreadType.MAIN)
23 | assert(thread.threadName == THREAD_MAIN_NO_UI)
24 | thread.quit()
25 | assert(!thread.isActive)
26 | }
27 |
28 | @Test
29 | fun testCreatedThreadActive() {
30 | val thread = defaultThreadHandlerFactory.create(ThreadType.BACKGROUND)
31 | assert(thread.isActive)
32 | thread.quit()
33 | assert(!thread.isActive)
34 | }
35 |
36 | @Test
37 | fun testCreatedThreadByName() {
38 | val name = "sampleThread"
39 | val thread = defaultThreadHandlerFactory.create(name)
40 | assert(thread.threadName == name)
41 | thread.quit()
42 | assert(!thread.isActive)
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/DefaultThreadHandlerTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.DefaultThreadHandler
4 | import com.tonyodev.dispatch.thread.ThreadHandlerQueueItem
5 | import com.tonyodev.dispatch.thread.findQueueMinIndex
6 | import org.junit.Test
7 |
8 | class DefaultThreadHandlerTest {
9 |
10 | @Test
11 | fun testName() {
12 | val name = "testThread"
13 | val thread = DefaultThreadHandler(name)
14 | assert(thread.threadName == name)
15 | assert(thread.name == name)
16 | }
17 |
18 | @Test
19 | fun testStart() {
20 | val name = "testThread"
21 | val thread = DefaultThreadHandler(name)
22 | assert(!thread.isActive)
23 | assert(!thread.isAlive)
24 | thread.start()
25 | assert(thread.isAlive)
26 | assert(thread.isActive)
27 | }
28 |
29 | @Test
30 | fun testMinQueueIndex() {
31 | val pool = ThreadHandlerQueueItem.Pool()
32 | val item = pool.obtain(4, Runnable { })
33 | val item2 = pool.obtain(0, Runnable { })
34 | val item3 = pool.obtain(0, Runnable { })
35 | val item4 = pool.obtain(6, Runnable { })
36 | val queue = listOf(item, item2, item3, item4)
37 | val minQueueIndexPair = DefaultThreadHandler.MinQueueIndexPair()
38 | findQueueMinIndex(queue, minQueueIndexPair)
39 | assert(minQueueIndexPair.index == 1)
40 | assert(minQueueIndexPair.waitTime > -1)
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/DispatchQueueImplTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.internals.DispatchQueueImpl
4 | import com.tonyodev.dispatch.internals.DispatchQueueInfo
5 | import com.tonyodev.dispatch.queuecontroller.DispatchQueueController
6 | import com.tonyodev.dispatch.utils.Threader
7 | import org.junit.Test
8 | import java.lang.Exception
9 |
10 | class DispatchQueueImplTest {
11 |
12 | @Test
13 | fun testFields() {
14 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
15 | val info = DispatchQueueInfo(99, false, handler)
16 | val queue = DispatchQueueImpl("test", 0,
17 | null, info, handler)
18 | assert(queue.id == 99)
19 | assert(queue.blockLabel == "test")
20 | assert(queue.controller == null)
21 | }
22 |
23 | @Test
24 | fun testManageController() {
25 | val controller = DispatchQueueController()
26 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
27 | val info = DispatchQueueInfo(99, false, handler)
28 | val queue = DispatchQueueImpl("test", 0,
29 | null, info, handler)
30 | queue.managedBy(controller)
31 | assert(queue.controller == controller)
32 | assert(controller.getManagedDispatchQueues().contains(queue))
33 | }
34 |
35 | @Test
36 | fun testCancelled() {
37 | val controller = DispatchQueueController()
38 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
39 | val info = DispatchQueueInfo(99, false, handler)
40 | val queue = DispatchQueueImpl("test", 0,
41 | null, info, handler)
42 | queue.managedBy(controller)
43 | assert(!queue.isCancelled)
44 | queue.cancel()
45 | assert(queue.isCancelled)
46 | }
47 |
48 | @Test
49 | fun testExecutePost() {
50 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
51 | val info = DispatchQueueInfo(99, false, handler)
52 | val queue = DispatchQueueImpl("test", 0,
53 | null, info, handler)
54 | info.enqueue(queue)
55 | var data: Any? = null
56 | queue.post {
57 | data = 44
58 | }
59 | queue.start()
60 | try {
61 | Thread.sleep(1000)
62 | } catch (e: Exception) {
63 |
64 | }
65 | assert(data is Int && data == 44)
66 | }
67 |
68 | @Test
69 | fun testExecuteAsync() {
70 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
71 | val info = DispatchQueueInfo(99, false, handler)
72 | val queue = DispatchQueueImpl("test", 0,
73 | null, info, handler)
74 | info.enqueue(queue)
75 | var data: Any? = null
76 | queue.async {
77 | data = 44
78 | }
79 | queue.start()
80 | try {
81 | Thread.sleep(1000)
82 | } catch (e: Exception) {
83 |
84 | }
85 | assert(data is Int && data == 44)
86 | }
87 |
88 | @Test
89 | fun testChaining() {
90 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
91 | val info = DispatchQueueInfo(99, false, handler)
92 | val queue = DispatchQueueImpl("test", 0,
93 | null, info, handler)
94 | info.enqueue(queue)
95 | var data: Any? = null
96 | queue.post {
97 | 44
98 | }.async {
99 | data = it * 2
100 | }
101 | queue.start()
102 | try {
103 | Thread.sleep(1000)
104 | } catch (e: Exception) {
105 |
106 | }
107 | assert(data is Int && data == (44 * 2))
108 | }
109 |
110 | @Test
111 | fun testDoOnError() {
112 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
113 | val info = DispatchQueueInfo(99, false, handler)
114 | val queue = DispatchQueueImpl("test", 0,
115 | null, info, handler)
116 | info.enqueue(queue)
117 | var data: Any? = null
118 | queue.post {
119 | throw Exception("test")
120 | 22
121 | }.doOnError {
122 | 44
123 | }.async {
124 | data = it * 2
125 | }
126 | queue.start()
127 | try {
128 | Thread.sleep(1000)
129 | } catch (e: Exception) {
130 |
131 | }
132 | assert(data is Int && data == (44 * 2))
133 | }
134 |
135 | @Test
136 | fun testZip() {
137 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
138 | val info = DispatchQueueInfo(99, false, handler)
139 | val queue = DispatchQueueImpl("test", 0,
140 | null, info, handler)
141 | val info2 = DispatchQueueInfo(99, false, handler)
142 | val queue2 = DispatchQueueImpl("test1", 0,
143 | null, info2, handler)
144 | info2.enqueue(queue2)
145 | info.enqueue(queue)
146 | var data: Any? = null
147 | queue.post {
148 | throw Exception("test")
149 | 22
150 | }.doOnError {
151 | 12
152 | }.zip(queue2.async { 44 * 2 })
153 | .async {
154 | data = it.second
155 | }
156 | queue.start()
157 | try {
158 | Thread.sleep(1000)
159 | } catch (e: Exception) {
160 |
161 | }
162 | assert(data is Int && data == (44 * 2))
163 | }
164 |
165 | @Test
166 | fun testRetry() {
167 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
168 | val info = DispatchQueueInfo(99, false, handler)
169 | val queue = DispatchQueueImpl("test", 0,
170 | null, info, handler)
171 | var counter = 0
172 | info.enqueue(queue)
173 | var data: Any? = null
174 | queue.async {
175 | if (counter == 0) {
176 | counter = 1
177 | throw Exception("")
178 | }
179 | data = 44
180 | }.retry(2, 0)
181 | queue.start()
182 | try {
183 | Thread.sleep(1000)
184 | } catch (e: Exception) {
185 |
186 | }
187 | assert(data is Int && data == 44)
188 | }
189 |
190 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/DispatchQueueInfoTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.internals.DispatchQueueImpl
4 | import com.tonyodev.dispatch.internals.DispatchQueueInfo
5 | import com.tonyodev.dispatch.utils.Threader
6 | import org.junit.Test
7 |
8 | class DispatchQueueInfoTest {
9 |
10 | @Test
11 | fun testFieldValue() {
12 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
13 | val info = DispatchQueueInfo(99, false, handler)
14 | assert(info.queueId == 99)
15 | assert(!info.isIntervalDispatch)
16 | assert(info.threadHandlerInfo == handler)
17 | assert(!info.isCancelled)
18 | assert(!info.isStarted)
19 | }
20 |
21 | @Test
22 | fun testEnqueue() {
23 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
24 | val info = DispatchQueueInfo(99, false, handler)
25 | val queue = DispatchQueueImpl("test", 0,
26 | null, info, handler)
27 | info.enqueue(queue)
28 | assert(info.rootDispatchQueue == queue)
29 | assert(info.endDispatchQueue == queue)
30 | val queue1 = DispatchQueueImpl("test2", 0,
31 | null, info, handler)
32 | info.enqueue(queue1)
33 | assert(info.rootDispatchQueue == queue)
34 | assert(info.endDispatchQueue == queue1)
35 | }
36 |
37 | @Test
38 | fun testCanPerformOperations() {
39 | val handler = Threader.getHandlerThreadInfo(ThreadType.TEST)
40 | val info = DispatchQueueInfo(99, false, handler)
41 | assert(info.canPerformOperations())
42 | info.isCancelled = true
43 | assert(!info.canPerformOperations())
44 | }
45 |
46 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/DispatchQueueObservableTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.TestThreadHandler
4 | import org.junit.Test
5 |
6 | class DispatchQueueObservableTest {
7 |
8 | @Test
9 | fun testAddObserver() {
10 | val observer = object: DispatchQueueObserver {
11 | override fun onChanged(t: Any) {
12 |
13 | }
14 | }
15 | val observable = DispatchQueueObservable(null)
16 | observable.addObserver(observer)
17 | assert(observable.getObservers().first() == observer)
18 | assert(observable.getObservers().size == 1)
19 | observable.addObservers(listOf(observer))
20 | assert(observable.getObservers().size == 1)
21 | }
22 |
23 | @Test
24 | fun testRemoveObserver() {
25 | val observer = object: DispatchQueueObserver {
26 | override fun onChanged(t: Any) {
27 |
28 | }
29 | }
30 | val observable = DispatchQueueObservable(null)
31 | observable.addObserver(observer)
32 | assert(observable.getObservers().size == 1)
33 | observable.removeObserver(observer)
34 | assert(observable.getObservers().isEmpty())
35 | observable.addObservers(listOf(observer))
36 | assert(observable.getObservers().size == 1)
37 | observable.removeObservers()
38 | assert(observable.getObservers().isEmpty())
39 | }
40 |
41 | @Test
42 | fun testObserverSize() {
43 | val observer = object: DispatchQueueObserver {
44 | override fun onChanged(t: Any) {
45 |
46 | }
47 | }
48 | val observable = DispatchQueueObservable(null)
49 | observable.addObserver(observer)
50 | assert(observable.getObservers().size == 1)
51 | observable.removeObserver(observer)
52 | assert(observable.getObservers().isEmpty())
53 | }
54 |
55 | @Test
56 | fun testThreadHandler() {
57 | val observable = DispatchQueueObservable(null)
58 | assert(observable.threadHandler == null)
59 | val handler = TestThreadHandler("Test")
60 | val observable1 = DispatchQueueObservable(handler)
61 | assert(observable1.threadHandler == handler)
62 | assert(observable1.shouldNotifyOnHandler)
63 | }
64 |
65 | @Test
66 | fun testShouldNotify() {
67 | val observable = DispatchQueueObservable(null)
68 | assert(!observable.shouldNotifyOnHandler)
69 | val handler = TestThreadHandler("Test")
70 | val observable1 = DispatchQueueObservable(handler)
71 | assert(observable1.shouldNotifyOnHandler)
72 | }
73 |
74 | @Test
75 | fun testNotify() {
76 | val value = 44
77 | val observer = object: DispatchQueueObserver {
78 | override fun onChanged(t: Any) {
79 | val t = t as Int
80 | assert(t == value)
81 | }
82 | }
83 | val observable = DispatchQueueObservable(null)
84 | observable.addObserver(observer)
85 | observable.notify(value)
86 | }
87 |
88 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/QueueControllerTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.queuecontroller.CancelType
4 | import com.tonyodev.dispatch.queuecontroller.DispatchQueueController
5 | import com.tonyodev.dispatch.queuecontroller.LifecycleDispatchQueueController
6 | import org.junit.Test
7 |
8 | class QueueControllerTest {
9 |
10 | @Test
11 | fun testManage() {
12 | val controller = DispatchQueueController()
13 | val queue = DispatchQueue.createDispatchQueue()
14 | controller.manage(queue)
15 | assert(controller.getManagedDispatchQueues().size == 1)
16 | assert(controller.getManagedDispatchQueues().first() == queue)
17 | assert(!queue.isCancelled)
18 | }
19 |
20 | @Test
21 | fun testUnManage() {
22 | val controller = DispatchQueueController()
23 | val queue = DispatchQueue.createDispatchQueue()
24 | controller.manage(queue)
25 | assert(controller.getManagedDispatchQueues().size == 1)
26 | assert(controller.getManagedDispatchQueues().first() == queue)
27 | controller.unmanage(queue)
28 | assert(controller.getManagedDispatchQueues().isEmpty())
29 | assert(!queue.isCancelled)
30 | }
31 |
32 | @Test
33 | fun testCancelId() {
34 | val controller = DispatchQueueController()
35 | val queue = DispatchQueue.createDispatchQueue()
36 | controller.manage(queue)
37 | controller.cancelDispatchQueues(queue.id)
38 | assert(controller.getManagedDispatchQueues().isEmpty())
39 | assert(queue.isCancelled)
40 | }
41 |
42 | @Test
43 | fun testCancel() {
44 | val controller = DispatchQueueController()
45 | val queue = DispatchQueue.createDispatchQueue()
46 | controller.manage(queue)
47 | controller.cancelDispatchQueues(queue)
48 | assert(controller.getManagedDispatchQueues().isEmpty())
49 | assert(queue.isCancelled)
50 | }
51 |
52 | @Test
53 | fun testLifecyclePaused() {
54 | val controller = LifecycleDispatchQueueController()
55 | val queue = DispatchQueue.createDispatchQueue()
56 | controller.manage(queue, CancelType.PAUSED)
57 | assert(controller.getManagedPausedDispatchQueues().isNotEmpty())
58 | assert(controller.getManagedDestroyedDispatchQueues().isNotEmpty())
59 | assert(controller.getManagedStoppedDispatchQueues().isEmpty())
60 | assert(controller.getManagedDispatchQueues().size == 1)
61 | assert(!queue.isCancelled)
62 | }
63 |
64 | @Test
65 | fun testLifecycleStopped() {
66 | val controller = LifecycleDispatchQueueController()
67 | val queue = DispatchQueue.createDispatchQueue()
68 | controller.manage(queue, CancelType.STOPPED)
69 | assert(controller.getManagedStoppedDispatchQueues().isNotEmpty())
70 | assert(controller.getManagedDestroyedDispatchQueues().isNotEmpty())
71 | assert(controller.getManagedPausedDispatchQueues().isEmpty())
72 | assert(controller.getManagedDispatchQueues().size == 1)
73 | assert(!queue.isCancelled)
74 | }
75 |
76 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/SettingsTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.DefaultThreadHandlerFactory
4 | import com.tonyodev.dispatch.utils.DefaultLogger
5 | import org.junit.Test
6 |
7 | class SettingsTest {
8 |
9 | @Test
10 | fun testWarningEnabled() {
11 | val settings = Settings()
12 | assert(!settings.enableLogWarnings)
13 | settings.enableLogWarnings = true
14 | assert(settings.enableLogWarnings)
15 | }
16 |
17 | @Test
18 | fun testErrorCallback() {
19 | val callback = DispatchQueueErrorCallback { }
20 | val settings = Settings()
21 | settings.dispatchQueueErrorCallback = callback
22 | assert(settings.dispatchQueueErrorCallback == callback)
23 | }
24 |
25 | @Test
26 | fun testThreadFactory() {
27 | val settings = Settings()
28 | val factory = DefaultThreadHandlerFactory()
29 | val thread1 = factory.create(ThreadType.BACKGROUND)
30 | thread1.quit()
31 | assert(settings.threadHandlerFactory is DefaultThreadHandlerFactory)
32 | val thread2 = settings.threadHandlerFactory.create(ThreadType.BACKGROUND)
33 | thread2.quit()
34 | assert(thread1.threadName == thread2.threadName)
35 | }
36 |
37 | @Test
38 | fun testLogger() {
39 | val settings = Settings()
40 | assert(settings.logger is DefaultLogger)
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/ThreadHandlerQueueItemTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.thread.ThreadHandlerQueueItem
4 | import org.junit.Test
5 |
6 | class ThreadHandlerQueueItemTest {
7 |
8 | @Test
9 | fun testValues() {
10 | val delay = 4L
11 | val runnable = Runnable { }
12 | val pool = ThreadHandlerQueueItem.Pool()
13 | val item = pool.obtain(delay, runnable)
14 | assert(item.delay == 4L)
15 | assert(item.runnable == runnable)
16 | assert(!item.isProcessing)
17 | }
18 |
19 | @Test
20 | fun testPoolSize() {
21 | val pool = ThreadHandlerQueueItem.Pool()
22 | assert(pool.size == 0)
23 | val item = pool.obtain(0, Runnable { })
24 | assert(pool.size == 0)
25 | assert(pool.maxPoolSize == 50)
26 | pool.recycle(item)
27 | assert(pool.size == 1)
28 | }
29 |
30 | @Test
31 | fun testRecycle() {
32 | val pool = ThreadHandlerQueueItem.Pool()
33 | assert(pool.size == 0)
34 | val item = pool.obtain(0, Runnable { })
35 | pool.recycle(item)
36 | assert(item.runnable == null)
37 | assert(item.delay == 0L)
38 | }
39 |
40 | @Test
41 | fun testRelease() {
42 | val pool = ThreadHandlerQueueItem.Pool()
43 | val item = pool.obtain(4, Runnable { })
44 | val item2 = pool.obtain(0, Runnable { })
45 | val item3 = pool.obtain(0, Runnable { })
46 | val item4 = pool.obtain(6, Runnable { })
47 | pool.release()
48 | assert(pool.size == 0)
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/dispatch/src/test/java/com/tonyodev/dispatch/ThreaderTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatch
2 |
3 | import com.tonyodev.dispatch.utils.THREAD_BACKGROUND
4 | import com.tonyodev.dispatch.utils.THREAD_MAIN_NO_UI
5 | import com.tonyodev.dispatch.utils.Threader
6 | import org.junit.Test
7 |
8 | class ThreaderTest {
9 |
10 | @Test
11 | fun getBackgroundThreadInfo() {
12 | val info = Threader.getHandlerThreadInfo(ThreadType.BACKGROUND)
13 | assert(!info.closeThreadHandler)
14 | assert(info.threadName == THREAD_BACKGROUND)
15 | val info2 = Threader.getHandlerThreadInfo(ThreadType.BACKGROUND)
16 | assert(info == info2)
17 | }
18 |
19 | @Test
20 | fun getMainThreadInfo() {
21 | val info = Threader.getHandlerThreadInfo(ThreadType.MAIN)
22 | assert(!info.closeThreadHandler)
23 | assert(info.threadName == THREAD_MAIN_NO_UI)
24 | val info2 = Threader.getHandlerThreadInfo(ThreadType.MAIN)
25 | assert(info == info2)
26 | }
27 |
28 | @Test
29 | fun getNewThreadInfo() {
30 | val name = "newThread"
31 | val info = Threader.getHandlerThreadInfo(name)
32 | assert(info.threadName == name)
33 | assert(info.closeThreadHandler)
34 | val info2 = Threader.getHandlerThreadInfo(name)
35 | assert(info2.threadName == name)
36 | assert(info != info2)
37 | assert(info2.closeThreadHandler)
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/dispatchandroid/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/dispatchandroid/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | apply plugin: 'kotlin-android'
4 |
5 | apply plugin: 'com.novoda.bintray-release'
6 |
7 | android {
8 |
9 | compileSdkVersion 28
10 |
11 | defaultConfig {
12 | minSdkVersion 14
13 | targetSdkVersion 28
14 | versionCode library_version_code
15 | versionName library_version
16 |
17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
18 |
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 |
28 | lintOptions {
29 | abortOnError false
30 | }
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_7
34 | targetCompatibility JavaVersion.VERSION_1_7
35 | }
36 |
37 | sourceSets {
38 | main.java.srcDirs += 'src/main/kotlin'
39 | androidTest.java.srcDirs += 'src/androidTest/kotlin'
40 | }
41 |
42 | }
43 |
44 | dependencies {
45 | implementation fileTree(dir: 'libs', include: ['*.jar'])
46 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
47 | api project(':dispatch')
48 | testImplementation 'junit:junit:4.12'
49 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
50 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
51 | }
52 |
53 | publish {
54 | userOrg = 'tonyofrancis'
55 | groupId = 'com.tonyodev.dispatch'
56 | artifactId = 'dispatch-android'
57 | publishVersion = library_version
58 | desc = 'Android Classes for Dispatch'
59 | website = 'https://github.com/tonyofrancis/dispatcher'
60 | }
61 |
62 | tasks.withType(Javadoc).all {
63 | enabled = false
64 | }
65 |
--------------------------------------------------------------------------------
/dispatchandroid/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 | -keep class com.tonyodev.dispatchandroid.queueController.ActivityDispatchQueueController { *; }
23 | -keep class com.tonyodev.dispatchandroid.thread.AndroidThreadHandler { *; }
24 | -keep class com.tonyodev.dispatchandroid.thread.AndroidThreadHandlerFactory { *; }
25 | -keep class com.tonyodev.dispatchandroid.utils.AndroidLogger { *; }
26 | -keep class com.tonyodev.dispatchandroid.utils.DispatchQueueAndroid { *; }
--------------------------------------------------------------------------------
/dispatchandroid/src/androidTest/java/com/tonyodev/dispatchandroid/AndroidThreadHandlerFactoryTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 | import com.tonyodev.dispatch.ThreadType
6 | import com.tonyodev.dispatch.utils.THREAD_BACKGROUND
7 | import com.tonyodev.dispatchandroid.thread.AndroidThreadHandlerFactory
8 | import junit.framework.Assert
9 | import org.junit.Before
10 | import org.junit.Test
11 | import org.junit.runner.RunWith
12 |
13 | @RunWith(AndroidJUnit4::class)
14 | class AndroidThreadHandlerFactoryTest {
15 |
16 | private lateinit var androidThreadHandlerFactory: AndroidThreadHandlerFactory
17 |
18 | @Before
19 | fun testInit() {
20 | androidThreadHandlerFactory = AndroidThreadHandlerFactory()
21 | }
22 |
23 | @Test
24 | fun useAppContext() {
25 | // Context of the app under test.
26 | val appContext = InstrumentationRegistry.getTargetContext()
27 | Assert.assertEquals("com.tonyodev.dispatchandroid.test", appContext.packageName)
28 | }
29 |
30 | @Test
31 | fun testCreateBackgroundThread() {
32 | val thread = androidThreadHandlerFactory.create(ThreadType.BACKGROUND)
33 | assert(thread.threadName == THREAD_BACKGROUND)
34 | thread.quit()
35 | assert(!thread.isActive)
36 | }
37 |
38 | @Test
39 | fun testCreateMainThread() {
40 | val thread = androidThreadHandlerFactory.create(ThreadType.MAIN)
41 | assert(thread.threadName == "main")
42 | thread.quit()
43 | assert(!thread.isActive)
44 | }
45 |
46 | @Test
47 | fun testCreatedThreadActive() {
48 | val thread = androidThreadHandlerFactory.create(ThreadType.BACKGROUND)
49 | assert(thread.isActive)
50 | thread.quit()
51 | assert(!thread.isActive)
52 | }
53 |
54 | @Test
55 | fun testCreatedThreadByName() {
56 | val name = "sampleThread"
57 | val thread = androidThreadHandlerFactory.create(name)
58 | assert(thread.threadName == name)
59 | thread.quit()
60 | assert(!thread.isActive)
61 | }
62 |
63 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/androidTest/java/com/tonyodev/dispatchandroid/AndroidThreadHandlerTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid
2 |
3 | import androidx.test.runner.AndroidJUnit4
4 | import com.tonyodev.dispatchandroid.thread.AndroidThreadHandler
5 | import org.junit.Test
6 | import org.junit.runner.RunWith
7 |
8 | @RunWith(AndroidJUnit4::class)
9 | class AndroidThreadHandlerTest {
10 |
11 | @Test
12 | fun testFields() {
13 | val thread = AndroidThreadHandler("test")
14 | assert(thread.threadName == "test")
15 | assert(thread.isActive)
16 | }
17 |
18 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/androidTest/java/com/tonyodev/dispatchandroid/DispatchQueueAndroidTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 | import com.tonyodev.dispatch.DispatchQueue
6 | import com.tonyodev.dispatchandroid.thread.AndroidThreadHandlerFactory
7 | import com.tonyodev.dispatchandroid.utils.AndroidLogger
8 | import junit.framework.Assert.assertEquals
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | @RunWith(AndroidJUnit4::class)
13 | class DispatchQueueAndroidTest {
14 |
15 | @Test
16 | fun useAppContext() {
17 | // Context of the app under test.
18 | val appContext = InstrumentationRegistry.getTargetContext()
19 | assertEquals("com.tonyodev.dispatchandroid.test", appContext.packageName)
20 | }
21 |
22 | @Test
23 | fun testInit() {
24 | initAndroidDispatchQueues()
25 | assert(DispatchQueue.globalSettings.logger is AndroidLogger)
26 | assert(DispatchQueue.globalSettings.threadHandlerFactory is AndroidThreadHandlerFactory)
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/androidTest/java/com/tonyodev/dispatchandroid/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid;
2 |
3 | import android.content.Context;
4 | import androidx.test.InstrumentationRegistry;
5 | import androidx.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.tonyodev.dispatchandroid.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dispatchandroid/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/dispatchandroid/src/main/java/com/tonyodev/dispatchandroid/DispatchAndroidExtentions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("DispatchQueueAndroid")
2 |
3 | package com.tonyodev.dispatchandroid
4 |
5 | import android.app.Activity
6 | import com.tonyodev.dispatch.DispatchQueue
7 | import com.tonyodev.dispatch.queuecontroller.CancelType
8 | import com.tonyodev.dispatchandroid.queueController.ActivityDispatchQueueController
9 | import com.tonyodev.dispatchandroid.thread.AndroidThreadHandlerFactory
10 | import com.tonyodev.dispatchandroid.utils.AndroidLogger
11 |
12 | /**
13 | * This method must be called in an Android project to initialize the proper threads that the dispatch queue will
14 | * use on android.
15 | * */
16 | fun initAndroidDispatchQueues() {
17 | DispatchQueue.globalSettings.logger = AndroidLogger()
18 | DispatchQueue.globalSettings.threadHandlerFactory = AndroidThreadHandlerFactory()
19 | }
20 |
21 | /**
22 | * Set's this dispatch queue to be managed by an Activity. The activity is wrapped in an instance
23 | * of ActivityDispatchQueueController. ActivityDispatchQueueController is controlled by the activity's lifecycle.
24 | * Managed dispatch queues can be cancelled by the DispatchQueueController if the dispatch queue is not already cancelled.
25 | * @param activity the activity that will manage the dispatch queue. The cancel type is Destroyed. Cancellation
26 | * occurs when the activity's onDestroy method is called.
27 | * @return dispatch queue.
28 | * */
29 | fun DispatchQueue.managedBy(activity: Activity): DispatchQueue {
30 | return managedBy(activity, CancelType.DESTROYED)
31 | }
32 |
33 | /**
34 | * Set's this dispatch queue to be managed by an Activity. The activity is wrapped in an instance
35 | * of ActivityDispatchQueueController. ActivityDispatchQueueController is controlled by the activity's lifecycle.
36 | * Managed dispatch queues can be cancelled by the DispatchQueueController if the dispatch queue is not already cancelled.
37 | * @param activity the activity that will manage the dispatch queue.
38 | * @param cancelType the cancel type
39 | * @return dispatch queue.
40 | * */
41 | fun DispatchQueue.managedBy(activity: Activity, cancelType: CancelType): DispatchQueue {
42 | val oldDispatchQueueController = controller
43 | oldDispatchQueueController?.unmanage(this)
44 | val dispatchQueueController = ActivityDispatchQueueController.getInstance(activity)
45 | managedBy(dispatchQueueController, cancelType)
46 | return this
47 | }
48 |
49 | /**
50 | * Gets the DispatchQueueController associated with this activity.
51 | * */
52 | val Activity.dispatchQueueController: ActivityDispatchQueueController
53 | get() {
54 | return ActivityDispatchQueueController.getInstance(this)
55 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/main/java/com/tonyodev/dispatchandroid/queueController/ActivityDispatchQueueController.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid.queueController
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.os.Bundle
6 | import com.tonyodev.dispatch.queuecontroller.LifecycleDispatchQueueController
7 |
8 | /**
9 | * A DispatchQueueController that uses an activity's lifecycle to manage
10 | * Dispatch queues. ActivityDispatchQueueController automatically cancels and unmanages
11 | * any dispatch queue that is not cancelled when the activity is destroyed.
12 | * */
13 | open class ActivityDispatchQueueController(protected val activity: Activity): LifecycleDispatchQueueController() {
14 |
15 | private val activityLifecycleCallbacks = object: Application.ActivityLifecycleCallbacks {
16 |
17 | override fun onActivityPaused(activity: Activity?) {
18 | if (this@ActivityDispatchQueueController.activity == activity) {
19 | cancelAllPaused()
20 | }
21 | }
22 |
23 | override fun onActivityStopped(activity: Activity?) {
24 | if (this@ActivityDispatchQueueController.activity == activity) {
25 | cancelAllStopped()
26 | }
27 | }
28 |
29 | override fun onActivityDestroyed(activity: Activity?) {
30 | if (this@ActivityDispatchQueueController.activity == activity) {
31 | cancelAllDestroyed()
32 | release()
33 | }
34 | }
35 |
36 | override fun onActivityResumed(activity: Activity?) {}
37 |
38 | override fun onActivityStarted(activity: Activity?) {}
39 |
40 | override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {}
41 |
42 | override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {}
43 |
44 | }
45 |
46 | init {
47 | activity.application.registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
48 | }
49 |
50 | private fun release() {
51 | activity.application.unregisterActivityLifecycleCallbacks(activityLifecycleCallbacks)
52 | synchronized(map) {
53 | val iterator = map.iterator()
54 | while (iterator.hasNext()) {
55 | if (iterator.next().key == activity) {
56 | iterator.remove()
57 | break
58 | }
59 | }
60 | }
61 | }
62 |
63 | companion object {
64 |
65 | @JvmStatic
66 | private val map = mutableMapOf()
67 |
68 | /**
69 | * Gets the ActivityDispatchQueueController instance for the passed in activity.
70 | * @param activity the activity.
71 | * @return return instance.
72 | * */
73 | @JvmStatic
74 | fun getInstance(activity: Activity): ActivityDispatchQueueController {
75 | return synchronized(map) {
76 | val controller = map[activity] ?: ActivityDispatchQueueController(activity)
77 | map[activity] = controller
78 | controller
79 | }
80 | }
81 |
82 | }
83 |
84 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/main/java/com/tonyodev/dispatchandroid/thread/AndroidThreadHandler.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid.thread
2 |
3 | import android.os.Build
4 | import android.os.Handler
5 | import android.os.HandlerThread
6 | import com.tonyodev.dispatch.thread.ThreadHandler
7 |
8 | /**
9 | * The default android thread handler that uses the android.os.Handler class to perform work.
10 | * */
11 | open class AndroidThreadHandler (val handler: Handler): ThreadHandler {
12 |
13 | @Volatile
14 | private var isCancelled = false
15 |
16 | constructor(threadName: String): this({
17 | val handlerThread = HandlerThread(threadName)
18 | handlerThread.start()
19 | Handler(handlerThread.looper)
20 | }())
21 |
22 | override val threadName: String
23 | get() {
24 | return handler.looper.thread.name
25 | }
26 |
27 | override fun post(runnable: Runnable) {
28 | if (!isCancelled) {
29 | handler.post(runnable)
30 | }
31 | }
32 |
33 | override fun postDelayed(delayInMilliseconds: Long, runnable: Runnable) {
34 | if (!isCancelled) {
35 | handler.postDelayed(runnable, delayInMilliseconds)
36 | }
37 | }
38 |
39 | override fun removeCallbacks(runnable: Runnable) {
40 | if (!isCancelled) {
41 | handler.removeCallbacksAndMessages(runnable)
42 | }
43 | }
44 |
45 | override fun quit() {
46 | if (!isCancelled) {
47 | isCancelled = true
48 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
49 | handler.looper.quitSafely()
50 | } else {
51 | handler.looper.quit()
52 | }
53 | }
54 | }
55 |
56 | override val isActive: Boolean
57 | get() {
58 | return !isCancelled
59 | }
60 |
61 | override fun start() {
62 |
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/main/java/com/tonyodev/dispatchandroid/thread/AndroidThreadHandlerFactory.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid.thread
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import com.tonyodev.dispatch.ThreadType
6 | import com.tonyodev.dispatch.thread.DefaultThreadHandler
7 | import com.tonyodev.dispatch.thread.TestThreadHandler
8 | import com.tonyodev.dispatch.thread.ThreadHandler
9 | import com.tonyodev.dispatch.thread.ThreadHandlerFactory
10 | import com.tonyodev.dispatch.utils.*
11 |
12 | /**
13 | * The Default Android Handler based ThreadHandler Factory used by the library.
14 | * */
15 | open class AndroidThreadHandlerFactory: ThreadHandlerFactory {
16 |
17 | @Volatile
18 | private var newThreadCount = 0
19 |
20 | override fun create(threadType: ThreadType): ThreadHandler {
21 | val threadHandler = when(threadType) {
22 | ThreadType.BACKGROUND -> DefaultThreadHandler(THREAD_BACKGROUND)
23 | ThreadType.IO -> DefaultThreadHandler(THREAD_IO)
24 | ThreadType.NETWORK -> DefaultThreadHandler(THREAD_NETWORK)
25 | ThreadType.COMPUTATION -> DefaultThreadHandler(THREAD_COMPUTATION)
26 | ThreadType.NEW -> getNewDispatchQueueHandler()
27 | ThreadType.MAIN -> AndroidThreadHandler(Handler(Looper.getMainLooper()))
28 | ThreadType.TEST -> TestThreadHandler(THREAD_TEST)
29 | }
30 | if (!threadHandler.isActive) {
31 | threadHandler.start()
32 | }
33 | return threadHandler
34 | }
35 |
36 | override fun create(threadName: String?): ThreadHandler {
37 | return getNewDispatchQueueHandler(threadName)
38 | }
39 |
40 | private fun getNewDispatchQueueHandler(name: String? = null): ThreadHandler {
41 | val threadName = if (name == null || name.isEmpty()) {
42 | "DispatchQueue${++newThreadCount}"
43 | } else {
44 | name
45 | }
46 | val threadHandler = DefaultThreadHandler(threadName)
47 | if (!threadHandler.isActive) {
48 | threadHandler.start()
49 | }
50 | return threadHandler
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/main/java/com/tonyodev/dispatchandroid/utils/AndroidLogger.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid.utils
2 |
3 | import android.util.Log
4 | import com.tonyodev.dispatch.utils.Logger
5 | import com.tonyodev.dispatch.utils.TAG
6 |
7 | open class AndroidLogger: Logger {
8 |
9 | override fun print(tag: String, message: String) {
10 | Log.d(tag, message)
11 | }
12 |
13 | override fun print(tag: String, throwable: Throwable) {
14 | Log.d(tag, throwable.message, throwable)
15 | }
16 |
17 | override fun print(tag: String, any: Any?) {
18 | Log.d(tag, any?.toString())
19 | }
20 |
21 | override fun print(message: String) {
22 | Log.d(TAG, message)
23 | }
24 |
25 | override fun print(throwable: Throwable) {
26 | Log.d(TAG, throwable.message, throwable)
27 | }
28 |
29 | override fun print(any: Any?) {
30 | Log.d(TAG, any?.toString())
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/dispatchandroid/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DispatchAndroid
3 |
4 |
--------------------------------------------------------------------------------
/dispatchandroid/src/test/java/com/tonyodev/dispatchandroid/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchandroid;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/dispatchretrofit/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/dispatchretrofit/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java-library'
2 |
3 | apply plugin: 'kotlin'
4 |
5 | apply plugin: 'com.novoda.bintray-release'
6 |
7 | sourceCompatibility = "7"
8 | targetCompatibility = "7"
9 |
10 | dependencies {
11 | implementation fileTree(dir: 'libs', include: ['*.jar'])
12 | implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
13 | implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
14 | api project(':dispatch')
15 | testImplementation 'junit:junit:4.12'
16 | }
17 |
18 | publish {
19 | userOrg = 'tonyofrancis'
20 | groupId = 'com.tonyodev.dispatch'
21 | artifactId = 'dispatch-retrofit2-adapter'
22 | publishVersion = library_version
23 | desc = 'Retrofit Call Adapter Factory for Dispatch'
24 | website = 'https://github.com/tonyofrancis/dispatcher'
25 | }
26 |
27 | tasks.withType(Javadoc).all {
28 | enabled = false
29 | }
30 |
--------------------------------------------------------------------------------
/dispatchretrofit/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 |
--------------------------------------------------------------------------------
/dispatchretrofit/src/androidTest/java/com/tonyodev/dispatchretrofit/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchretrofit;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.tonyodev.dispatchretrofit.test", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/dispatchretrofit/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
--------------------------------------------------------------------------------
/dispatchretrofit/src/main/java/com/tonyodev/dispatchretrofit/DispatchQueueCallAdapterFactory.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchretrofit
2 |
3 | import com.tonyodev.dispatch.DispatchQueue
4 | import com.tonyodev.dispatch.ThreadType
5 | import com.tonyodev.dispatch.thread.ThreadHandler
6 | import retrofit2.*
7 | import java.lang.IllegalArgumentException
8 | import java.lang.reflect.ParameterizedType
9 | import java.lang.reflect.Type
10 |
11 | /**
12 | * DispatchQueueCallAdapterFactory for Retrofit.
13 | * */
14 | class DispatchQueueCallAdapterFactory constructor(
15 | /** Optional threadHandler used by the DispatchQueues created by this Factory.*/
16 | val threadHandler: ThreadHandler? = null,
17 | /** Optional error callback for network requests made by a DispatchQueueCallAdapter instance.
18 | * Only called if an error occurred. Note: The errors will still be thrown inside the DispatchQueue.
19 | * This callback only allows for observing at a global level.*/
20 | val onErrorCallback: OnErrorCallback?): CallAdapter.Factory() {
21 |
22 | override fun get(returnType: Type, annotations: Array, retrofit: Retrofit): CallAdapter<*, *>? {
23 | val clazz = getRawType(returnType)
24 | if (clazz == DispatchQueue::class.java) {
25 | if (returnType !is ParameterizedType) {
26 | throw IllegalArgumentException("DispatchQueue return type must be parameterized as DispatchQueue")
27 | }
28 | val responseType = getParameterUpperBound(0, returnType)
29 | return DispatchQueueCallAdapter(responseType, threadHandler, onErrorCallback)
30 | }
31 | return null
32 | }
33 |
34 | companion object {
35 |
36 | /**
37 | * Creates an instance of DispatchQueueCallAdapterFactory
38 | * @param threadHandler Optional threadHandler used to start this dispatch object. Can be null. If null the default network thread is used.
39 | * @param onErrorCallback Optional on error callback. Can be null.
40 | * @throws IllegalArgumentException is the passed in threadHandler uses the main thread to do background work.
41 | * @return new instance of DispatchQueueCallAdapterFactory.
42 | * */
43 | @JvmStatic
44 | @JvmOverloads
45 | fun create(threadHandler: ThreadHandler? = null, onErrorCallback: OnErrorCallback? = null): DispatchQueueCallAdapterFactory {
46 | if (threadHandler?.threadName == DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.MAIN).threadName) {
47 | throw IllegalArgumentException("DispatchQueueCallAdapterFactory: ThreadHandler cannot be the main thread for network operations.")
48 | }
49 | return DispatchQueueCallAdapterFactory(threadHandler, onErrorCallback)
50 | }
51 |
52 | /**
53 | * Creates an instance of DispatchQueueCallAdapterFactory. The default network thread is used.
54 | * @param onErrorCallback Optional on error callback. Can be null.
55 | * @return new instance of DispatchQueueCallAdapterFactory.
56 | * */
57 | @JvmStatic
58 | fun create(onErrorCallback: OnErrorCallback? = null): DispatchQueueCallAdapterFactory {
59 | return DispatchQueueCallAdapterFactory(null, onErrorCallback)
60 | }
61 |
62 | /**
63 | * Creates an instance of DispatchQueueCallAdapterFactory that uses a test dispatch queue to perform network requests.
64 | * @param onErrorCallback Optional on error callback. Can be null.
65 | * @return new instance of DispatchQueueCallAdapterFactory for test.
66 | * */
67 | @JvmStatic
68 | @JvmOverloads
69 | fun createTestFactory(onErrorCallback: OnErrorCallback? = null): DispatchQueueCallAdapterFactory {
70 | return DispatchQueueCallAdapterFactory(DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.TEST), onErrorCallback)
71 | }
72 |
73 | }
74 |
75 | class DispatchQueueCallAdapter(private val responseType: Type,
76 | private val threadHandler : ThreadHandler?,
77 | private val onErrorCallback: OnErrorCallback?): CallAdapter> {
78 |
79 | override fun adapt(call: Call): DispatchQueue<*> {
80 | if (threadHandler != null && !threadHandler.isActive) {
81 | threadHandler.start()
82 | }
83 | return if (threadHandler == null) {
84 | DispatchQueue.createDispatchQueue(ThreadType.NETWORK)
85 | } else {
86 | DispatchQueue.createDispatchQueue(threadHandler)
87 | }.async {
88 | val callClone = call.clone()
89 | val response = callClone.execute()
90 | val data: Any? = if (response.isSuccessful) {
91 | response.body()
92 | } else {
93 | HttpException(response)
94 | }
95 | callClone.cancel()
96 | if (data is HttpException) {
97 | onErrorCallback?.onError(data, callClone.request(), response)
98 | throw data
99 | }
100 | data
101 | }
102 | }
103 |
104 | override fun responseType(): Type {
105 | return responseType
106 | }
107 |
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/dispatchretrofit/src/main/java/com/tonyodev/dispatchretrofit/OnErrorCallback.java:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchretrofit;
2 |
3 | import okhttp3.Request;
4 | import retrofit2.HttpException;
5 | import retrofit2.Response;
6 |
7 | /**
8 | * Interface used by DispatchQueueCallAdapterFactory to relay errors.
9 | * */
10 | @FunctionalInterface
11 | public interface OnErrorCallback {
12 |
13 | /**
14 | * Called when an error occur when executing a request.
15 | * @param exception the error.
16 | * @param request the request.
17 | * @param response the response.
18 | * */
19 | void onError(HttpException exception, Request request, Response response);
20 |
21 | }
--------------------------------------------------------------------------------
/dispatchretrofit/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Dispatchretrofit
3 |
4 |
--------------------------------------------------------------------------------
/dispatchretrofit/src/test/java/com/tonyodev/dispatchretrofit/DispatchQueueCallFactoryTest.kt:
--------------------------------------------------------------------------------
1 | package com.tonyodev.dispatchretrofit
2 |
3 | import com.tonyodev.dispatch.DispatchQueue
4 | import com.tonyodev.dispatch.ThreadType
5 | import org.junit.Test
6 |
7 | class DispatchQueueCallFactoryTest {
8 |
9 | @Test
10 | fun testFields() {
11 | val factory = DispatchQueueCallAdapterFactory.create()
12 | assert(factory.threadHandler == null)
13 | assert(factory.onErrorCallback == null)
14 | val handler = DispatchQueue.globalSettings.threadHandlerFactory.create(ThreadType.TEST)
15 | val callback = OnErrorCallback { exception, request, response -> }
16 | val factory2 = DispatchQueueCallAdapterFactory.create(handler, callback)
17 | assert(factory2.threadHandler == handler)
18 | assert(factory2.onErrorCallback == callback)
19 | val testFactory = DispatchQueueCallAdapterFactory.createTestFactory()
20 | assert(testFactory.threadHandler?.threadName == handler.threadName)
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tonyofrancis/Dispatcher/4ee0770ef7d71b8c07ac8a57a3521e0a9ac8daba/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Apr 29 09:25:54 EDT 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':dispatch', ':dispatchretrofit', ':dispatchandroid'
2 |
--------------------------------------------------------------------------------