我尝试使用 2 个服务
当在主活动的 Oncreate 方法上同时调用这两个服务时,我写的第一个服务只会被执行。
例子:
onCreate{ startService(this,phoneMicToBle::class.java) startService(this,BleMicToPhone::class.java) }
那么 phoneMicToBle 只会被执行。如果我编写了 BleMicToPhone 的服务,那么它将被执行。任何人都可以帮助我如何使其同时工作。
1.BleMicToPhone 服务代码
class BleMicToPhone : Service() {
/**
* Signals whether a recording is in progress (true) or not (false).
*/
private val recordingInProgress = AtomicBoolean(false)
private var recorder: AudioRecord? = null
private var recordingThread: Thread? = null
private var audioManager: AudioManager? = null
private var streamOutput = 0
override fun onCreate() {
Log.d("AudioRelayService", "Sampling rate: " + SAMPLING_RATE_IN_HZ + " Hz")
instance = this
audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
// STREAM_ALARM also works, but STREAM_VOICE_CALL reduces the echo
streamOutput = intent.getIntExtra(STREAM_KEY, AudioManager.STREAM_VOICE_CALL)
displayNotification()
startRecording()
Log.d(TAG, "onStartCommand: AudiRelay service")
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
fun startRecording() {
// Depending on the device one might has to change the AudioSource, e.g. to DEFAULT
// or VOICE_COMMUNICATION
recorder = AudioRecord(
MediaRecorder.AudioSource.DEFAULT,
SAMPLING_RATE_IN_HZ, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE
)
setPreferredInputDevice(recorder!!)
improveRecorder(recorder!!)
recorder!!.startRecording()
recordingInProgress.set(true)
recordingThread = Thread(RecordingRunnable(), "Recording Thread Audio Relay")
recordingThread!!.start()
}
private fun improveRecorder(recorder: AudioRecord) {
val audioSessionId = recorder.audioSessionId
// Turn on Android library filter for reducing background noise in recordings
if (NoiseSuppressor.isAvailable()) {
NoiseSuppressor.create(audioSessionId)
}
// Android library filter for automatic volume control in recordings
if (AutomaticGainControl.isAvailable()) {
AutomaticGainControl.create(audioSessionId)
}
// Android library filter for reducing echo in recordings
if (AcousticEchoCanceler.isAvailable()) {
AcousticEchoCanceler.create(audioSessionId)
}
}
fun stopRecording() {
if (null == recorder) {
return
}
recordingInProgress.set(false)
recorder!!.stop()
recorder!!.release()
recorder = null
recordingThread = null
}
override fun onDestroy() {
stopRecording()
}
fun shutDown() {
stopRecording()
instance = null
stopSelf()
}
private fun displayNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val chan = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"AudioRelayService", NotificationManager.IMPORTANCE_NONE
)
chan.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
val manager = (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
manager.createNotificationChannel(chan)
val notificationBuilder = Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
val notification = notificationBuilder.setOngoing(true)
.setContentTitle(NOTIFICATION_MESSAGE)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(2, notification)
} else {
val notification = Notification.Builder(this)
.setContentTitle(NOTIFICATION_MESSAGE)
startForeground(1, notification.build())
}
}
private inner class RecordingRunnable : Runnable {
override fun run() {
val buffer = ByteBuffer.allocateDirect(BUFFER_SIZE)
val audio = AudioTrack(
streamOutput,
SAMPLING_RATE_IN_HZ,
AudioFormat.CHANNEL_OUT_MONO,
AUDIO_FORMAT,
BUFFER_SIZE,
AudioTrack.MODE_STREAM
)
setPreferredOutputDevice(audio)
audio.play()
while (recordingInProgress.get()) {
val result = recorder!!.read(buffer, BUFFER_SIZE)
if (result < 0) {
Log.w(TAG, "Reading of buffer failed.")
} else {
audio.write(buffer.array(), 0, BUFFER_SIZE)
buffer.clear()
}
}
}
}
fun recordingInProgress(): Boolean {
return recordingInProgress.get()
}
/**
* Function to set the preferred input device to Bluetooth SCO.
* Function has no effect on Android version below Android 6.0 Marshmallow
* @param recorder: AudioRecord instance
*/
private fun setPreferredInputDevice(recorder: AudioRecord) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val inputs = audioManager!!.getDevices(AudioManager.GET_DEVICES_INPUTS)
for (input in inputs) {
if (input.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
recorder.preferredDevice = input
}
}
}
}
/**
* Function to set the preferred input device to a connected AUX line (preferably) or
* the built-in speakers if the AUX line is not connected.
* Function has no effect on Android version below Android 6.0 Marshmallow
* @param audio: AudioTrack instance
*/
private fun setPreferredOutputDevice(audio: AudioTrack) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val outputs = audioManager!!.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
for (output in outputs) {
if (output.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER ||
output.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE) {
audio.preferredDevice = output
}
}
}
}
companion object {
// private const val NOTIFICATION_CHANNEL_ID = "1"
var instance: AudioRelayService? = null
private val TAG = AudioRelayService::class.java.canonicalName
const val STREAM_KEY = "STREAM"
val NOTIFICATION_CHANNEL_ID = (BuildConfig::class.java.getPackage().toString()
+ "." + TAG)
private const val NOTIFICATION_MESSAGE = "Mic Repeater is running."
private val SAMPLING_RATE_IN_HZ = minSupportedSampleRate
private const val CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO
private const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
/**
* Size of the buffer where the audio data is stored by Android
*/
private val BUFFER_SIZE = AudioRecord.getMinBufferSize(
SAMPLING_RATE_IN_HZ,
CHANNEL_CONFIG, AUDIO_FORMAT
)// return the minimum supported audio sample rate
// If none of the sample rates are supported return -1 and handle it in calling method
/*
* Selecting default audio input source for recording since
* AudioFormat.CHANNEL_CONFIGURATION_DEFAULT is deprecated and selecting
* default encoding format.
*/
private val minSupportedSampleRate: Int
private get() {
val validSampleRates = intArrayOf(
8000, 11025, 16000, 22050,
32000, 37800, 44056, 44100, 47250, 48000, 50000, 50400, 88200,
96000, 176400, 192000, 352800, 2822400, 5644800
)
/*
* Selecting default audio input source for recording since
* AudioFormat.CHANNEL_CONFIGURATION_DEFAULT is deprecated and selecting
* default encoding format.
*/
for (validSampleRate in validSampleRates) {
val result = AudioRecord.getMinBufferSize(
validSampleRate,
CHANNEL_CONFIG,
AUDIO_FORMAT
)
if (result > 0) {
// return the minimum supported audio sample rate
return validSampleRate
}
}
// If none of the sample rates are supported return -1 and handle it in calling method
return -1
}
}
}
class PhoneMicToBleService:Service() {
private val recordingInProgress = AtomicBoolean(false)
private var recorder: AudioRecord? = null
private var recordingThread: Thread? = null
private var audioManager: AudioManager? = null
private var streamOutput = 0
override fun onCreate() {
Log.d("AudioRelayService", "Sampling rate: " + SAMPLING_RATE_IN_HZ + " Hz")
instance = this
audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
// STREAM_ALARM also works, but STREAM_VOICE_CALL reduces the echo
streamOutput = intent.getIntExtra(STREAM_KEY, AudioManager.STREAM_VOICE_CALL)
displayNotification()
startRecording()
Log.d(TAG, "onStartCommand: Phonemic to Ble")
return START_STICKY
}
override fun onBind(intent: Intent): IBinder? {
return null
}
fun startRecording() {
// Depending on the device one might has to change the AudioSource, e.g. to DEFAULT
// or VOICE_COMMUNICATION
recorder = AudioRecord(
MediaRecorder.AudioSource.DEFAULT,
SAMPLING_RATE_IN_HZ, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE
)
setPreferredInputDevice(recorder!!)
improveRecorder(recorder!!)
recorder!!.startRecording()
recordingInProgress.set(true)
recordingThread = Thread(RecordingRunnable(), "Recording Thread")
recordingThread!!.start()
}
private fun improveRecorder(recorder: AudioRecord) {
val audioSessionId = recorder.audioSessionId
// Turn on Android library filter for reducing background noise in recordings
if (NoiseSuppressor.isAvailable()) {
NoiseSuppressor.create(audioSessionId)
}
// Android library filter for automatic volume control in recordings
if (AutomaticGainControl.isAvailable()) {
AutomaticGainControl.create(audioSessionId)
}
// Android library filter for reducing echo in recordings
if (AcousticEchoCanceler.isAvailable()) {
AcousticEchoCanceler.create(audioSessionId)
}
}
fun stopRecording() {
if (null == recorder) {
return
}
recordingInProgress.set(false)
recorder!!.stop()
recorder!!.release()
recorder = null
recordingThread = null
}
override fun onDestroy() {
stopRecording()
}
fun shutDown() {
stopRecording()
instance = null
stopSelf()
}
private fun displayNotification() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val chan = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
"PhoneMicToSpeaker", NotificationManager.IMPORTANCE_NONE
)
chan.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
val manager = (getSystemService(NOTIFICATION_SERVICE) as NotificationManager)
manager.createNotificationChannel(chan)
val notificationBuilder = Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
val notification = notificationBuilder.setOngoing(true)
.setContentTitle(NOTIFICATION_MESSAGE)
.setCategory(Notification.CATEGORY_SERVICE)
.build()
startForeground(3, notification)
} else {
val notification = Notification.Builder(this)
.setContentTitle(NOTIFICATION_MESSAGE)
startForeground(4, notification.build())
}
}
private inner class RecordingRunnable : Runnable {
override fun run() {
val buffer = ByteBuffer.allocateDirect(BUFFER_SIZE)
val audio = AudioTrack(
streamOutput,
SAMPLING_RATE_IN_HZ,
AudioFormat.CHANNEL_OUT_MONO,
AUDIO_FORMAT,
BUFFER_SIZE,
AudioTrack.MODE_STREAM
)
setPreferredOutputDevice(audio)
audio.play()
while (recordingInProgress.get()) {
val result = recorder!!.read(buffer, BUFFER_SIZE)
if (result < 0) {
Log.w(TAG, "Reading of buffer failed.")
} else {
audio.write(buffer.array(), 0, BUFFER_SIZE)
buffer.clear()
}
}
}
}
fun recordingInProgress(): Boolean {
return recordingInProgress.get()
}
/**
* Function to set the preferred input device to Bluetooth SCO.
* Function has no effect on Android version below Android 6.0 Marshmallow
* @param recorder: AudioRecord instance
*/
private fun setPreferredInputDevice(recorder: AudioRecord) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val inputs = audioManager!!.getDevices(AudioManager.GET_DEVICES_INPUTS)
for (input in inputs) {
if (input.type == AudioDeviceInfo.TYPE_BUILTIN_MIC ) {
recorder.preferredDevice = input
}
}
}
}
/**
* Function to set the preferred input device to a connected AUX line (preferably) or
* the built-in speakers if the AUX line is not connected.
* Function has no effect on Android version below Android 6.0 Marshmallow
* @param audio: AudioTrack instance
*/
private fun setPreferredOutputDevice(audio: AudioTrack) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val outputs = audioManager!!.getDevices(AudioManager.GET_DEVICES_OUTPUTS)
for (output in outputs) {
if (output.type == AudioDeviceInfo.TYPE_AUX_LINE || output.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES || output.type == AudioDeviceInfo.TYPE_WIRED_HEADSET) {
audio.preferredDevice = output
break
} else if (output.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
audio.preferredDevice = output
}
}
}
}
companion object {
// private const val NOTIFICATION_CHANNEL_ID = "1"
var instance: PhoneMicToBleService? = null
private val TAG = PhoneMicToBleService::class.java.canonicalName
const val STREAM_KEY = "STREAM PHONE MIC TO BLE SERVICE"
val NOTIFICATION_CHANNEL_ID = (BuildConfig::class.java.getPackage().toString()
+ "." + TAG)
private const val NOTIFICATION_MESSAGE = "Mic Repeater is running."
private val SAMPLING_RATE_IN_HZ = minSupportedSampleRate
private const val CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO
private const val AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT
/**
* Size of the buffer where the audio data is stored by Android
*/
private val BUFFER_SIZE = AudioRecord.getMinBufferSize(
SAMPLING_RATE_IN_HZ,
CHANNEL_CONFIG, AUDIO_FORMAT
)// return the minimum supported audio sample rate
// If none of the sample rates are supported return -1 and handle it in calling method
/*
* Selecting default audio input source for recording since
* AudioFormat.CHANNEL_CONFIGURATION_DEFAULT is deprecated and selecting
* default encoding format.
*/
private val minSupportedSampleRate: Int
private get() {
val validSampleRates = intArrayOf(
8000, 11025, 16000, 22050,
32000, 37800, 44056, 44100, 47250, 48000, 50000, 50400, 88200,
96000, 176400, 192000, 352800, 2822400, 5644800
)
/*
* Selecting default audio input source for recording since
* AudioFormat.CHANNEL_CONFIGURATION_DEFAULT is deprecated and selecting
* default encoding format.
*/
for (validSampleRate in validSampleRates) {
val result = AudioRecord.getMinBufferSize(
validSampleRate,
CHANNEL_CONFIG,
AUDIO_FORMAT
)
if (result > 0) {
// return the minimum supported audio sample rate
return validSampleRate
}
}
// If none of the sample rates are supported return -1 and handle it in calling method
return -1
}
}
}