2024年最全Android音视频开发入门(4)MediaPlayerService,含面试题+答案

最后

题外话,我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料。

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

Android开发8年,阿里、百度一面惨被吊打!我是否应该转行了?

【Android进阶学习视频】、【全套Android面试秘籍】

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展

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

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

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

String16(“media.player”), new MediaPlayerService());

}

defaultServiceManager() 返回的是刚创建的BpServiceManager,并调用它的 addService()

BpMediaPlayerService作为服务端代理,那么BnMediaPlayerService一定是实现端

MediaPlayerService继承自 BnMediaPlayerService,实现了真正的的业务函数。

4. 添加服务的过程

============================================================================

下面分析一下 BpServiceManager的 addService()

virtual status_t addService(const String16& name, const sp& service,

bool allowIsolated, int dumpsysPriority) {

Parcel data, reply;

data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());

data.writeString16(name);

//也就是MediaPlayerService

data.writeStrongBinder(service);

data.writeInt32(allowIsolated ? 1 : 0);

data.writeInt32(dumpsysPriority);

status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

return err == NO_ERROR ? reply.readExceptionCode() : err;

}

这里的 remote函数返回的就是 前面创建的 BpBinder(0)对象:

status_t BpBinder::transact(

uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

// Once a binder has died, it will never come back to life.

if (mAlive) {

status_t status = IPCThreadState::self()->transact(

mHandle, code, data, reply, flags);

if (status == DEAD_OBJECT) mAlive = 0;

return status;

}

return DEAD_OBJECT;

}

status_t IPCThreadState::transact(int32_t handle,

uint32_t code, const Parcel& data,

Parcel* reply, uint32_t flags)

{

//发送addTransactionData

err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

} else {

//等待响应

err = waitForResponse(NULL, NULL);

}

return err;

}

status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,

int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)

{

//cmd BC_TRANSACTION 应用程序向Binder发送的命令

binder_transaction_data tr;

tr.target.handle = handle;

tr.code = code;

tr.flags = binderFlags;

//把命令和数据一起发送到 Parcel mOut中

mOut.writeInt32(cmd);

mOut.write(&tr, sizeof(tr));

return NO_ERROR;

}

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)

{

uint32_t cmd;

while (1) {

//和Driver进行沟通后获得结果

if ((err=talkWithDriver()) < NO_ERROR) break;

cmd = (uint32_t)mIn.readInt32();

switch (cmd) {

case BR_TRANSACTION_COMPLETE:

break;

}

}

return err;

}

status_t IPCThreadState::talkWithDriver(bool doReceive) {

binder_write_read bwr;

bwr.write_size = outAvail;

//写入 mOut的数据

bwr.write_buffer = (uintptr_t) mOut.data();

bwr.read_size = mIn.dataCapacity();

bwr.read_buffer = (uintptr_t) mIn.data();

//把mOut写到Binder中,并读取mIn的数据

if (ioctl(mProcess -> mDriverFD, BINDER_WRITE_READ, & bwr) >=0)

err = NO_ERROR;

return err;

}

joinThreadPool、startThreadPool 进入线程循环

talkWithDriver等待客户端Client请求,从Binder读取命令请求进行处理。

到现在为止,MediaPlayerService的服务端已经向总管 ServiceManager注册了

5. 通过BinderDriver和MediaPlayer通信的过程

====================================================================================================

下面看看客户端是如何获得服务的代理并和服务端通信的。

我们以 MediaPlayer的业务函数解码解析播放一个 网络视频的URL为例。

首先在 MediaPlayer decode(url)时,首先调用了 getMediaPlayerService()

我们来看看这个函数:

IMediaDeathNotifier::

getMediaPlayerService() {

//生成一个BpServiceManager代理对象

sp sm = defaultServiceManager();

sp binder;

do {

//获取String16(“media.player”)的服务对象

binder = sm -> getService(String16(“media.player”));

if (binder != 0) {

break;

}

usleep(500000); // 0.5 s

} while (true);

sMediaPlayerService = interface_cast < IMediaPlayerService > (binder);

return sMediaPlayerService;

}

Client端的MediaPlayer先获取一个 BpServiceManager的代理,然后调用其 getService()向服务总管 ServiceManager查询名叫 String16(“media.player”)的服务,这个getService的方法处于 /frameworks/native/libs/binder/ 的 IServiceManager.cpp:

class BpServiceManager :public BpInterface

{

public:

virtual sp getService(const String16 & name) const

{

int n = 0;

while (uptimeMillis() < timeout) {

n++;

//调用checkService函数

sp svc = checkService(name);

if (svc != NULL) return svc;

}

return NULL;

}

virtual sp checkService( const String16 & name) const

{

Parcel data, reply;

//首先调用writeInterfaceToken,然后写入 android.os.IServiceManager

data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor ());

//写入media.player

data.writeString16(name);

//然后通过Binder把data及reply的数据通知出去,在onTransact函数中进行回调

remote()->transact(CHECK_SERVICE_TRANSACTION, data, & reply);

return reply.readStrongBinder();

}

}

这里首先将请求打包成Parcel格式,然后调用 remote->transact函数,前面我们分析过 BpServiceManager::remote返回的就是 newBpBinder(0),对应的句柄为 ServiceManager。继续在BpBinder中寻找实现代码:

status_t BpBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags){

IPCThreadState::self()->transact(mHandle, code, data, reply, flags);

}

最后调用 IPCThreadState的transact函数,在Android中 ProcessState是哭护短和服务端的公共部分,作为Binder通信的基础,ProcessState是一个singleton类, 每个进程只有一个对象,这个对象负责打开Binder驱动,建立线程池,让其进程里面的所有线程都能通过Binder通信。

与之相关的就是 IPCThreadState,每个线程都有一个IPCThreadState实例登记在Linux线程的上下文附属数据中,主要负责Binder的读取、写入和请求处理框架。

IPCThreadState在构造的时候获取进程的ProcessState并记录在自己的成员变量中:

status_t IPCThreadState::transact(int32_t handle,uint32_t code, const Parcel&data,Parcel*reply, uint32_t flags) {

status_t err;

//填充 binder_transaction_data 结构体,写入mOut中

//这里就是把 “media.player”、"android.os.IServiceManager"等写进去

err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL);

//然后等待返回

waitForResponse(reply);

return err;

}

status_t IPCThreadState:: waitForResponse(Parcel *reply, status_t *acquireResult) {

while (1) {

talkWithDriver())

//输入mOut,输出mIn

cmd = mIn.errorCheck();

switch (cmd) {

case BR_REPLY: {

binder_transaction_data tr;

if ((tr.flags & TF_STATUS_CODE) == 0) {

reply -> ipcSetDataReference(

reinterpret_cast <const uint8_t * > (tr.data.ptr.buffer),

tr.data_size,

reinterpret_cast <const

binder_size_t * > (tr.data.ptr.offsets),

tr.offsets_size / sizeof(binder_size_t),

freeBuffer, this);

} else {

err = *reinterpret_cast<const status_t * > (tr.data.ptr.buffer);

freeBuffer(NULL,

reinterpret_cast <const uint8_t * > (tr.data.ptr.buffer),

tr.data_size,

reinterpret_cast <const

binder_size_t * > (tr.data.ptr.offsets),

tr.offsets_size / sizeof(binder_size_t), this);

}

}

}

}

}

waitForResponse返回的是一个 readStrongBinder(),进入Parcel的readStrongBinder()中:

status_t unflatten_binder(const sp& proc,

const Parcel& in, wp* out) {

const flat_binder_object * flat = in.readObject(false);

if (flat) {

switch (flat->hdr.type) {

case BINDER_TYPE_BINDER:

out = reinterpret_cast<IBinder>(flat->cookie);

return finish_unflatten_binder(NULL, *flat, in);

case BINDER_TYPE_HANDLE:

*out = proc->getStrongProxyForHandle(flat->handle);

return finish_unflatten_binder(

static_cast<BpBinder*>(out->get()), *flat, in);

}

}

}

这里的flat->type是BINDER_TYPE_HANDLE,所以调用 ProcessState::getStrongProxyForHandle():

sp ProcessState::getStrongProxyForHandle(int32_t handle)

{

sp result;

handle_entry* e = lookupHandleLocked(handle);

if (e != NULL) {

IBinder* b = e->binder;

if (b == NULL || !e->refs->attemptIncWeak(this)) {

b = BpBinder::create(handle);

e->binder = b;

if (b) e->refs = b->getWeakRefs();

result = b;

} else {

result.force_set(b);

e->refs->decWeak(this);

}

}

return result;

}

这里的handle就是ServiceManager内维护的MediaPlayerService对应的Binder句柄。

这个ProcessState根据这个句柄创建了一个 BpBinder,并将其保存了起来。这样下次需要的时候就从ServiceManager请求获取相同的句柄就可以直接返回了。

根据这个返回的BpBinder获得MediaPlayerService的代理:

sMediaPlayerService = interface_cast(binder);

和前面的ServiceManager一样,调用IMediaPlayerService的asInterface宏函数,就可以获得一个代理BpMediaPlayerService对象,它的remote为BpBinder(handle),这个handle就是向服务总管ServiceManager查询到的MediaPlayerService对应的Binder句柄。

最后总结一下:

  • 在实际业务中,如当 MediaPlayer::setDataSource 返回时,会创建一个与 MediaPlayerService::Client对应的BpMediaPlayer,用于获取 MediaPlayerService::Client的各项功能

  • MediaPlayer又是如何找到 MediaPlayerService::Client的呢?

只有MediaPlayerService向ServiceManager进行了注册才可以,所以MediaPlayer必须先获取 BpMediaPlayerService,然后通过BpMediaService的管理功能创建一个MediaPlayerService::Client

  • 为什么不直接定义一个MediaPlayer,让其向 ServiceManager注册呢?

MediaPlayerService包含的功能不仅是 Client,还有 AudioOutput、AudioCache、MediaConfigClient等功能。

MediaPlayerService就是一个 媒体服务的窗口

6. 创建播放器

==========================================================================

在MediaPlayer的构造函数中,会预先注册一些播放器的Factory。很多ROM厂商如果要应用自己写的播放器,也需要有一个Factory,这里也是一样的:

MediaPlayerService::MediaPlayerService()

{

ALOGV(“MediaPlayerService created”);

mNextConnId = 1;

//注册一些播放器的Factory

MediaPlayerFactory::registerBuiltinFactories();

}

void MediaPlayerFactory::registerBuiltinFactories() {

Mutex::Autolock lock_(&sLock);

IFactory* factory = new NuPlayerFactory();

//注册两个 Factory

if (registerFactory_l(factory, NU_PLAYER) != OK)

delete factory;

factory = new TestPlayerFactory();

if (registerFactory_l(factory, TEST_PLAYER) != OK)

delete factory;

sInitComplete = true;

}

尾声

在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:


展示学习笔记

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

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

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

tory_l(factory, NU_PLAYER) != OK)

delete factory;

factory = new TestPlayerFactory();

if (registerFactory_l(factory, TEST_PLAYER) != OK)

delete factory;

sInitComplete = true;

}

尾声

在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:

[外链图片转存中…(img-lOA90mCQ-1715885901529)]
展示学习笔记
[外链图片转存中…(img-6VUKMOJp-1715885901529)]
[外链图片转存中…(img-AjI3EGLV-1715885901530)]

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值