Reputation: 21
I'm implementing Video component for audio playback:
import React, {Component} from 'react';
import {StyleSheet, View,} from 'react-native';
import Video from 'react-native-video';
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Video source={require('./cats.mp3')}
ref={(ref) => {
this.player = ref
}}
playInBackground={true}
playWhenInactive={true}
onBuffer={this.onBuffer}
onEnd={this.onEnd}
onError={this.videoError}
/>
</View>
);
}
}
When running this on physical device (Pixel 2 8.1.0) and leaving the app on background, the audio playback stops after 3-10 minutes and app crashes. Playback can only be restarted by relaunching the app. The issue appears only when there is no power cable attached to device. When there is external power, the app runs as it is supposed to.
I pulled this from the log:
ActivityManager: Killing 20894:com.videotest/u0a419 (adj 700): excessive cpu 53130 during 300068 dur=1762444 limit=2
It looks like Android rejects the app for cpu misuse? What could be the solution to force the OS to allow this app to run without interruptions, as it does when we have external power?
react native version: 0.57 react-native-video version: 3.2.1
Upvotes: 2
Views: 2581
Reputation: 463
I just answered this question on the react-native-video issues here and will copy it here.
=========================================================
I've managed to resolve this issue, at least for my use case, by following the ExoPlayer FAQ's guidelines for background audio: https://google.github.io/ExoPlayer/faqs.html#how-do-i-keep-audio-playing-when-my-app-is-backgrounded:
How do I keep audio playing when my app is backgrounded?
There are a few steps that you need to take to ensure continued playback of audio when your app is in the background:
- You need to have a running foreground service. This prevents the system from killing your process to free up resources.
- You need to hold a WifiLock and a WakeLock. These ensure that the system keeps the WiFi radio and CPU awake.
It’s important that you stop the service and release the locks as soon as audio is no longer being played.
To implement wakelock and wifilock in RN, I added this package: "react-native-wakeful": "^0.1.0"
To implement a foreground service in RN, I added this package: "@voximplant/react-native-foreground-service": "^1.1.0"
Be sure to update AndroidManifest.xml as described in the READMEs for those 2 packages.
This demo repo's App.js has a clear example of how to use the foreground service: https://github.com/voximplant/react-native-foreground-service-demo/blob/master/App.js:
/**
* Copyright (c) 2011-2019, Zingaya, Inc. All rights reserved.
*
* @format
* @flow
* @lint-ignore-every XPLATJSCOPYRIGHT1
*/
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View, Button} from 'react-native';
import VIForegroundService from "@voximplant/react-native-foreground-service";
type Props = {};
export default class App extends Component<Props> {
async startService() {
if (Platform.OS !== 'android') {
console.log('Only Android platform is supported');
return;
}
if (Platform.Version >= 26) {
const channelConfig = {
id: 'ForegroundServiceChannel',
name: 'Notification Channel',
description: 'Notification Channel for Foreground Service',
enableVibration: false,
importance: 2
};
await VIForegroundService.createNotificationChannel(channelConfig);
}
const notificationConfig = {
id: 3456,
title: 'Foreground Service',
text: 'Foreground service is running',
icon: 'ic_notification',
priority: 0
};
if (Platform.Version >= 26) {
notificationConfig.channelId = 'ForegroundServiceChannel';
}
await VIForegroundService.startService(notificationConfig);
}
async stopService() {
await VIForegroundService.stopService();
}
render() {
return (
<View style={styles.container}>
<Button title="Start foreground service" onPress={() => this.startService()}/>
<View style={styles.space}/>
<Button title="Stop foreground service" onPress={() => this.stopService()}/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
space: {
flex: 0.1
}
});
The one thing missing from that example is that stopService()
needs a guard at the start:
async stopService() {
// Only Android platform is supported
if (Platform.OS !== 'android') {
return;
}
await VIForegroundService.stopService();
}
In my app, when the user clicks the Play/Pause button I do this:
// play or pause the player, then.....
if (this.isPaused) {
// release wakelock, wifilock and stop foreground service
wakeful.release();
this.stopService();
} else {
// acquire wakelock, wifilock and start foreground service
wakeful.acquire();
this.startService();
}
wakeful.release()
/ wakeful.acquire()
can be left in for iOS as they simply no-op in that case.
If anyone needs any help, just let me know :)
Upvotes: 1