Skip to content

Commit 3b9b0e1

Browse files
authored
Merge pull request #5 from jamesisaac/0.2
Native Android implementation
2 parents 1af1814 + 070edf1 commit 3b9b0e1

17 files changed

+654
-136
lines changed

.gitignore

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
/.idea
2-
/node_modules
3-
npm-debug.log
1+
/.idea/
2+
/android/.idea/
3+
/android/gen/
4+
/android/gradle/
5+
/android/gradlew
6+
/android/gradlew.bat
7+
/android/local.properties
8+
/node_modules/
9+
npm-debug.log
10+
yarn.lock
11+
yarn-debug.log

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2017 James Isaac
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 all
13+
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 THE
21+
SOFTWARE.

README.md

Lines changed: 148 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,150 @@
11
# react-native-background-task
22

3-
**WORK IN PROGRESS / NOT YET READY FOR PRODUCTION USE**
3+
[![npm version](https://img.shields.io/npm/v/react-native-background-task.svg)](https://www.npmjs.com/package/react-native-background-task)
4+
[![license](https://img.shields.io/github/license/jamesisaac/react-native-background-task.svg)](https://opensource.org/licenses/MIT)
5+
[![npm downloads](https://img.shields.io/npm/dm/react-native-background-task.svg)](https://www.npmjs.com/package/react-native-background-task)
46

5-
Periodic background tasks for React Native apps, cross-platform (iOS and Android), which run even when the app is closed.
7+
Periodic background tasks for React Native apps, cross-platform (iOS and
8+
Android), which run even when the app is closed.
69

7-
This builds on top of the native modules provided by the following two libraries:
10+
This library allows the scheduling of a single periodic task, which executes
11+
when the app is in the background or closed, no more frequently than every 15
12+
minutes. Network, AsyncStorage etc can be used (anything except UI), so
13+
perfect for things like a background data sync for offline support.
814

9-
- **iOS**: [react-native-background-fetch](https://github.com/transistorsoft/react-native-background-fetch), which uses the iOS-specific `Background Fetch` technique.
10-
- **Android**: [react-native-background-job](https://github.com/vikeri/react-native-background-job), which provides a job scheduler on RN's built-in [Headless JS](https://facebook.github.io/react-native/docs/headless-js-android.html) (Android only).
15+
Behind the scenes, this library takes a different approach with each platform:
1116

12-
To achieve a unified API, this package exposes the lowest common denominator (e.g. only support for a single task, even though Android can support multiple).
17+
- **Android**: A native implementation, which provides scheduling on top of
18+
RN's built-in [Headless JS](https://facebook.github.io/react-native/docs/headless-js-android.html)
19+
(Android only).
20+
- Min API level: 16 (Android 4.1).
21+
- **iOS**: [react-native-background-fetch](https://github.com/transistorsoft/react-native-background-fetch),
22+
which uses the iOS-specific `Background Fetch` technique.
1323

14-
For more per-platform flexibility, those libraries should be used individually.
24+
To achieve a unified API, this library exposes the lowest common denominator
25+
(e.g. only support for a single task, even though Android can support multiple).
26+
27+
For more per-platform flexibility, there are other platform-specific libraries
28+
with more granular customisation.
1529

1630
## Installation
1731

1832
```bash
19-
$ npm install --save react-native-background-task
33+
$ npm install react-native-background-task --save
2034
```
35+
36+
### Android
37+
38+
1. The linking of the library can be done automatically by running:
39+
40+
```bash
41+
$ react-native link react-native-background-task
42+
```
43+
44+
2. One manual step is still needed - in your project file
45+
`android/app/src/main/java/myapp/MainApplication.java`, add the following to
46+
the end of the `onCreate()` method:
47+
48+
```java
49+
BackgroundTaskPackage.useContext(this);
50+
```
51+
52+
### iOS
2153

22-
* **iOS**: Follow native iOS module installation instructions from
23-
[react-native-background-fetch](https://github.com/transistorsoft/react-native-background-fetch)
24-
* **Android**: Follow native Android module installation instructions from
25-
[react-native-background-job](https://github.com/vikeri/react-native-background-job)
26-
* Recommended version: v1.1.0 (API has changed as of v1.1.3)
54+
For iOS support, this library relies on version 2.0.x of
55+
[react-native-background-fetch](https://github.com/transistorsoft/react-native-background-fetch)
56+
which can be installed as follows:
57+
58+
```bash
59+
$ npm install [email protected] --save
60+
$ react-native link react-native-background-fetch
61+
```
62+
63+
This library will behave correctly on iOS as long as `react-native-background-fetch`
64+
is installed alongside it, and has been linked with your project.
2765

2866
## API
2967

30-
### `register(task, options)`
68+
### `define(task)`
3169

32-
Define the task that this module should be executing.
70+
Define the JS code that this module should be executing.
3371

34-
- Will overwrite any previously registered task.
35-
- Should be called at the root of your main app entrypoint file.
36-
- Should `console.error` if it can't register the task
72+
- Should be called at the top level of your JS, **not** inside a component.
73+
This is because in headless mode no components are mounted, but the code
74+
still needs to be accessible.
75+
- Will overwrite any previously defined task.
3776

3877
Parameters:
3978

40-
* **`task`**: **required** `() => void` - Function to be executed in the background
41-
* **`options`**: `?object` - Any configuration you want to be set with
79+
- **`task`**: **required** `() => void` - Function to be executed in the background
80+
81+
### `schedule(options)`
82+
83+
Specify the scheduling options for the task, and register it with the
84+
platform's scheduler.
85+
86+
- Should be called from inside a component (e.g. your App's
87+
`componentDidMount`). This is to avoid double-scheduling when the task
88+
launches in headless mode.
89+
- Will `console.warn` if the device is task was unable to be scheduled.
90+
91+
Parameters:
92+
93+
- **`options?`**: `Object` - Any configuration you want to be set with
4294
the task. Note that most of these will only work on one platform.
4395

44-
* **`period`** `number` - (Android only) Desired number of seconds between each
96+
- **`period?`** `number` - (Android only) Desired number of seconds between each
4597
execution of the task. Even on Android, the OS will only take this as a
46-
recommendation, and will likely enforce a minimum of 15 minutes (similar to
47-
iOS). Default is 900 (15 minutes)
48-
* **`timeout`** `number` - (Android only) Number of seconds the task will have
98+
recommendation, and will enforce a minimum of 15 minutes (similar to iOS).
99+
Default is 900 (15 minutes)
100+
- **`timeout?`** `number` - (Android only) Number of seconds the task will have
49101
to execute. iOS has a hardcoded limit of 30 seconds. Default 30 seconds.
50102

103+
### `finish()`
104+
105+
**Must be called at the end of your task** to indicate to the OS that it's
106+
finished. (Only required on iOS, no-op on Android).
107+
51108
### `cancel()`
52109

53-
Cancels any currently registered task.
110+
Cancels any currently scheduled task.
54111

55-
### `finish()`
112+
### `statusAsync()`
113+
114+
Query the status of background tasks within this app. Returns a Promise with
115+
an object of the following shape:
116+
117+
- **`available`** `boolean` - Whether background tasks are available to the app.
118+
On Android this will always be true, but on iOS background tasks could be
119+
blocked (see [UIBackgroundRefreshStatus](https://developer.apple.com/documentation/uikit/uibackgroundrefreshstatus)).
120+
- **`unavailableReason?`** `string` - If unavailable, gives the reason:
121+
- **`BackgroundTask.UNAVAILABLE_DENIED`** - The user explicitly disabled
122+
background behavior for this app or for the whole system.
123+
- **`BackgroundTask.UNAVAILABLE_RESTRICTED`** - Background updates
124+
unavailable and can't be enabled by the user (e.g. parental controls).
125+
126+
## Caveats
127+
128+
- The exact timings of tasks are unpredictable (depends on device sleep state
129+
etc.), and cannot be more frequent than every 15 minutes. This library
130+
should only be used for tasks that can have inexact timing, such as the
131+
periodic background syncing of data.
132+
133+
Android:
134+
135+
- Tasks will not run while the app is in the foreground, and scheduling can be
136+
made even more imprecise when the app goes in/out of the foreground (as tasks
137+
are rescheduled as soon as the app goes into the background).
138+
139+
iOS:
140+
141+
- iOS Background Fetch does not work in the simulator. It must be tested on a
142+
real device.
143+
- The user can disable Background App Refresh for your app from their Settings
144+
(use `statusAsync()` to check for this).
56145

57-
Must be called at the end of your task to indicate to the OS that it's
58-
finished. (Only required on iOS, no-op on Android).
59146

60-
## Example
147+
## Examples
61148

62149
### Simple
63150

@@ -66,12 +153,16 @@ import React from 'react'
66153
import { Text } from 'react-native'
67154
import BackgroundTask from 'react-native-background-task'
68155

69-
BackgroundTask.register(() => {
156+
BackgroundTask.define(() => {
70157
console.log('Hello from a background task')
71158
BackgroundTask.finish()
72159
})
73160

74161
class MyApp extends React.Component {
162+
componentDidMount() {
163+
BackgroundTask.schedule()
164+
}
165+
75166
render() {
76167
return <Text>Hello world</Text>
77168
}
@@ -82,10 +173,10 @@ class MyApp extends React.Component {
82173

83174
```js
84175
import React from 'react'
85-
import { AsyncStorage, Button, Text } from 'react-native'
176+
import { Alert, AsyncStorage, Button } from 'react-native'
86177
import BackgroundTask from 'react-native-background-task'
87178

88-
BackgroundTask.register(async () => {
179+
BackgroundTask.define(async () => {
89180
// Fetch some data over the network which we want the user to have an up-to-
90181
// date copy of, even if they have no network when using the app
91182
const response = await fetch('http://feeds.bbci.co.uk/news/rss.xml')
@@ -96,11 +187,34 @@ BackgroundTask.register(async () => {
96187

97188
// Remember to call finish()
98189
BackgroundTask.finish()
99-
}, {
100-
period: 1800, // Aim to run every 30 mins - more conservative on battery
101190
})
102191

103192
class MyApp extends React.Component {
193+
componentDidMount() {
194+
BackgroundTask.schedule({
195+
period: 1800, // Aim to run every 30 mins - more conservative on battery
196+
})
197+
198+
// Optional: Check if the device is blocking background tasks or not
199+
this.checkStatus()
200+
}
201+
202+
async checkStatus() {
203+
const status = await BackgroundTask.statusAsync()
204+
205+
if (status.available) {
206+
// Everything's fine
207+
return
208+
}
209+
210+
const reason = status.unavailableReason
211+
if (reason === BackgroundTask.UNAVAILABLE_DENIED) {
212+
Alert.alert('Denied', 'Please enable background "Background App Refresh" for this app')
213+
} else if (reason === BackgroundTask.UNAVAILABLE_RESTRICTED) {
214+
Alert.alert('Restricted', 'Background tasks are restricted on your device')
215+
}
216+
}
217+
104218
render() {
105219
return (
106220
<View>

android/build.gradle

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
buildscript {
2+
repositories {
3+
jcenter()
4+
}
5+
6+
dependencies {
7+
classpath 'com.android.tools.build:gradle:1.1.3'
8+
}
9+
}
10+
11+
apply plugin: 'com.android.library'
12+
13+
android {
14+
compileSdkVersion 23
15+
buildToolsVersion "25.0.2"
16+
17+
defaultConfig {
18+
minSdkVersion 16
19+
targetSdkVersion 22
20+
versionCode 1
21+
versionName "1.0"
22+
}
23+
lintOptions {
24+
abortOnError false
25+
}
26+
}
27+
28+
repositories {
29+
mavenCentral()
30+
}
31+
32+
dependencies {
33+
compile 'com.facebook.react:react-native:+'
34+
compile 'com.evernote:android-job:1.1.11'
35+
}

android/src/main/AndroidManifest.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.jamesisaac.rnbackgroundtask">
4+
<application>
5+
<service android:name=".HeadlessTaskService"
6+
android:enabled="true" />
7+
</application>
8+
</manifest>

0 commit comments

Comments
 (0)