这一次主要是研究Jamendo的播放流程,相对比较繁复一些。播放显然要启动Service来实现,在这之前,Jamendo是怎么处理的。本文将简单展开。
(一) 基本流程图
(二) 实现
1. 启动
在Jamendo中,开发者习惯用被启动Activity的静态方法来完成这个功能。Gallery的Item点击事件之后,代码如下:
- Album album = (Album) adapterView.getItemAtPosition(position);
- PlayerActivity.launch(HomeActivity.this, album);
2. 数据加载封装
这一部分的处理思路在笔记之七中已经详细的说明。只在此列出代码,如下:
- public class PlayerAlbumLoadingDialog extends LoadingDialog
{ - private Album mAlbum;
- public PlayerAlbumLoadingDialog(Activity activity, int loadingMsg, int failMsg) {
- super(activity, loadingMsg, failMsg);
- }
- @Override
- public Track[] doInBackground(Album... params) {
- mAlbum = params[0];
- JamendoGet2Api service = new JamendoGet2ApiImpl();
- Track[] tracks = null;
- try {
- tracks = service.getAlbumTracks(mAlbum, JamendoApplication.getInstance().getStreamEncoding());
- Log.d("play_workflow", "doInBackground tracks is:" + tracks.toString());
- } catch (JSONException e) {
- e.printStackTrace();
- return null;
- } catch (WSError e) {
- publishProgress(e);
- cancel(true);
- }
- return tracks;
- }
- @Override
- public void doStuffWithResult(Track[] tracks) {
- Intent intent = new Intent(mActivity, PlayerActivity.class);
- Playlist playlist = new Playlist();
- mAlbum.setTracks(tracks);
- playlist.addTracks(mAlbum);
- intent.putExtra("playlist", playlist);
- Log.d("play_workflow", "doStuffWithResult playlist is:" + playlist.toString());
- mActivity.startActivity(intent);
- }
- }
封装的示意图如下:
3. 播放列表加载及准备
数据封装完毕后,开始进行各种播放器的准备。我猜因为是开源软件的原因,部分代码写的不是很完善。比如if的某个条件,打了log之后,始终并未执行。我在代码中已经做了注释。handleIntent()方法里边处理已经封装好的PlayList类。代码如下:
- private void handleIntent(){
- Log.i(JamendoApplication.TAG, "PlayerActivity.handleIntent");
- // This will be result of this intent handling
- Playlist playlist = null;
- Log.d("play_workflow", "data is:" + getIntent().getData());//本身并未给data赋值,所以执行的都是data==null的情形
- // We need to handle Uri
- if(getIntent().getData() != null){//不执行此条判断
- // Check if this intent was already once parsed
- // we don't need to do that again
- if(!getIntent().getBooleanExtra("handled", false)){
- Log.d("play_workflow", "handled is true" );
- mUriLoadingDialog = (LoadingDialog) new UriLoadingDialog(this, R.string.loading, R.string.loading_fail).execute();
- }
- } else {
- Log.d("play_workflow", "handled is false" );
- playlist = (Playlist) getIntent().getSerializableExtra("playlist");
- Log.d("play_workflow", "handle_intent playlist is:" + playlist);
- loadPlaylist(playlist);
- }
- }
其中loadPlaylist()方法的代码如下:
- private void loadPlaylist(Playlist playlist){
- Log.i(JamendoApplication.TAG, "PlayerActivity.loadPlaylist");
- if(playlist == null)
- return;
- mPlaylist = playlist;
- if(mPlaylist != getPlayerEngine().getPlaylist()){//播放列表不相等,就要重新加载
- //getPlayerEngine().stop();
- getPlayerEngine().openPlaylist(mPlaylist);
- getPlayerEngine().play();
- }
- }
其实,jamendo的处理是使用了两步来实现的,示意图如下:
按照先后顺序,以openPlayList()为例,代码如下:
Step1:在handleIntent中执行
代码如上所示
Step2:在IntentPlayEngine中
- @Override
- public void openPlaylist(Playlist playlist) {
- mPlaylist = playlist;
- if(mServicePlayerEngine != null){
- mServicePlayerEngine.openPlaylist(playlist);
- }
- }
Step3:在PlayEngineImpl中
- @Override
- public void openPlaylist(Playlist playlist) {
- if(!playlist.isEmpty())
- mPlaylist = playlist;
- else
- mPlaylist = null;
- }
4. 启动服务播放
从3中, play方法最终调用到的也是PlayEngine中的。
这样,整个流程就ok了。但是整个流程下来,给人的感觉是,冗繁。
(三) 播放的控制
对于播放控制的处理是(以play键为例):
1, 监听按键
代码如下:
- /**
- * on click play/pause and open playlist if necessary
- */
- private OnClickListener mPlayOnClickListener = new OnClickListener(){
- @Override
- public void onClick(View v) {
- if(getPlayerEngine().isPlaying()){
- getPlayerEngine().pause();
- } else {
- getPlayerEngine().play();
- }
- }
- };
2, 调用接口,启动intent
代码如下:
- @Override
- public void play() {
- if (mServicePlayerEngine != null) {
- Log.d("play_workflow", "mServicePlayerEngine is not null:" );
- playlistCheck();
- mServicePlayerEngine.play();
- } else {
- Log.d("play_workflow", "mServicePlayerEngine is null");
- startAction(PlayerService.ACTION_PLAY);
- }
- }
3, 执行Service指定操作
代码如下:
- if(action.equals(ACTION_PLAY)){
- mPlayerEngine.play();
- return;
- }
最终执行的代码如下:
- @Override
- public void play() {
- if( mPlayerEngineListener.onTrackStart() == false ){
- return; // apparently sth prevents us from playing tracks
- }
- // check if there is anything to play
- if(mPlaylist != null){
- // check if media player is initialized
- if(mCurrentMediaPlayer == null){
- mCurrentMediaPlayer = build(mPlaylist.getSelectedTrack());
- }
- // check if current media player is set to our song
- if(mCurrentMediaPlayer != null && mCurrentMediaPlayer.playlistEntry != mPlaylist.getSelectedTrack()){
- cleanUp(); // this will do the cleanup job
- mCurrentMediaPlayer = build(mPlaylist.getSelectedTrack());
- }
- // check if there is any player instance, if not, abort further execution
- if(mCurrentMediaPlayer == null)
- return;
- // check if current media player is not still buffering
- if(!mCurrentMediaPlayer.preparing){
- // prevent double-press
- if(!mCurrentMediaPlayer.isPlaying()){
- // i guess this mean we can play the song
- Log.i(JamendoApplication.TAG, "Player [playing] "+mCurrentMediaPlayer.playlistEntry.getTrack().getName());
- // starting timer
- mHandler.removeCallbacks(mUpdateTimeTask);
- mHandler.postDelayed(mUpdateTimeTask, 1000);
- mCurrentMediaPlayer.start();
- }
- } else {
- // tell the mediaplayer to play the song as soon as it ends preparing
- mCurrentMediaPlayer.playAfterPrepare = true;
- }
- }
- }
(四) 思考
在播放准备及播放的流程上,有些冗余,代码不够清晰。我之前做过一个音乐播放器,采用的是bind service,回调让控制比较方便。Jamendo对于IntentPlayerEngine和PlayEngineImpl做出了相应的解释。未绑定的原因是适应代码的原始版本,减少重构量。
l IntentPlayerEngine
- /**
- * Since 0.9.8.7 we embrace "bindless" PlayerService thus this adapter. No
- * big need of code refactoring, we just wrap sending intents around defined
- * interface
- *
- * 意思是:因为jamendo使用的是无绑定的PlayService,所以有着这个适配器。不需要对代码
- * 进行太多的重构。我们只需要通过这个指定的接口发送Intent就ok了。
- * @author Lukasz Wisniewski
- */
- private class IntentPlayerEngine implements PlayerEngine
l PlayEngineImpl
- /**
- * Player core engine allowing playback, in other words, a
- * wrapper around Android's
MediaPlayer
, supporting- *
Playlist
classes- *
- * @author Lukasz Wisniewski
- */
- public class PlayerEngineImpl implements PlayerEngine