首页 > 解决方案 > Android 背景上的 react-native-video 崩溃

问题描述

我正在为音频播放实现视频组件:

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>
    );
  }
}

在物理设备(Pixel 2 8.1.0)上运行此应用程序并将应用程序置于后台时,音频播放会在 3-10 分钟后停止并且应用程序崩溃。只有重新启动应用程序才能重新开始播放。仅当设备未连接电源线时才会出现此问题。当有外部电源时,应用程序按预期运行。

我从日志中提取了这个:

ActivityManager: Killing 20894:com.videotest/u0a419 (adj 700): excessive cpu 53130 during 300068 dur=1762444 limit=2

看起来Android拒绝该应用程序滥用CPU?有什么解决方案可以强制操作系统允许这个应用程序不间断地运行,就像我们有外部电源时那样?

反应原生版本:0.57 反应原生视频版本:3.2.1

标签: androidreact-nativereact-native-video

解决方案


我刚刚在此处就 react-native-video 问题回答了这个问题,并将在此处复制。

==================================================== =======

通过遵循 ExoPlayer 常见问题解答的背景音频指南,我已经设法解决了这个问题,至少对于我的用例:https ://google.github.io/ExoPlayer/faqs.html#how-do-i-keep-音频播放时我的应用程序是背景

当我的应用程序在后台运行时,如何继续播放音频?

当您的应用程序在后台时,您需要采取一些步骤来确保继续播放音频:

  1. 您需要有一个正在运行的前台服务。这可以防止系统终止您的进程以释放资源。
  2. 您需要持有 WifiLock 和 WakeLock。这些可确保系统保持 WiFi 无线电和 CPU 处于唤醒状态。

停止服务并在不再播放音频后立即释放锁定非常重要。

为了在RN中实现wakelock和wifilock,我添加了这个包:“react-native-wakeful”:“^0.1.0”

为了在 RN 中实现前台服务,我添加了这个包:“@voximplant/react-native-foreground-service”:“^1.1.0”

请务必按照这 2 个软件包的自述文件中的说明更新 AndroidManifest.xml。

这个演示 repo 的 App.js 有一个如何使用前台服务的清晰示例: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
    }
});

该示例中缺少的一件事是stopService()一开始就需要一个守卫:

  async stopService() {
    // Only Android platform is supported
    if (Platform.OS !== 'android') {
      return;
    }

    await VIForegroundService.stopService();
  }

在我的应用程序中,当用户单击播放/暂停按钮时,我会这样做:

    // 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()可以留给iOS,因为在这种情况下它们根本就没有操作。

如果有人需要任何帮助,请告诉我 :)


推荐阅读