zoukankan      html  css  js  c++  java
  • Android 本地播放器

    概述

    这是一款Android 端的本地音乐播放器,界面风格有模仿网易云音乐、bilibili、酷安、酷狗等。整体设计遵循了 Material Design 设计风格,界面美观,轻便实用,目前实现了基本的播放控制功能,还有主题切换, 可以扫描本地所有音乐文件并按歌单分类播放,目前已经上架到酷安应用市场。

    详细

    一、先看效果图

    home_nav.jpg

    二、项目的播放流程简要介绍

    1.首先我们需要一个常驻在后台的播放服务,在播放服务中绑定一个播放广播,我们在打开播放器的时候就启动这个播放服务。

    public class MusicPlayerService extends Service {
        private static final String TAG = MusicPlayerService.class.getName();
    
        public static final String PLAYER_MANAGER_ACTION = "com.lijunyan.blackmusic.service.MusicPlayerService.player.action";
    
        private PlayerManagerReceiver mReceiver;
    
        public MusicPlayerService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            throw new UnsupportedOperationException("Not yet implemented");
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.e(TAG, "onCreate: ");
            register();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.e(TAG, "onDestroy: ");
            unRegister();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.e(TAG, "onStartCommand: ");
            return super.onStartCommand(intent, flags, startId);
        }
    
    
        private void register() {
            mReceiver = new PlayerManagerReceiver(MusicPlayerService.this);
            IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(PLAYER_MANAGER_ACTION);
            registerReceiver(mReceiver, intentFilter);
        }
    
        private void unRegister() {
            if (mReceiver != null) {
                unregisterReceiver(mReceiver);
            }
        }
    
    }

    2.播放服务中的广播可以接受各种音频控制操作,包括播放、暂停、切歌等。程序在响应用户的音频控制操作时向这个播放广播发送对应的播放、暂停、停止等指令。广播收到不同的指令做不同的功能实现。

    public class PlayerManagerReceiver extends BroadcastReceiver {
    
        private static final String TAG = PlayerManagerReceiver.class.getName();
        public static final String ACTION_UPDATE_UI_ADAPTER = "com.lijunyan.blackmusic.receiver.PlayerManagerReceiver:action_update_ui_adapter_broad_cast";
        private MediaPlayer mediaPlayer;
        private DBManager dbManager;
        public static int status = Constant.STATUS_STOP;
        private int playMode;
        private int threadNumber;
        private Context context;
    
        public PlayerManagerReceiver() {
        }
    
        public PlayerManagerReceiver(Context context) {
            super();
            this.context = context;
            dbManager = DBManager.getInstance(context);
            mediaPlayer = new MediaPlayer();
            Log.d(TAG, "create");
            initMediaPlayer();
        }
    
        @Override
        public void onReceive(Context context, Intent intent) {
            int cmd = intent.getIntExtra(Constant.COMMAND,Constant.COMMAND_INIT);
            Log.d(TAG, "cmd = " + cmd);
            switch (cmd) {
                case Constant.COMMAND_INIT:    
                    Log.d(TAG, "COMMAND_INIT");
                    break;
                case Constant.COMMAND_PLAY:
                    Log.d(TAG, "COMMAND_PLAY");
                    status = Constant.STATUS_PLAY;
                    String musicPath = intent.getStringExtra(Constant.KEY_PATH);
                    if (musicPath!=null) {
                        playMusic(musicPath);
                    }else {
                        mediaPlayer.start();
                    }
                    break;
                case Constant.COMMAND_PAUSE:
                    mediaPlayer.pause();
                    status = Constant.STATUS_PAUSE;
                    break;
                case Constant.COMMAND_STOP: 
                    NumberRandom();
                    status = Constant.STATUS_STOP;
                    if(mediaPlayer!=null) {
                        mediaPlayer.stop();
                    }
                    initStopOperate();
                    break;
                case Constant.COMMAND_PROGRESS://拖动进度
                    int curProgress = intent.getIntExtra(Constant.KEY_CURRENT, 0);
                    //异步的,可以设置完成监听来获取真正定位完成的时候
                    mediaPlayer.seekTo(curProgress);
                    break;
                case Constant.COMMAND_RELEASE:
                    NumberRandom();
                    status = Constant.STATUS_STOP;
                    if(mediaPlayer!=null) {
                        mediaPlayer.stop();
                        mediaPlayer.release();
                    }
                    break;
            }
            UpdateUI();
        }
    
        private void initStopOperate(){
            MyMusicUtil.setShared(Constant.KEY_ID,dbManager.getFirstId(Constant.LIST_ALLMUSIC));
        }
    
        private void playMusic(String musicPath) {
            NumberRandom();
            if (mediaPlayer!=null) {
                mediaPlayer.release();
            }
            mediaPlayer = new MediaPlayer();
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    
                @Override
                public void onCompletion(MediaPlayer mp) {
                    Log.d(TAG, "playMusic onCompletion: ");
                    NumberRandom();             //切换线程
                    onComplete();     //调用音乐切换模块,进行相应操作
                    UpdateUI();             //更新界面
                }
            });
    
            try {
                File file = new File(musicPath);
                if(!file.exists()){
                    Toast.makeText(context,"歌曲文件不存在,请重新扫描",Toast.LENGTH_SHORT).show();
                    MyMusicUtil.playNextMusic(context);
                    return;
                }
                mediaPlayer.setDataSource(musicPath);   //设置MediaPlayer数据源
                mediaPlayer.prepare();
                mediaPlayer.start();
    
                new UpdateUIThread(this, context, threadNumber).start();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
    
        //取一个(0,100)之间的不一样的随机数
        private void NumberRandom() {
            int count;
            do {
                count =(int)(Math.random()*100);
            } while (count == threadNumber);
            threadNumber = count;
        }
    
        private void onComplete() {
            MyMusicUtil.playNextMusic(context);
        }
    
        private void UpdateUI() {
            Intent playBarintent = new Intent(PlayBarFragment.ACTION_UPDATE_UI_PlayBar);    //接收广播为MusicUpdateMain
            playBarintent.putExtra(Constant.STATUS, status);
            context.sendBroadcast(playBarintent);
    
            Intent intent = new Intent(ACTION_UPDATE_UI_ADAPTER);    //接收广播为所有歌曲列表的adapter
            context.sendBroadcast(intent);
    
        }
    
    
        private void initMediaPlayer() {
    
            NumberRandom(); // 改变线程号,使旧的播放线程停止
    
            int musicId = MyMusicUtil.getIntShared(Constant.KEY_ID);
            int current = MyMusicUtil.getIntShared(Constant.KEY_CURRENT);
            Log.d(TAG, "initMediaPlayer musicId = " + musicId);
    
            // 如果是没取到当前正在播放的音乐ID,则从数据库中获取第一首音乐的播放信息初始化
            if (musicId == -1) {
                return;
            }
            String path = dbManager.getMusicPath(musicId);
            if (path == null) {
                Log.e(TAG, "initMediaPlayer: path == null");
                return;
            }
            if (current == 0) {
                status = Constant.STATUS_STOP; // 设置播放状态为停止
            }else {
                status = Constant.STATUS_PAUSE; // 设置播放状态为暂停
            }
            Log.d(TAG, "initMediaPlayer status = " + status);
            MyMusicUtil.setShared(Constant.KEY_ID,musicId);
            MyMusicUtil.setShared(Constant.KEY_PATH,path);
    
            UpdateUI();
        }
    
        public MediaPlayer getMediaPlayer() {
            return mediaPlayer;
        }
    
        public int getThreadNumber() {
            return threadNumber;
        }
    }

    3.项目在播放一个音频的同时维护了一个线程实时去通知界面刷新,该线程从MediaPlayer中获取当前的播放进度、总时间等信息发送给播放界面,播放界面拿到数据就可以刷新播放显示信息了。

    public class UpdateUIThread extends Thread {
    
       private static final String TAG = UpdateUIThread.class.getName();
       private int threadNumber;
       private Context context;
       private PlayerManagerReceiver playerManagerReceiver;
       private int duration;
       private int curPosition;
       
       public UpdateUIThread(PlayerManagerReceiver playerManagerReceiver, Context context, int threadNumber) {
          Log.i(TAG, "UpdateUIThread: " );
          this.playerManagerReceiver = playerManagerReceiver;
          this.context = context;
          this.threadNumber = threadNumber;
       }
    
       @Override
       public void run() {
          try {
             while (playerManagerReceiver.getThreadNumber() == this.threadNumber) {
                if (playerManagerReceiver.status == Constant.STATUS_STOP) {
                   Log.e(TAG, "run: Constant.STATUS_STOP");
                   break;
                }
                if (playerManagerReceiver.status == Constant.STATUS_PLAY ||
                      playerManagerReceiver.status == Constant.STATUS_PAUSE) {
                   if (!playerManagerReceiver.getMediaPlayer().isPlaying()) {
                      Log.i(TAG, "run: getMediaPlayer().isPlaying() = " + playerManagerReceiver.getMediaPlayer().isPlaying());
                      break;
                   }
                   duration = playerManagerReceiver.getMediaPlayer().getDuration();
                   curPosition = playerManagerReceiver.getMediaPlayer().getCurrentPosition();
                   Intent intent = new Intent(PlayBarFragment.ACTION_UPDATE_UI_PlayBar);
                   intent.putExtra(Constant.STATUS, Constant.STATUS_RUN);
                   intent.putExtra(Constant.KEY_DURATION, duration);
                   intent.putExtra(Constant.KEY_CURRENT, curPosition);
                   context.sendBroadcast(intent);
                }
                try {
                   Thread.sleep(100);
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
             }
          }catch (Exception e){
             e.printStackTrace();
          }
          
       }
    }

    三、项目文件结构

    屏幕快照 2018-09-06 上午10.10.47.png

    四、其他

    如果对你有帮助的话,可以给我github个star,谢谢。

    https://github.com/lijunyandev/MeetMusic

    注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    高精度计算
    高精度除以低精度
    P1258 小车问题
    POJ 2352 stars (树状数组入门经典!!!)
    HDU 3635 Dragon Balls(超级经典的带权并查集!!!新手入门)
    HDU 3938 Portal (离线并查集,此题思路很强!!!,得到所谓的距离很巧妙)
    POJ 1703 Find them, Catch them(确定元素归属集合的并查集)
    HDU Virtual Friends(超级经典的带权并查集)
    HDU 3047 Zjnu Stadium(带权并查集,难想到)
    HDU 3038 How Many Answers Are Wrong(带权并查集,真的很难想到是个并查集!!!)
  • 原文地址:https://www.cnblogs.com/demodashi/p/9712123.html
Copyright © 2011-2022 走看看