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

本文详细介绍了如何在Android应用中实现一个跨进程的RecorderService,涉及接口定义、权限检查、AIDL通信以及状态管理,重点展示了如何处理多线程同步和优先级控制以确保服务的正确运作。
摘要由CSDN通过智能技术生成

override fun isRecording(): Boolean {

return mState == RecorderState.RECORDING

}

@Synchronized

override fun stopRecording() {

try {

if (mState == RecorderState.RECORDING) {

mMediaRecorder?.stop()

mMediaRecorder?.reset()

mMediaRecorder?.release()

}

} catch (e: java.lang.IllegalStateException) {

e.printStackTrace()

}

mMediaRecorder = null

mState = RecorderState.IDLE

}

override fun state(): RecorderState {

return mState

}

}

这里需要注意的就是加 @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

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-bARZTdU6-1714197238083)]

【算法合集】

[外链图片转存中…(img-F9lpVLgy-1714197238084)]

【延伸Android必备知识点】

[外链图片转存中…(img-DwWU6DnE-1714197238084)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值