android音频服务,Android 一次完美的跨进程服务共享实践

privatevariRecorder: IRecorder? = nullprivatevarcurrentRecorderResult: RecorderResult = RecorderResultprivatevarcurrentWeight: Int= - 1

privatevalremoteCallbackList: RemoteCallbackList = RemoteCallbackList

privatevalmBinder: IRecorderService.Stub = object: IRecorderService.Stub {

overridefunstartRecording(recorderConfig: RecorderConfig){startRecordingInternal(recorderConfig)}

overridefunstopRecording(recorderConfig: RecorderConfig){if(recorderConfig.recorderId == currentRecorderResult.recorderId)stopRecordingInternalelse{notifyCallBack {it.onException("Cannot stop the current recording because the recorderId is not the same as the current recording",currentRecorderResult)}}}

overridefungetActiveRecording: RecorderResult? {returncurrentRecorderResult}

overridefunisRecording(recorderConfig: RecorderConfig?): Boolean{returnif(recorderConfig?.recorderId == currentRecorderResult.recorderId)iRecorder?.isRecording ?: falseelsefalse}

overridefunregisterCallback(callBack: IRecorderCallBack){remoteCallbackList.register(callBack)}

overridefununregisterCallback(callBack: IRecorderCallBack){remoteCallbackList.unregister(callBack)}

}

overridefunonBind(intent: Intent?): IBinder? {returnmBinder}

@SynchronizedprivatefunstartRecordingInternal(recorderConfig: RecorderConfig){

valwillStartRecorderResult =RecorderResultBuilder.aRecorderResult.withRecorderFile(recorderConfig.recorderFile).withRecorderId(recorderConfig.recorderId).build

if(ContextCompat.checkSelfPermission(this@RecorderService,android.Manifest.permission.RECORD_AUDIO)!= PackageManager.PERMISSION_GRANTED) {logD( "Record audio permission not granted, can't record")notifyCallBack {it.onException("Record audio permission not granted, can't record",willStartRecorderResult)}return}

if(ContextCompat.checkSelfPermission(this@RecorderService,android.Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED) {logD( "External storage permission not granted, can't save recorded")notifyCallBack {it.onException("External storage permission not granted, can't save recorded",willStartRecorderResult)}return}

if(isRecording) {

valweight = recorderConfig.weight

if(weight < currentWeight) {logD( "Recording with weight greater than in recording")notifyCallBack {it.onException("Recording with weight greater than in recording",willStartRecorderResult)}return}

if(weight > currentWeight) {//只要权重大于当前权重,立即停止当前。stopRecordingInternal}

if(weight == currentWeight) {if(recorderConfig.recorderId == currentRecorderResult.recorderId) {notifyCallBack {it.onException("The same recording cannot be started repeatedly",willStartRecorderResult)}return} else{stopRecordingInternal}}

startRecorder(recorderConfig, willStartRecorderResult)

} else{

startRecorder(recorderConfig, willStartRecorderResult)

}

}

privatefunstartRecorder(recorderConfig: RecorderConfig,willStartRecorderResult: RecorderResult){logD( "startRecording result ${willStartRecorderResult.toString}")

iRecorder = when(recorderConfig.recorderOutFormat) {RecorderOutFormat.MPEG_4, RecorderOutFormat.AMR_WB -> {JLMediaRecorder}RecorderOutFormat.PCM -> {JLAudioRecorder}}

valresult = iRecorder?.startRecording(recorderConfig)

if(!result.isNullOrEmpty) {logD( "startRecording result $result")notifyCallBack {it.onException(result, willStartRecorderResult)}} else{currentWeight = recorderConfig.weightnotifyCallBack {it.onStart(willStartRecorderResult)}currentRecorderResult = willStartRecorderResult}}

privatefunisRecording: Boolean{returniRecorder?.isRecording ?: false}

@SynchronizedprivatefunstopRecordingInternal{logD( "stopRecordingInternal")iRecorder?.stopRecordingcurrentWeight = - 1iRecorder = nullMediaScannerConnection.scanFile(this,arrayOf(currentRecorderResult.recorderFile?.absolutePath),null,null)notifyCallBack {it.onStop(currentRecorderResult)}}

privatefunnotifyCallBack(done: ( IRecorderCallBack)-> Unit) {valsize = remoteCallbackList.beginBroadcastlogD( "recorded notifyCallBack size $size")( 0until size).forEach {done(remoteCallbackList.getBroadcastItem(it))}remoteCallbackList.finishBroadcast}

}

这里需要注意的几点:

因为是跨进程服务,启动录音的时候有可能是多个app在同一时间启动,还有可能在一个App录音的同时,另一个App调用停止的功能,所以这里维护好当前currentRecorderResult对象的维护,还有一个currentWeight字段也很重要,这个字段主要是维护优先级的问题,只要有比当前优先级高的指令,就按新的指令操作录音服务。

notifyCallBack 在合适时候调用AIDL回调,通知App做相应的操作。

RecorderManager 实现

step 1

服务注册,这里按主App的包名来启动,所有App都是以这种方式启动

funinitialize(context: Context?, serviceConnectState: (( Boolean)-> Unit)? = null) {mApplicationContext = context?.applicationContextif(!isServiceConnected) {this.mServiceConnectState = serviceConnectStatevalserviceIntent = IntentserviceIntent.` package` = "com.julive.recorder"serviceIntent.action = "com.julive.audio.service"valisCanBind = mApplicationContext?.bindService(serviceIntent,mConnection,Context.BIND_AUTO_CREATE) ?: falseif(!isCanBind) {logE( "isCanBind: $isCanBind")this.mServiceConnectState?.invoke( false)bindSelfService}}}

isCanBind 是false的情况,就是未发现主App的情况,这个时候就需要启动自己的服务

privatefunbindSelfService{valserviceIntent = Intent(mApplicationContext, RecorderService:: class. java)valisSelfBind =mApplicationContext?.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)logE( "isSelfBind: $isSelfBind")}

step 2

连接成功后

privatevalmConnection: ServiceConnection = object: ServiceConnection {overridefunonServiceConnected(name: ComponentName, service: IBinder){mRecorderService = IRecorderService.Stub.asInterface(service)mRecorderService?.asBinder?.linkToDeath(deathRecipient, 0)isServiceConnected = truemServiceConnectState?.invoke( true)}overridefunonServiceDisconnected(name: ComponentName){isServiceConnected = falsemRecorderService = nulllogE( "onServiceDisconnected:name= $name")}}

接下来就可以用mRecorderService 来操作AIDL接口,最终调用RecorderService的实现

//启动funstartRecording(recorderConfig: RecorderConfig?){if(recorderConfig != null)mRecorderService?.startRecording(recorderConfig)}//暂停funstopRecording(recorderConfig: RecorderConfig?){if(recorderConfig != null)mRecorderService?.stopRecording(recorderConfig)}//是否录音中funisRecording(recorderConfig: RecorderConfig?): Boolean{returnmRecorderService?.isRecording(recorderConfig) ?: false}

这样一套完成的跨进程通信就完成了,代码注释很少,经过这个流程的代码展示,应该能明白整体的调用流程。如果有不明白的,欢迎留言区哦。

总结

通过这两天,对这个AIDL实现的录音服务,对跨进程的数据处理有了更加深刻的认知,这里面有几个比较难处理的就是录音的状态维护,还有就是优先级的维护,能把这两点整明白其实也很好处理。不扯了,有问题留言区交流。

欢迎交流:

git 源码:https://github.com/ibaozi-cn/Multi-Process-Audio-Recorder

SegmentFault 思否社区和文章作者展开更多互动和交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值