日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android开源音乐播放器之播放器基本功能

發布時間:2025/3/20 Android 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android开源音乐播放器之播放器基本功能 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

系列文章

  • Android開源在線音樂播放器——波尼音樂
  • Android開源音樂播放器之播放器基本功能
  • Android開源音樂播放器之高仿云音樂黑膠唱片
  • Android開源音樂播放器之自動滾動歌詞
  • Android開源音樂播放器之在線音樂列表自動加載更多

前言

音樂播放器是我們最常用的應用之一,也是每部手機都會預裝的應用。作為一個合格的音樂播放器,應該具有哪些功能呢?“無非是播放、暫停、切換歌曲、進度調節、切換播放模式、專輯封面顯示、歌詞顯示、歌曲列表、歌曲管理(由于國產手機大多都是修改過的Android系統,因此系統自帶播放器功能也不一樣,這里以Android原生播放器為參考)這些功能” 一開始我也是這么認為的,但當我著手做的時候,才發現這些功能遠遠不夠。如手機來電時,音樂需要自動暫停播放,耳機拔出時,同樣需要暫停,還要支持耳機線控,等等,這些都是需要我們考慮的。

一個合格的音樂播放器應該具有哪些基本素質?

由于播放、暫停、切換歌曲、進度調節等這些功能過于簡單,因此不過多討論,這里只討論一些容易被忽略的功能。

掃描本地音樂

掃描歌曲是播放器的基本功能,一般通過ContentProvider配合Media相關類查詢系統數據庫,獲得媒體庫中的歌曲信息。

/*** 掃描歌曲*/ public static void scanMusic(Context context, List<Music> musicList) {musicList.clear();Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,new String[]{BaseColumns._ID,MediaStore.Audio.AudioColumns.IS_MUSIC,MediaStore.Audio.AudioColumns.TITLE,MediaStore.Audio.AudioColumns.ARTIST,MediaStore.Audio.AudioColumns.ALBUM,MediaStore.Audio.AudioColumns.ALBUM_ID,MediaStore.Audio.AudioColumns.DATA,MediaStore.Audio.AudioColumns.DISPLAY_NAME,MediaStore.Audio.AudioColumns.SIZE,MediaStore.Audio.AudioColumns.DURATION},null,null,MediaStore.Audio.Media.DEFAULT_SORT_ORDER);if (cursor == null) {return;}while (cursor.moveToNext()) {// 是否為音樂int isMusic = cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.IS_MUSIC));if (isMusic == 0) {continue;}long id = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));// 標題String title = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.AudioColumns.TITLE)));// 藝術家String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.ARTIST));// 專輯String album = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.AudioColumns.ALBUM)));// 專輯封面id,根據該id可以獲得專輯封面圖片long albumId = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.ALBUM_ID));// 持續時間long duration = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION));// 音樂文件路徑String path = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DATA));// 音樂文件名String fileName = cursor.getString((cursor.getColumnIndex(MediaStore.Audio.AudioColumns.DISPLAY_NAME)));// 音樂文件大小long fileSize = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.SIZE));Music music = new Music();music.set...musicList.add(music);}cursor.close(); }/*** 從媒體庫加載封面*/ private Bitmap loadCoverFromMediaStore(long albumId) {ContentResolver resolver = mContext.getContentResolver();Uri uri = MusicUtils.getMediaStoreAlbumCoverUri(albumId);InputStream is;try {is = resolver.openInputStream(uri);} catch (FileNotFoundException ignored) {return null;}BitmapFactory.Options options = new BitmapFactory.Options();options.inPreferredConfig = Bitmap.Config.RGB_565;return BitmapFactory.decodeStream(is, null, options); } 復制代碼

通過以上方法基本可以獲得音樂的所有信息,弊端是依賴于Android系統媒體庫,有時新增音樂后沒有通知系統掃描,就無法獲得該音樂的信息,不夠靈活。

避免播放器內存被系統回收

我們都知道Android系統有自動回收內存機制,如果系統內存緊張,就會觸發該機制,應用就有可能被回收,不過Android提供了前臺機制,保證內存不足時也不會回收該應用。

/*** 播放時啟動前臺機制*/ public static void showPlay(Music music) {playService.startForeground(NOTIFICATION_ID, buildNotification(playService, music, true)); }/*** 暫停時取消前臺機制*/ public static void showPause(Music music) {playService.stopForeground(false);notificationManager.notify(NOTIFICATION_ID, buildNotification(playService, music, false)); } 復制代碼

捕獲/丟棄音樂焦點

大家可能不懂這個標題是什么意思,別著急,讓我細細道來。 大家有沒有試過,如果手機上安裝了兩個音樂播放器,當一個正在播放的時候,打開第二個播放歌曲,有沒有發現第一個自動暫停了? 或者正在聽歌時來電話了,音樂暫停了,掛斷電話后音樂又繼續播放了, 或者收到通知的時候音樂的音量變小了一下又恢復。

“-納尼!難道不是自動暫停?” “-圖樣圖森破!”

這其實是因為播放器在后臺處理了音頻焦點的原因。

public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {private PlayService mPlayService;private AudioManager mAudioManager;private boolean isPausedByFocusLossTransient;private int mVolumeWhenFocusLossTransientCanDuck;public AudioFocusManager(@NonNull PlayService playService) {mPlayService = playService;mAudioManager = (AudioManager) playService.getSystemService(AUDIO_SERVICE);}/*** 播放音樂前先請求音頻焦點*/public boolean requestAudioFocus() {return mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN)== AudioManager.AUDIOFOCUS_REQUEST_GRANTED;}/*** 退出播放器后不再占用音頻焦點*/public void abandonAudioFocus() {mAudioManager.abandonAudioFocus(this);}/*** 音頻焦點監聽回調*/@Overridepublic void onAudioFocusChange(int focusChange) {int volume;switch (focusChange) {// 重新獲得焦點case AudioManager.AUDIOFOCUS_GAIN:if (!willPlay() && isPausedByFocusLossTransient) {// 通話結束,恢復播放mPlayService.playPause();}volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);if (mVolumeWhenFocusLossTransientCanDuck > 0 && volume == mVolumeWhenFocusLossTransientCanDuck / 2) {// 恢復音量mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mVolumeWhenFocusLossTransientCanDuck,AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);}isPausedByFocusLossTransient = false;mVolumeWhenFocusLossTransientCanDuck = 0;break;// 永久丟失焦點,如被其他播放器搶占case AudioManager.AUDIOFOCUS_LOSS:if (willPlay()) {forceStop();}break;// 短暫丟失焦點,如來電case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:if (willPlay()) {forceStop();isPausedByFocusLossTransient = true;}break;// 瞬間丟失焦點,如通知case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:// 音量減小為一半volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);if (willPlay() && volume > 0) {mVolumeWhenFocusLossTransientCanDuck = volume;mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, mVolumeWhenFocusLossTransientCanDuck / 2,AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);}break;}}private boolean willPlay() {return mPlayService.isPreparing() || mPlayService.isPlaying();} } 復制代碼

耳機拔出時暫停播放

“-納尼!難道耳機拔出時不是自動暫停嗎?” “-圖樣……”

private IntentFilter mNoisyFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);public class NoisyAudioStreamReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {// 耳機拔出時暫停播放PlayService.startCommand(context, Actions.ACTION_MEDIA_PLAY_PAUSE);} } 復制代碼

播放時注冊廣播接收器,暫停時取消注冊即可。

聯動系統媒體中心

這個標題大家可能也不懂,先放張圖吧

明白了吧,我的播放器除了播放了一首音樂之外什么都沒做,就可以分別在任務管理、鎖屏、負一屏控制我的播放器了,是不是感覺碉堡了。 這些圖是在我的小米手機上截的,不保證所有手機都有這些控制功能,但是只要你的Android版本是5.0以上,應該都會有媒體中心,無非是表現形式不同。 Android 5.0中新增了MediaSession類,官方說明是

允許與媒體控制器、音量鍵、媒體按鈕和傳輸控件交互。

一個類包含了媒體控制和線控等功能,是不是很好用。 現在support-v4包加入了MediaSessionCompat用于在低版本上也能使用這個高大上的功能, 但是低版本上并不能實現媒體控制和線控等功能,低版本的線控功能我會在后面講。

public class MediaSessionManager {private static final String TAG = "MediaSessionManager";private static final long MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY| PlaybackStateCompat.ACTION_PAUSE| PlaybackStateCompat.ACTION_PLAY_PAUSE| PlaybackStateCompat.ACTION_SKIP_TO_NEXT| PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS| PlaybackStateCompat.ACTION_STOP| PlaybackStateCompat.ACTION_SEEK_TO;private PlayService mPlayService;private MediaSessionCompat mMediaSession;public MediaSessionManager(PlayService playService) {mPlayService = playService;setupMediaSession();}/*** 初始化并激活MediaSession*/private void setupMediaSession() {mMediaSession = new MediaSessionCompat(mPlayService, TAG);mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS| MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS);mMediaSession.setCallback(callback);mMediaSession.setActive(true);}/*** 更新播放狀態,播放/暫停/拖動進度條時調用*/public void updatePlaybackState() {int state = (mPlayService.isPlaying() || mPlayService.isPreparing())? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED;mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder().setActions(MEDIA_SESSION_ACTIONS).setState(state, mPlayService.getCurrentPosition(), 1).build());}/*** 更新正在播放的音樂信息,切換歌曲時調用*/public void updateMetaData(Music music) {if (music == null) {mMediaSession.setMetadata(null);return;}MediaMetadataCompat.Builder metaData = new MediaMetadataCompat.Builder().putString(MediaMetadataCompat.METADATA_KEY_TITLE, music.getTitle()).putString(MediaMetadataCompat.METADATA_KEY_ARTIST, music.getArtist()).putString(MediaMetadataCompat.METADATA_KEY_ALBUM, music.getAlbum()).putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ARTIST, music.getArtist()).putLong(MediaMetadataCompat.METADATA_KEY_DURATION, music.getDuration()).putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, CoverLoader.getInstance().loadThumbnail(music));if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {metaData.putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, AppCache.getMusicList().size());}mMediaSession.setMetadata(metaData.build());}/*** 釋放MediaSession,退出播放器時調用*/public void release() {mMediaSession.setCallback(null);mMediaSession.setActive(false);mMediaSession.release();}private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {@Overridepublic void onPlay() {mPlayService.playPause();}@Overridepublic void onPause() {mPlayService.playPause();}@Overridepublic void onSkipToNext() {mPlayService.next();}@Overridepublic void onSkipToPrevious() {mPlayService.prev();}@Overridepublic void onStop() {mPlayService.stop();}@Overridepublic void onSeekTo(long pos) {mPlayService.seekTo((int) pos);}}; } 復制代碼

耳機線控(適用于API 19及以下)

“-納尼……” “-Shut up !”

是的,需要我們自己控制。 如果你已經按照上面的方法激活了MediaSession,那么在5.0以上的系統你已經不需要關心線控功能了,但是在5.0以上仍然需要自己監聽耳機按鍵。

public class RemoteControlReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);if (event == null || event.getAction() != KeyEvent.ACTION_UP) {return;}Intent serviceIntent;switch (event.getKeyCode()) {case KeyEvent.KEYCODE_MEDIA_PLAY:case KeyEvent.KEYCODE_MEDIA_PAUSE:case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:case KeyEvent.KEYCODE_HEADSETHOOK:serviceIntent = new Intent(context, PlayService.class);serviceIntent.setAction(Actions.ACTION_MEDIA_PLAY_PAUSE);context.startService(serviceIntent);break;case KeyEvent.KEYCODE_MEDIA_NEXT:serviceIntent = new Intent(context, PlayService.class);serviceIntent.setAction(Actions.ACTION_MEDIA_NEXT);context.startService(serviceIntent);break;case KeyEvent.KEYCODE_MEDIA_PREVIOUS:serviceIntent = new Intent(context, PlayService.class);serviceIntent.setAction(Actions.ACTION_MEDIA_PREVIOUS);context.startService(serviceIntent);break;}} }<!--在AndroidManifest中注冊Receiver--> <receiver android:name=".receiver.RemoteControlReceiver"><intent-filter><action android:name="android.intent.action.MEDIA_BUTTON" /></intent-filter> </receiver> 復制代碼

總結

感謝你能耐心的看到最后,本文主要討論了音樂播放器容易忽略的重要功能,如果還有其他的本文沒有提到的,請大家不吝賜教。

遷移自我的簡書 2016.06.08

總結

以上是生活随笔為你收集整理的Android开源音乐播放器之播放器基本功能的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。