A powerful React Native library designed specifically to handle autoprint functionality with continuous fetching/long polling in the background and printing. This package is optimized for a single autoprint process - if you need multiple background processes, consider using a different package or updating this one to support dynamic background services and semaphores.
Built on Android Services API - Learn more
Forked from react-native-background-actions
- Background Task Execution - Run tasks in the background with notification support
- Alarm Management - Schedule exact alarms with proper permissions
- Battery Optimization - Handle battery optimization settings
- Semaphore Management - Thread-safe operations with custom semaphores
- Service Management - Monitor and control background services
- Permission Handling - Request and check various system permissions
npm install upsell-background-actionsAdd these permissions to your android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />import BackgroundService from 'upsell-background-actions';import BackgroundService from 'upsell-background-actions';
const sleep = (time) =>
new Promise((resolve) => setTimeout(() => resolve(), time));
const veryIntensiveTask = async (taskDataArguments) => {
const { delay } = taskDataArguments;
await new Promise(async (resolve) => {
for (let i = 0; BackgroundService.isBackgroundServiceRunning(); i++) {
console.log(i);
await sleep(delay);
}
});
};
const options = {
taskName: 'AutoPrint', // THE NAME HAS TO BE THIS OTHERWISE IT WONT WORK WILL NEED TO MAKE THIS MORE DYNAMIC IN THE FUTURE
taskTitle: 'AutoPrint',
taskDesc: 'AutoPrint',
taskIcon: {
name: 'ic_launcher',
type: 'mipmap',
},
color: '#ff00ff',
linkingURI: 'upsell://autoprint/off',
parameters: {
delay: 3000,
},
};
// Start the service
await BackgroundService.setIsBackgroundServiceRunning(true);
await BackgroundService.start(veryIntensiveTask, options, 90000);
// Update notification
await BackgroundService.updateNotification({
taskDesc: 'New ExampleTask description',
});// Stop the background loop
await BackgroundService.setIsBackgroundServiceRunning(false);
// Stop the alarm restart mechanism
await BackgroundService.stopAlarm();
// Send stop broadcast to service
await BackgroundService.sendStopBroadcast();
// Interrupt any queued threads
await BackgroundService.interruptQueuedThread();| Method | Description | Returns |
|---|---|---|
start(task, options, triggerTime) |
Starts background task | Promise<void> |
isBackgroundServiceRunning() |
Checks if service is running | Promise<Boolean> |
setIsBackgroundServiceRunning(value) |
Sets service state in database | Promise<void> |
updateNotification(taskData) |
Updates notification (Android only) | Promise<void> |
sendStopBroadcast() |
Sends stop broadcast | Promise<void> |
listRunningServices() |
Lists all running services | Promise<Array> |
| Method | Description | Returns |
|---|---|---|
setAlarm(milliseconds) |
Sets alarm trigger time | Promise<void> |
stopAlarm() |
Stops current alarm | Promise<void> |
checkScheduleExactAlarmPermission() |
Checks exact alarm permission | Promise<Boolean> |
requestExactAlarmPermission() |
Requests exact alarm permission | Promise<void> |
| Method | Description | Returns |
|---|---|---|
isIgnoreBatteryOptimization() |
Checks battery optimization status | Promise<Boolean> |
requestIgnoreBatteryOptmization() |
Requests battery optimization exemption | Promise<void> |
requestActionIgnoreBatteryOptimizationSettings() |
Opens battery settings | Promise<void> |
| Method | Description |
|---|---|
lock() |
Acquires general-purpose lock |
unlock() |
Releases general-purpose lock |
lockAddPrinterSemaphore() |
Acquires printer-specific lock |
unlockAddPrinterSemaphore() |
Releases printer-specific lock |
interruptQueuedThread() |
Interrupts queued threads |
const options = {
taskName: 'UniqueTaskName', // Unique task identifier
taskTitle: 'Task Title', // Notification title
taskDesc: 'Task Description', // Notification description
taskIcon: {
name: 'icon_name', // Icon resource name
type: 'mipmap', // Resource type (mipmap/drawable)
package: 'com.yourapp', // Package name (optional)
},
color: '#FF0000', // Notification color (hex)
linkingURI: 'yourapp://action', // Deep link URI (optional)
progressBar: {
// Progress bar (optional)
indeterminate: false,
max: 100,
value: 0,
},
};try {
await BackgroundService.start(myTask, options, 5000);
} catch (error) {
console.error('Failed to start background task:', error);
if (error.message.includes('Pending Intent not Found')) {
// Handle pending intent error
} else if (error.message.includes('Not Safe to stop Alarm')) {
// Handle unsafe alarm stop
}
}Android Only - This library provides:
- Background tasks with persistent notifications
- Exact alarm scheduling
- Battery optimization handling
- Background service management
- Always check permissions before performing sensitive operations
- Handle errors gracefully with proper try-catch blocks
- Clean up resources by calling unlock() and stop methods
- Use exact alarms - Don't use WorkManager or JobScheduler as they adhere to Doze mode
This package bypasses Android Doze Mode (Learn about Doze mode) to enable continuous data fetching and printing.
The package uses React Native's Headless JS API (Documentation) which utilizes the Android Service API (Learn about Services).
- Database Storage: Service state is stored in database rather than memory because React Native may flush memory on weaker devices. Don't fall into the rabbit hole of trying to optimize this just for better app size - React Native/JavaScript is known to have memory flushed to create space as deemed necessary
- Service Stopping: Stopping the service doesn't immediately stop your JS code - use
isBackgroundServiceRunning()flags to control loops. Your JS code keeps running but will eventually be stopped - Headless JS: The service calls HeadlessService to handle JavaScript tasks with start/finish listeners
- Single Process Design: This package expects only one autoprint process to be running. If you need multiple background processes, use a different package or update this one to support dynamic
isBackgroundServicefunction and semaphores - Exact Alarms: NEEDS TO BE DONE - DO NOT use anything other than EXACT ALARM Android API. WorkManager and JobScheduler don't work as they adhere to Doze mode
- Sempahore Limitations: Note that you cannot implement Semaphore in JavaScript - it's a single-threaded language that does concurrency but cannot do locking mechanisms
- Expo Compatibility: Previous errors when upgrading to EXPO 53 were caused by undocumented changes where Intent could be null in newer versions
HeadlessJSService.kt- Main service implementationHeadlessJSServiceContext.kt- Service context management
Behind the scenes, this package uses React Native's Headless JS API and the Android Service API. A previous error when trying to upgrade to EXPO 53 was caused by an undocumented error when trying to start a Service where the Intent was able to be null but on older versions of EXPO and React Native it was not set to be null.
Headless JS works by calling HeadlessService in the background to handle the JavaScript task passed to it. It has listeners that indicate when the process is started or finished. However, when stopping the service it does not stop your JS code that you pass in React Native. Instead your JS code keeps running but will eventually be stopped, hence why we use isBackgroundServiceRunning and setIsBackgroundServiceRunning to stop the JS process.
Check the example/ directory for a complete implementation showing all features.
The current autoprint process uses the long polling concept to fetch data and print, this introduces a problem where we don't exactly know and cannot notify users if this connection is severed or intterupted because sometimes an error does occur in API level such as error 500, or 501 however they usually only last for a second or 1 to two minutes.
Another problem is, this package doesn't fully solve the doze mode issue on different devices hence why we sill run into a problem where sometimes autoprint doesn't run properly. This is an android problem as I've said multiple times to Sindri that Android by design is not meant to have continous process running in the background. You can do it but you would need to root the device, but by then it would have taken too much effort and it wouldn't be a viable solution as it's not scalable
To fix this I recommend moving into a websocket procedure or SSE procedure on the javascript side. The good thing about using this is the server can tell when a connection is severed, and the device could also tell that. Combining it with package that restarts the process every n milliseconds, we will be able to send notification to remind users if the process is :
- Restarting
- Connection Established
- Connection Disconnected
PLEASE DO NOT FALL INTO THE TRAP OF TRYING TO BYPASS DOZE MODE TOO MUCH, THIS IS SIMPLY ANDROID's LIMITATION
To Edit the function in Android my ideal workflow is to open this in VSCODE and Android Studio. The difference is in Android Studio you open it from example/android folder. Do not open it from /android it must be from example/android folder, otherwise you'd run into compile issues or the IDE simply not recognizing the syntaxes. A comprehensive Readme is available in android/src/main/java/com/upsellbackgroundactions
MIT Β© Dwikavindra
Made with create-react-native-library