Jamendo学习笔记之:播放流程

转自:http://mikewang.blog.51cto.com/3826268/890180

这一次主要是研究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<Album, Track[]>{ 
     
    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); 
    } 
 
} 

封装的示意图如下:

Jamendo学习笔记之:播放流程_第1张图片

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的处理是使用了两步来实现的,示意图如下:

Jamendo学习笔记之:播放流程_第2张图片

按照先后顺序,以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 <code>MediaPlayer</code>, supporting 
 * <code>Playlist</code> classes 
 *  
 * @author Lukasz Wisniewski 
 */ 
public class PlayerEngineImpl implements PlayerEngine  





你可能感兴趣的:(Jamendo学习笔记之:播放流程)