Android Car音乐播放器分析

                       Android Car音乐播放器分析

 

概述

    Android Car系统音乐路径是packages/apps/Car/Media,应用名称是CarMediaApp,包名是com.android.car.media。系统音乐的数据来自同目录的LocalMediaPlayer,应用名称是LocalMediaPlayer,包名是com.android.car.media.localmediaplayer。这两个应用共用了sharedUserId,这样可以互相访问数据了。注意共用sharedUserId,必需签名也要相同,否则应用无法安装。

 

 

MediaSession框架

MediaBrowser 
    媒体浏览器,用来连接MediaBrowserService订阅数据,通过它的回调接口我们可以获取和Service的连接状态以及获取在Service中异步获取的音乐库数据。媒体浏览器一般创建于客户端

MediaBrowserService 

    浏览器服务,提供onGetRoot(控制客户端媒体浏览器的连接请求,通过返回值决定是否允许该客户端连接服务)和onLoadChildren(媒体浏览器向Service发送数据订阅时调用,一般在这执行异步获取数据的操作,最后将数据发送至媒体浏览器的回调接口中)这两个抽象方法 。客户端通过MediaBrowser.subscribe()方法发起数据请求,每次subscribe都会调用到onLoadChildren。

MediaSession

    媒体会话,即受控端,通过设置MediaSessionCompat.Callback回调来接收媒体控制器MediaController发送的指令,当收到指令时会触发Callback中各个指令对应的回调方法(回调方法中会执行播放器相应的操作,如播放、暂停等)。Session一般在Service.onCreate方法中创建,最后需调用setSessionToken方法设置用于和控制器配对的令牌并通知浏览器连接服务成功。一个seesion可以接收来自一个或多个媒体播放器的callback。这使得通过其它设备来控制成为可能

Media controller

    我们的UI只是和Media controller交互,而不是Player 本身,Media controller会将一些控制信息传递给Media Session,它也会在seesion发生变化的时候,得到来自session的回调,一个media controller一次只可以连接一个session。当使用一个media contoller和Session的时候,我们可以在运行期部署多个播放器,在其执行的时候根据设备去修改app的外观。

 

 

本地音乐播放器

LocalMediaPlayer服务端

    LocalMediaPlayer主要功能是从数据库获取音乐数据及一个后台播放器Player。获取音乐是在LocalMediaBrowserService中实现。LocalMediaBrowserService又继承MediaBrowserService,这个目录下还有IMediaBrowserService.aidl,IMediaBrowserServiceCallbacks.aidl两个文件。IMediaBrowserService.aidl中定义了getMediaItem方法,这个方法会在MediaBrowserService中重写,作为binder服务端实现,在MediaBrowser中调用 。MediaBrowserService作为一个播放后台服务,是通过MediaSession实现的,在使用过程中MediaSession会与该服务及音乐客户端关联,所有的播放操作都是由MediaSession实现的。

 

 

Media客户端

MediaActivity

    音乐客户端的主Activity是MediaActivity,这个类继承自CarDrawerActivity,我们只看MediaActivity。在其onCreate()方法中创建了MediaDrawerController。MediaDrawerController实现了MediaItemOnClickListener接口的onMediaItemClicked(MediaBrowser.MediaItem mediaItem)方法,我们在播放列表点击某个列表就会走到这里来,参数是MediaBrowser.MediaItem,根据它可以获取到音乐的标题、ID等信息,但是获取IconUri为null,获取音乐图片的信息我们稍后再讲。在这里,先调用MediaController的pause方法,暂停上个音乐的播放,然后调用MediaController的playFromMediaId方法,播放当前点击的音乐。MediaController内部是通过binder和服务端通讯的,最终还是通过MediaSession控制音乐的状态。

MediaPlaybackFragment

    在MediaActivity中会创建MediaPlaybackFragment,这个Fragment作为主界面显示。在MediaPlaybackFragment的onCreate()方法中会创建MediaPlaybackModel类。然后调用其onStart()方法。

MediaManager

    音乐客户端封装了MediaManager类,客户端和服务端的连接和状态控制都是在该类中处理。MediaManager类中的一些回调。MediaBrowser.ConnectionCallback。客户端向服务端发起连接的回调。包括连接成功onConnected(),连接挂起onConnectionSuspended(),连接失败onConnectionFailed()方法。当收到连接成功回调后,会通过MediaSession生成MediaController,然后MediaController注册回调mController.registerCallback(mMediaControllerCallback)。MediaController的回调方法主要包括状态改变回调onPlaybackStateChanged

(),会话销毁回调onSessionDestroyed()。音乐客户端的所有状态控制都在onPlaybackStateChanged方法中处理。

 

 

获取音乐数据的流程

服务连接

    起始是由媒体浏览客户端来发起媒体数据的获取请求,即MediaBrowser客户端。MediaBrowser首先要连接上媒体服务。

    接着分析MediaActivity,在其onCreate()方法中,还会调用MediaManager的addListener()方法:MediaManager.getInstance(this).addListener(mListener),整个流程由此启动。在MediaActivity中还会调用到MediaManager的setMediaClientComponent方法,潘多拉的魔盒由此打开。在setMediaClientComponent方法中创建和服务端的连接mBrowser = new MediaBrowser(mContext, component, mMediaBrowserConnectionCallback, null);其中mMediaBrowserConnectionCallback就是上文提到的MediaBrowser.ConnectionCallback接口。至此音乐客户端已经和服务端连接上了,接下来就是如何获取音乐数据了。

获取数据

    接下来分析入口在哪里。从界面可以看到,主界面显示几个文件夹,比如Folders,Albums,Artists,Genres...这些数据是从LocalMediaBrowserService类中创建的,在其onLoadChildren回调中,当传入的parentId是ROOT_ID时,就返回这些文件夹列表。从这里我们知道,音乐数据并不是一下子全部返回的,而是根据parentId,返回其下相应的数据。无论是文件夹还是音乐数据,都是通过MediaItem集合的方式返回。通过前面我们知道每当客户端调用subscribe方法的时候,服务端会回调onLoadChildren方法。那第一个subscribe方法是在什么地方调用的呢?这里直接给出答案。

流程顺序图

MediaPlaybackFragment.onResume()->MediaPlaybackModel.start()->MediaManager.addListener()添加了onMediaAppChanged监听;MediaActivity.onResumeFragments()->MediaManager.setMediaClientComponent()->调用所有的onMediaAppChanged回调;

MediaPlaybackModel.mMediaManagerListener.onMediaAppChanged()->new MediaBrowser(mContext, name, mConnectionCallback, mBrowserExtras).connect()->MediaPlaybackModel.mConnectionCallback.onConnected()->notifyListeners(onMediaConnected)->MediaDrawerController.mModelListener.onMediaConnected)()->MediaDrawerAdapter.setFetcher(createRootMediaItemsFetcher());

MediaDrawerController.createRootMediaItemsFetcher(getMediaBrowser().getRoot())[在这里调用了MediaBrowser的getRoot方法,就是上面提到的LocalMediaBrowserService的ROOT_ID字段]->MediaDrawerController.createMediaBrowserItemFetcher();

继续看MediaDrawerAdapter.setFetcher()方法

->MediaItemsFetcher.start()->getMediaBrowser().subscribe(mMediaId),这里的mMediaId就是ROOT_ID。到此客户端调用subscribe方法获取数据,服务端回调onLoadChildren方法返回数据的流程就清楚了。

 

 

 

 

 

 

    

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值