motion-async 0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +22 -0
- data/README.md +199 -0
- data/lib/motion-async/motion_async.rb +103 -0
- data/lib/motion-async/motion_async_task.rb +78 -0
- data/lib/motion-async/version.rb +4 -0
- data/lib/motion-async.rb +8 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ebdc675fdcad19bacb5e6b61b767abb402480031
|
4
|
+
data.tar.gz: d4880f32b2067e5b3b89a3ba319997cac462de6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f95af187c39d1878188ace11f37ab15f64563c09729ff486ca95bd1dd66543f45caf5eb8ec1f804c1c1d27cae55bffc7667bc5c9cf8b6d89bb27bbcd6eee175
|
7
|
+
data.tar.gz: e442a6f8e3467d4886838d98aadc8b2165a27c5fa7f077a4e149919f9b995110bfad3394b57a9b308123f3953801d46b9d64489a3709b3131d7d431c3cf2b690
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2015 Silvertop Studio, LLC
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,199 @@
|
|
1
|
+
# motion-async
|
2
|
+
|
3
|
+
motion-async is a gem for RubyMotion Android that provides a friendly Ruby wrapper around Android's [AsyncTask:](https://developer.android.com/reference/android/os/AsyncTask.html)
|
4
|
+
|
5
|
+
> AsyncTask enables proper and easy use of the UI thread. This class allows [you] to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
|
6
|
+
>
|
7
|
+
> AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.)
|
8
|
+
|
9
|
+
AsyncTask must be loaded on the UI thread, _and must only be executed once._ See [the documentation](https://developer.android.com/reference/android/os/AsyncTask.html) for more details.
|
10
|
+
|
11
|
+
## Setup
|
12
|
+
|
13
|
+
Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem "motion-async"
|
17
|
+
```
|
18
|
+
|
19
|
+
then run `bundle` on the command line.
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The main entry point is the `MotionAsync.async` function, which creates, and then executes the async code with the options you provide (see below for details).
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
MotionAsync.async do
|
27
|
+
# some long operation
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
You can also `include MotionAsync` to have access to the `async` function without the module prefix:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
include MotionAsync
|
35
|
+
...
|
36
|
+
async do
|
37
|
+
# some long operation
|
38
|
+
end
|
39
|
+
```
|
40
|
+
|
41
|
+
`async` takes a block, which is the code that should be executed in the background. You can optionally specify callback blocks that are run at various points during the tasks's lifecycle:
|
42
|
+
|
43
|
+
* `:pre_execute` : before the background task is executed
|
44
|
+
* `:completion` : when the task finishes
|
45
|
+
* `:progress` : whenever `progress` is called on the task object
|
46
|
+
* `:cancelled` : if the task is cancelled
|
47
|
+
|
48
|
+
These callbacks can be added with the `on` method, or passed in as options to `async`.
|
49
|
+
|
50
|
+
This:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
async do
|
54
|
+
# some long operation
|
55
|
+
end.on(:completion) do |result|
|
56
|
+
# process result
|
57
|
+
end
|
58
|
+
```
|
59
|
+
|
60
|
+
is the same as this:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
async(
|
64
|
+
completion: -> (result) {
|
65
|
+
# process result
|
66
|
+
}
|
67
|
+
) do
|
68
|
+
# some long operation
|
69
|
+
end
|
70
|
+
```
|
71
|
+
|
72
|
+
To avoid the awkward syntax of the latter example, you can use the `:background` option to specify the async code:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
async(
|
76
|
+
background: -> {
|
77
|
+
# some long operation
|
78
|
+
},
|
79
|
+
completion: -> (result) {
|
80
|
+
# process result
|
81
|
+
}
|
82
|
+
)
|
83
|
+
```
|
84
|
+
|
85
|
+
## Examples
|
86
|
+
|
87
|
+
Run a block of code in the background:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
async do
|
91
|
+
# some long operation
|
92
|
+
end
|
93
|
+
```
|
94
|
+
|
95
|
+
Specify a block to execute when the operation completes. The return value of the async block is passed
|
96
|
+
in as a parameter:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
task = async do
|
100
|
+
some_expensive_calculation()
|
101
|
+
end
|
102
|
+
task.on :completion do |result|
|
103
|
+
p "The result was #{result}"
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
Alternate syntax for the same example:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
async do
|
111
|
+
some_expensive_calculation()
|
112
|
+
end.on(:completion) do |result|
|
113
|
+
p "The result was #{result}"
|
114
|
+
end
|
115
|
+
```
|
116
|
+
|
117
|
+
### Progress Indicators
|
118
|
+
|
119
|
+
For progress updates, provide a `:progress block`, and periodically call `#progress` on the task object in the background block. The `:progress` block is executed on the main thread.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
async do |task|
|
123
|
+
100.times do |i|
|
124
|
+
# do some work
|
125
|
+
task.progress i
|
126
|
+
end
|
127
|
+
end.on(:progress) do |progress_value|
|
128
|
+
p "Progress: #{progress_value + 1}% complete"
|
129
|
+
end
|
130
|
+
```
|
131
|
+
|
132
|
+
### Chaining
|
133
|
+
|
134
|
+
Calls to `on` are chainable:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
async do |task|
|
138
|
+
100.times do |i|
|
139
|
+
# do some work
|
140
|
+
task.progress i
|
141
|
+
end
|
142
|
+
end.on(:progress) do |progress_value|
|
143
|
+
p "Progress: #{progress_value + 1}% complete"
|
144
|
+
end.on(:completion) do |result|
|
145
|
+
p "The result was #{result}"
|
146
|
+
end
|
147
|
+
```
|
148
|
+
|
149
|
+
### Other Callbacks
|
150
|
+
|
151
|
+
`:pre_execute` is invoked before the async operation begins and `:cancelled` is called if the task is cancelled.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
async do
|
155
|
+
# long operation
|
156
|
+
end.on(:pre_execute) do
|
157
|
+
p "About to run a long operation"
|
158
|
+
end.on(:cancelled) do
|
159
|
+
p "Operation cancelled."
|
160
|
+
end
|
161
|
+
```
|
162
|
+
|
163
|
+
### Canceling a Task
|
164
|
+
|
165
|
+
`async` returns a reference to the task object (a subclass of `AsyncTask`); you can hold on to this
|
166
|
+
in case you want to cancel it later. You can see if a task has been cancelled by calling
|
167
|
+
`cancelled?` The Android docs recommend checking this value periodically during task execution
|
168
|
+
so you can exit gracefully.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
@async_task = async do |task|
|
172
|
+
image_urls.each do |image_url|
|
173
|
+
images << load_image(image_url)
|
174
|
+
break if task.cancelled?
|
175
|
+
end
|
176
|
+
end
|
177
|
+
...
|
178
|
+
# e.g. in an Activity or Fragment
|
179
|
+
def onStop
|
180
|
+
@async_task.cancel(true) # passing in true indicates that the task should be interrupted
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
### Other Task States
|
185
|
+
|
186
|
+
```ruby
|
187
|
+
task.pending?
|
188
|
+
task.running?
|
189
|
+
task.finished?
|
190
|
+
```
|
191
|
+
|
192
|
+
## Development
|
193
|
+
|
194
|
+
### Tests
|
195
|
+
|
196
|
+
It's a little tricky to test background threads in a unit test context. I went through a number of blog posts and SO questions, but never could manage to get it to work.
|
197
|
+
|
198
|
+
So, we've got a few tests in `main_spec.rb` and then a bunch in `main_activity.rb` which are run simply by running the app in this codebase via `rake`. I'm not especially proud of this, but figured it was better than nothing. If anyone can show me a better way, I'd love to see it.
|
199
|
+
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# MotionAsync provides a friendly Ruby wrapper around Android's AsyncTask.
|
2
|
+
#
|
3
|
+
# You can call it directly:
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# MotionAsync.async do
|
7
|
+
# # some long task
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# Or include the module to make the async command available wherever you need it
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# include MotionAsync
|
14
|
+
# ...
|
15
|
+
# async do
|
16
|
+
# # some long task
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# Usage:
|
20
|
+
#
|
21
|
+
# Run a block of code in the background:
|
22
|
+
#
|
23
|
+
# @example
|
24
|
+
# async do
|
25
|
+
# # some long operation
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Specify a block to execute when the operation completes. The return value of the async block is passed
|
29
|
+
# in as a parameter:
|
30
|
+
#
|
31
|
+
# @example
|
32
|
+
# task = async do
|
33
|
+
# some_expensive_calculation()
|
34
|
+
# end
|
35
|
+
# task.on :completion do |result|
|
36
|
+
# p "The result was #{result}"
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Alternate syntax for the same example:
|
40
|
+
#
|
41
|
+
# @example
|
42
|
+
# async.on(:background) do |task|
|
43
|
+
# some_expensive_calculation()
|
44
|
+
# end.on(:completion) do |result|
|
45
|
+
# p "The result was #{result}"
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# For progress updates, provide a :progress block, and periodically call #progress
|
49
|
+
# on the task object in the :background block. The :progress block is executed on the
|
50
|
+
# main thread.
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# async.on(:background) do |task|
|
54
|
+
# 100.times do |i|
|
55
|
+
# # do some work
|
56
|
+
# task.progress i
|
57
|
+
# end
|
58
|
+
# end.on(:progress) do |result|
|
59
|
+
# p "Progress: #{progress + 1}% complete"
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# :pre_execute is invoked before the async operation begins and :cancelled is called
|
63
|
+
# if the task is cancelled.
|
64
|
+
#
|
65
|
+
# @example
|
66
|
+
# async.on(:background) do |task|
|
67
|
+
# # long operation
|
68
|
+
# end.on(:pre_execute) do
|
69
|
+
# p "About to run a long operation"
|
70
|
+
# end.on(:cancelled) do
|
71
|
+
# p "Operation cancelled."
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# async returns a reference to the task object (a subclass of AsyncTask); you can hold on to this
|
75
|
+
# in case you want to cancel it later. You can see if a task has been cancelled by calling
|
76
|
+
# cancelled?
|
77
|
+
#
|
78
|
+
# @example
|
79
|
+
# @async_task = async do |task|
|
80
|
+
# image_urls.each do |image_url|
|
81
|
+
# images << load_image(image_url)
|
82
|
+
# break if task.cancelled?
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
# ...
|
86
|
+
# def on_stop
|
87
|
+
# @async_task.cancel
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
module MotionAsync
|
91
|
+
|
92
|
+
def self.async(options={}, &block)
|
93
|
+
MotionAsyncTask.create(options, &block).tap do |task|
|
94
|
+
task.execute []
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def async(options={}, &block)
|
99
|
+
MotionAsync.async(options, &block)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class MotionAsyncTask < Android::Os::AsyncTask
|
2
|
+
attr_reader :result
|
3
|
+
|
4
|
+
# Java is super picky about constructors, so we'll use a factory method
|
5
|
+
def self.create(options={}, &block)
|
6
|
+
MotionAsyncTask.new.tap do |task|
|
7
|
+
options[:background] = block if block
|
8
|
+
task.callbacks.merge!(options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def on(callback, &block)
|
13
|
+
callbacks[callback] = block
|
14
|
+
if callback == :completion && finished?
|
15
|
+
# task already ran, but we'll call the completion block anyway
|
16
|
+
block.call @result
|
17
|
+
end
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
# publishProgress must be passed an Array - we can make that easier
|
22
|
+
def progress(progress)
|
23
|
+
progress = [progress] unless progress.respond_to?(:[])
|
24
|
+
publishProgress(progress)
|
25
|
+
end
|
26
|
+
|
27
|
+
def pending?
|
28
|
+
status == Android::Os::AsyncTask::Status::PENDING
|
29
|
+
end
|
30
|
+
|
31
|
+
def running?
|
32
|
+
status == Android::Os::AsyncTask::Status::RUNNING
|
33
|
+
end
|
34
|
+
|
35
|
+
def finished?
|
36
|
+
status == Android::Os::AsyncTask::Status::FINISHED
|
37
|
+
end
|
38
|
+
|
39
|
+
def cancelled?
|
40
|
+
isCancelled
|
41
|
+
end
|
42
|
+
|
43
|
+
##########################
|
44
|
+
## AsyncTask event methods
|
45
|
+
|
46
|
+
def onPreExecute
|
47
|
+
call_if_defined :pre_execute, self
|
48
|
+
end
|
49
|
+
|
50
|
+
def onPostExecute(result)
|
51
|
+
call_if_defined :completion, result
|
52
|
+
end
|
53
|
+
|
54
|
+
def doInBackground(params)
|
55
|
+
@result = call_if_defined :background, self
|
56
|
+
end
|
57
|
+
|
58
|
+
def onProgressUpdate(progress)
|
59
|
+
progress = progress.first if progress.size == 1
|
60
|
+
call_if_defined :progress, progress
|
61
|
+
end
|
62
|
+
|
63
|
+
def onCancelled(result)
|
64
|
+
call_if_defined :cancelled, result
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def callbacks
|
70
|
+
@callbacks ||= {}
|
71
|
+
end
|
72
|
+
|
73
|
+
def call_if_defined(callback, param=nil)
|
74
|
+
callbacks[callback].call(param) if callbacks[callback]
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
data/lib/motion-async.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
unless defined?(Motion::Project::App)
|
2
|
+
raise "This must be required from within a RubyMotion Rakefile"
|
3
|
+
end
|
4
|
+
|
5
|
+
lib_dir_path = File.dirname(File.expand_path(__FILE__))
|
6
|
+
Motion::Project::App.setup do |app|
|
7
|
+
app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion-async/**/*.rb")))
|
8
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion-async
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.5'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Darin Wilson
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bacon
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: MotionAsync was written for use with RubyMotion Android, and makes it
|
42
|
+
easy to run code off the main UI thread by providing a friendly wrapper around Android's
|
43
|
+
AsyncTask class.
|
44
|
+
email: darinwilson@gmail.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- LICENSE
|
50
|
+
- README.md
|
51
|
+
- lib/motion-async.rb
|
52
|
+
- lib/motion-async/motion_async.rb
|
53
|
+
- lib/motion-async/motion_async_task.rb
|
54
|
+
- lib/motion-async/version.rb
|
55
|
+
homepage: http://github.com/darinwilson/motion-async
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.2.2
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: A Ruby wrapper around Android's AsyncTask
|
79
|
+
test_files: []
|