• 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大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    gdb 调试(查看运行时数据)(五)
    Kendo UI开发教程(23): 单页面应用(一)概述
    Kendo UI开发教程(24): 单页面应用(二) Router 类
    Kendo UI开发教程(26): 单页面应用(四) Layout
    Kendo UI开发教程(25): 单页面应用(三) View
    Kendo UI开发教程(27): 移动应用开发简介
    [置顶] Kendo UI开发教程: Kendo UI 示例及总结
    web端、android端的文件上传
    [置顶] 关于本博客 http://www.imobilebbs.com
    hdu4707 Pet
  • 原文地址:https://www.cnblogs.com/demodashi/p/9712123.html
Copyright © 2020-2023  润新知