最近開始學習音頻相關。所以,很想自己做一個音樂播放器,於是,花了一天學習,將播放器的基本功能實現了出來。我覺得學習知識點還是蠻多的,所以寫篇博客總結一下關於一個音樂播放器實現的邏輯。希望這篇博文對你的學習和生活有所幫助。效果圖:
**實現邏輯**
在市面上的音樂播放app,即時你關了。那么一樣會在后台播放,所以播放的邏輯應該寫在Service中。並且能夠實現Service和Activity之間進行通信。那么Service是四大組件之一,所以在使用的時候一定不要忘了在配置文件中聲明一下。
<service android:name="com.yakir.services.MusicService"> </service>
我們需要重寫Service中的三個方法,onCreate(),onStartCommand(),onDestroy()。
onCreate():Service第一次啟動的時候調用這個方法,可以做一些變量的初始化。
onStartCommand():Service每一次啟動的時候調用這個方法,可以在此方法寫一些業務邏輯。
onDestroy():Service銷毀的時候調用,用於釋放資源。
接下來,如果需要播放一個音樂文件,可以使用安卓自帶的播放器MediaPlayer,將播放邏輯封裝起來:
//開始 public void start(String path) { try { mediaPlayer.reset(); mediaPlayer.setDataSource(path); mediaPlayer.prepare(); mediaPlayer.start(); MediaUtils.currentState= Constants.PLAY_START; } catch (IOException e) { e.printStackTrace(); } } //暫停 public void pause (){ if (mediaPlayer!=null&&mediaPlayer.isPlaying()) { mediaPlayer.pause(); MediaUtils.currentState=Constants.PLAY_PAUSE; } } //繼續播放 public void continuePlay() { if (mediaPlayer!=null&&!mediaPlayer.isPlaying()) { mediaPlayer.start(); MediaUtils.currentState= Constants.PLAY_START; } } //停止播放 public void stop() { if (mediaPlayer!=null) { mediaPlayer.stop(); MediaUtils.currentState=Constants.PLAY_STOP; } }
我們知道,UI是寫在Activity中的,那么就涉及到Activity與Service之間進行通信,他們之間通信的方式有5種,這里我是用了Intent,調用startService()進行通信,
同時讓Intent攜帶一組鍵值對數據,與Service端進行匹配。
Activity:
private void startMusicService(String option,String path) { Intent intentService = new Intent(MainActivity.this, MusicService.class); intentService.putExtra("option", option); intentService.putExtra("messenger",new Messenger(handler)); intentService.putExtra("path", path); startService(intentService); } private void startMusicService(String option) { Intent intentService = new Intent(MainActivity.this, MusicService.class); intentService.putExtra("option", option); intentService.putExtra("messenger",new Messenger(handler)); startService(intentService); } private void startMusicService(String option,int progress) { Intent intentService = new Intent(MainActivity.this, MusicService.class); intentService.putExtra("option", option); intentService.putExtra("progress",progress); intentService.putExtra("messenger",new Messenger(handler)); startService(intentService); }
Service:
@Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e("thread",Thread.currentThread().getName() ); String option=intent.getStringExtra("option"); if (messenger==null) { messenger = (Messenger) intent.getExtras().get("messenger"); } if ("開始".equals(option)) { start(intent.getStringExtra("path")); } else if ("暫停".equals(option)) { pause(); } else if ("繼續".equals(option)) { continuePlay(); } else if ("停止".equals(option)) { stop(); } else if ("跳轉".equals(option)) { seekPlay(intent.getIntExtra("progress",-1)); } return super.onStartCommand(intent, flags, startId); }
這樣,就實現了兩者之間的通信,接下來,當我點擊下一首的時候,那么下一個item的文字高亮,並且播放下一首歌。
先從系統中拿到所有的音頻數據:
public static void getSongList (Context context) { musicBeanList.clear(); Uri uri= MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Cursor cursor=context.getContentResolver().query(uri, new String[]{ MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DATA},null,null,null); while (cursor.moveToNext()) { musicBeanList.add(new MusicBean( cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)), cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)), cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)))); } }
通過內容提供者,拿到系統的所有音頻數據,以及音頻的相關信息。DATA就是音頻路徑,拿到音頻路徑后就可以根據現實位置進行播放切換了。(這里很簡單,不細說)
處理文字高亮:
我們需要知道當前的item位置和下一個item位置,當點擊下一首歌曲,下個位置高亮,其他位置不亮。所以,需要我們定義一個常量記錄當前位置,並且,當點擊下一首,常量增加,上一首,常量減少。
case R.id.ib_bottom_last: setColor(Color.BLUE); MediaUtils.currentPosition--; setColor(Color.RED); startMusicService("開始",MediaUtils.musicBeanList.get(MediaUtils.currentPosition).path); imgBottomPlay.setImageResource(R.drawable.appwidget_pause); break; case R.id.ib_bottom_next: setColor(Color.BLUE); MediaUtils.currentPosition++; setColor(Color.RED); startMusicService("開始",MediaUtils.musicBeanList.get(MediaUtils.currentPosition).path); imgBottomPlay.setImageResource(R.drawable.appwidget_pause); break;
需要在改變顏色時做一下邏輯處理:
當到最后一個item時點擊下一個高亮變為第一個,當位於第一個時,點擊上一首,高亮位於最后一個位置。
private void setColor(int color) { if (MediaUtils.currentPosition==MediaUtils.musicBeanList.size()) { MediaUtils.currentPosition=0; } if (MediaUtils.currentPosition==-1) { MediaUtils.currentPosition=MediaUtils.musicBeanList.size()-1; } TextView textView= (TextView) songList.findViewWithTag(MediaUtils.currentPosition); if (textView!=null) { textView.setTextColor(color); } }
接下來,需要實現一個更隨音樂播放進度的進度條,這就需要實現Service向Activity的通信,可以通過消息機制,讓Service發送消息給Activity,將播放信息傳遞給Activity.
拿着這個Intent傳一個信使(Messager).
intentService.putExtra("messenger",new Messenger(handler));
從而Service:
Message message=Message.obtain(); message.arg1=currentPostion; message.arg2=duration; message.what=Constants.MUSIC_PREPARE; messenger.send(message);
Activity再處理:
private Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case Constants.MUSIC_PREPARE: int curduration=msg.arg1; int totalduration=msg.arg2; tv_curduration.setText(MediaUtils.duration2Str(curduration)); tv_totalduration.setText(MediaUtils.duration2Str(totalduration)); sk_duration.setMax(totalduration); sk_duration.setProgress(curduration); break; } } };
在SeekBar中設置監聽,當拖動停止時把當前進度傳給Service,讓音樂就當前位置播放:
sk_duration.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { sk_duration.setProgress(seekBar.getProgress()); startMusicService("跳轉",seekBar.getProgress()); } });
同時,我們需要注意到一個情況,當啟動電話時,音樂應該是停止狀態,可以通過AudioManager獲得音頻焦點,當焦點失去時,音樂也應該停止:
AudioManager audioManager= (AudioManager) getSystemService(Context.AUDIO_SERVICE); audioManager.requestAudioFocus(new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: mediaPlayer.start(); mediaPlayer.setVolume(1.f,2.0f); break; case AudioManager.AUDIOFOCUS_LOSS: if (mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer=null; break; case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: if (mediaPlayer.isPlaying()); mediaPlayer.pause(); break; } } },AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN);
好了,這樣一個簡單的音樂播放器就完成了,雖然說代碼不是很困難,但是還是有很多知識點,比如Activity中Service中的通信,以及ListView相關,還有程序的邏輯性,所以我把幾個重要的點總結了一下。希望這篇博文對你的生活和學習有所幫助,如果有什么疑問可以在下方留言,如果你想要源碼,可以私聊我~