我正在学习如何在Android上使用服务和音频记录。
我想创建一个应用程序,除了启动服务之外什么也不会做:在请求权限(RECORD_AUDIO
,INTERNET
)之后,该应用程序仅从startService()
调用MainActivity
。
然后该服务将录制音频并将其流式传输到给定的IP地址。我从this question的答案中获得了音频流服务器和客户端的灵感。到目前为止,我正在Android Studio Emulator上的Android 6.0上测试该应用程序。
这是我的服务的onStartCommand()
方法。我创建一个新线程,等待用户在权限请求对话框中单击“允许”。收到权限后,我将初始化AudioRecord
,然后在while
循环中从中读取并发送数据。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Thread streamThread = new Thread(new Runnable() {
@Override
public void run() {
waitForPermissions();
try {
DatagramSocket socket = new DatagramSocket();
byte[] buffer = new byte[minBufSize];
DatagramPacket packet;
final InetAddress destination = InetAddress.getByName(address);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, minBufSize * 10);
recorder.startRecording();
while (status == true) {
minBufSize = recorder.read(buffer, 0, buffer.length);
packet = new DatagramPacket(buffer, buffer.length, destination, port);
socket.send(packet);
}
} catch (...) {...}
}
});
streamThread.start();
return START_STICKY;
}
在onDestroy()
中,我将标志status
设置为false
,以便终止在onStartCommand()
中创建的线程中的循环。然后,我松开recorder
,以便可以对其进行初始化并再次使用。
@Override
public void onDestroy(){
status = false;
recorder.stop();
recorder.release();
super.onDestroy();
}
此应用程序只能部分运行:
AudioRecord
和所有内容,没有引发异常),只有我听不到任何声音。当我再次运行该应用程序(并因此再次调用startService()
)时,我在Logcat中看到一个异常,告诉我AudioRecord
启动失败,错误代码为-38(这意味着前一个麦克风正在使用中)服务实例)。我在这里做错了什么?为什么该服务正在运行,但是当我终止该应用程序时,音频却没有流过?
非常感谢您的回答。
编辑:
我知道这种方法不适用于较新的Android版本。这个应用程式仅供私人使用,可以在我的一部旧手机上执行(搭载Android 5.1.1或6.0)。
[阅读完this answer中的文章后,我将以前在onDestroy
中的内容移至了onTaskRemoved
。但是,什么都没有改变。
该服务显然是仍在发送一些数据包。我在Wireshark中看着它们,数据包仍在离开设备,服务器仍在接收它们。数据包的有效载荷非零,对于每个数据包都是唯一的。
服务器可能有问题吗?它的代码看起来像这样(取自here):
...
static int sampleRate = 8000;
public static void main(String args[]) throws Exception {
DatagramSocket serverSocket = new DatagramSocket(port);
byte[] receiveData = new byte[512];
format = new AudioFormat(sampleRate, 16, 1, true, false);
dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
sourceDataLine.open(format);
sourceDataLine.start();
FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
volumeControl.setValue(volumeControl.getMaximum());
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
while (status == true) {
ByteArrayInputStream baiss = new ByteArrayInputStream(
receivePacket.getData());
serverSocket.receive(receivePacket);
ais = new AudioInputStream(baiss, format, receivePacket.getLength());
toSpeaker(receivePacket.getData());
}
sourceDataLine.drain();
sourceDataLine.close();
}
public static void toSpeaker(byte soundbytes[]) {
try {
sourceDataLine.write(soundbytes, 0, soundbytes.length);
} catch (Exception e) {
System.out.println("Not working in speakers...");
e.printStackTrace();
}
}
...
服务器的采样率和缓冲区大小与Android应用程序相同。格式为AudioFormat.ENCODING_PCM_16BIT
。
阅读本文:What exactly happens to running services when you swipe an android app from recent app list ?
在您的情况下,该服务停止工作,由于开始发粘,因此在娱乐中似乎出现了录制失败的问题,我不知道这是什么。在改善服务的意义上,请使用IntentService或前台服务来解决问题。
BTW,如果您想在android 8或更高版本中使用您的应用,则可能仅需使用ForeGrounService,因为如果应用在后台运行,几分钟后IntentService将被系统杀死。阅读此:Background Execution Limits
因此,在测试了所有解决方案之后,您可能会同意我使用前台服务:Android Foreground Service Example