Android一次完美的跨进程服务共享实践

}

}

这里需要注意的就是加 @Synchronized因为多进程同时调用的时候会出现状态错乱问题,需要加上才安全。

AIDL 接口定义

interface IRecorderService {

void startRecording(in RecorderConfig recorderConfig);

void stopRecording(in RecorderConfig recorderConfig);

boolean isRecording(in RecorderConfig recorderConfig);

RecorderResult getActiveRecording();

void registerCallback(IRecorderCallBack callBack);

void unregisterCallback(IRecorderCallBack callBack);

}

注意点:

  • 自定义参数需要实现Parcelable接口

  • 需要回调的话也是AIDL接口定义

AIDL 接口回调定义

interface IRecorderCallBack {

void onStart(in RecorderResult result);

void onStop(in RecorderResult result);

void onException(String error,in RecorderResult result);

}

RecorderService 实现

接下来就是功能的核心,跨进程的服务

class RecorderService : Service() {

private var iRecorder: IRecorder? = null

private var currentRecorderResult: RecorderResult = RecorderResult()

private var currentWeight: Int = -1

private val remoteCallbackList: RemoteCallbackList = RemoteCallbackList()

private val mBinder: IRecorderService.Stub = object : IRecorderService.Stub() {

override fun startRecording(recorderConfig: RecorderConfig) {

startRecordingInternal(recorderConfig)

}

override fun stopRecording(recorderConfig: RecorderConfig) {

if (recorderConfig.recorderId == currentRecorderResult.recorderId)

stopRecordingInternal()

else {

notifyCallBack {

it.onException(

“Cannot stop the current recording because the recorderId is not the same as the current recording”,

currentRecorderResult

)

}

}

}

override fun getActiveRecording(): RecorderResult? {

return currentRecorderResult

}

override fun isRecording(recorderConfig: RecorderConfig?): Boolean {

return if (recorderConfig?.recorderId == currentRecorderResult.recorderId)

iRecorder?.isRecording ?: false

else false

}

override fun registerCallback(callBack: IRecorderCallBack) {

remoteCallbackList.register(callBack)

}

override fun unregisterCallback(callBack: IRecorderCallBack) {

remoteCallbackList.unregister(callBack)

}

}

override fun onBind(intent: Intent?): IBinder? {

return mBinder

}

@Synchronized

private fun startRecordingInternal(recorderConfig: RecorderConfig) {

val willStartRecorderResult =

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()) {

val weight = 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)

}

}

private fun startRecorder(

recorderConfig: RecorderConfig,

willStartRecorderResult: RecorderResult

) {

logD(“startRecording result ${willStartRecorderResult.toString()}”)

iRecorder = when (recorderConfig.recorderOutFormat) {

RecorderOutFormat.MPEG_4, RecorderOutFormat.AMR_WB -> {

JLMediaRecorder()

}

RecorderOutFormat.PCM -> {

JLAudioRecorder()

}

}

val result = iRecorder?.startRecording(recorderConfig)

if (!result.isNullOrEmpty()) {

logD(“startRecording result $result”)

notifyCallBack {

it.onException(result, willStartRecorderResult)

}

} else {

currentWeight = recorderConfig.weight

notifyCallBack {

it.onStart(willStartRecorderResult)

}

currentRecorderResult = willStartRecorderResult

}

}

private fun isRecording(): Boolean {

return iRecorder?.isRecording ?: false

}

@Synchronized

private fun stopRecordingInternal() {

logD(“stopRecordingInternal”)

iRecorder?.stopRecording()

currentWeight = -1

iRecorder = null

MediaScannerConnection.scanFile(

this,

arrayOf(currentRecorderResult.recorderFile?.absolutePath),

null,

null

)

notifyCallBack {

it.onStop(currentRecorderResult)

}

}

private fun notifyCallBack(done: (IRecorderCallBack) -> Unit) {

val size = remoteCallbackList.beginBroadcast()

logD(“recorded notifyCallBack size $size”)

(0 until size).forEach {

done(remoteCallbackList.getBroadcastItem(it))

}

remoteCallbackList.finishBroadcast()

}

}

这里需要注意的几点:

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

RecorderManager 实现

step 1

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

fun initialize(context: Context?, serviceConnectState: ((Boolean) -> Unit)? = null) {

mApplicationContext = context?.applicationContext

if (!isServiceConnected) {

this.mServiceConnectState = serviceConnectState

val serviceIntent = Intent()

serviceIntent.package = “com.julive.recorder”

serviceIntent.action = “com.julive.audio.service”

val isCanBind = mApplicationContext?.bindService(

serviceIntent,

mConnection,

Context.BIND_AUTO_CREATE

) ?: false

if (!isCanBind) {

logE(“isCanBind:$isCanBind”)

this.mServiceConnectState?.invoke(false)

bindSelfService()

}

}

}

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

private fun bindSelfService() {

val serviceIntent = Intent(mApplicationContext, RecorderService::class.java)

val isSelfBind =

mApplicationContext?.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)

logE(“isSelfBind:$isSelfBind”)

}

  • step 2

连接成功后

private val mConnection: ServiceConnection = object : ServiceConnection {

override fun onServiceConnected(name: ComponentName, service: IBinder) {

mRecorderService = IRecorderService.Stub.asInterface(service)

mRecorderService?.asBinder()?.linkToDeath(deathRecipient, 0)

isServiceConnected = true

mServiceConnectState?.invoke(true)

}

override fun onServiceDisconnected(name: ComponentName) {

isServiceConnected = false

mRecorderService = null

logE(“onServiceDisconnected:name=$name”)

}

}

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

//启动

fun startRecording(recorderConfig: RecorderConfig?) {

if (recorderConfig != null)

mRecorderService?.startRecording(recorderConfig)

}

//暂停

fun stopRecording(recorderConfig: RecorderConfig?) {

if (recorderConfig != null)

mRecorderService?.stopRecording(recorderConfig)

}

//是否录音中

fun isRecording(recorderConfig: RecorderConfig?): Boolean {

return mRecorderService?.isRecording(recorderConfig) ?: false

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值