• android学习笔记----简易音乐播放器原理


    只是熟悉MediaPlayer用法

    简易功能是:播放暂停,停止后从头播放,进度条跟随音乐进度,拖动进度条音乐也随之改变播放进度。

    网上音乐接口百度能够搜到,这里就只写一点原理。

    官方文档地址:https://developer.android.google.cn/reference/android/media/MediaPlayer

    Demo地址:https://github.com/liuchenyang0515/MusicBox

    状态图:

    MediaPlayer State diagram

    运行效果图:

    MainActivity.java

    import android.Manifest;
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.content.pm.PackageManager;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.SeekBar;
    import android.widget.Toast;
    
    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        private Iservice iservice;
        private MyConn myConn;
        public static Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                // 获取携带的数据
                Bundle data = msg.getData();
                // 获取歌曲的总时长和当前进度
                int duration = data.getInt("duration");
                int currentPosition = data.getInt("currentPosition");
                // 设置seekbar进度
                sbar.setMax(duration);
                sbar.setProgress(currentPosition);
            }
        };
        private static SeekBar sbar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 混合方式开启服务
            Intent intent = new Intent(this, MusicSevice.class);
            startService(intent);
            // 调用bindService,为了回去定义的中间人对象
            myConn = new MyConn();
            bindService(intent, myConn, BIND_AUTO_CREATE);
            // 找到seekbar,设置进度
            sbar = findViewById(R.id.seekBar);
            // 4.给seekBar设置监听事件
            sbar.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) {
                    iservice.callSeekTo(seekBar.getProgress());
                }
            });
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            // 当activity销毁的时候调用,为了不报异常
            unbindService(myConn);
            Log.d(TAG, "onDestroy: ");
        }
    
        // 播放
        public void click(View view) {
            // 这里播放tomcat服务器的音乐不需要权限,如果是播放/mnt/sdcard/...需要权限,因为测试的时候在这个目录,所以这里写了权限
            applyPermissions(Manifest.permission.READ_EXTERNAL_STORAGE);
        }
    
        private void applyPermissions(String readExternalStorage) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{readExternalStorage}, 1);
            } else {
                iservice.callPlayMusic();
            }
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            switch (requestCode) {
                case 1:
                    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                        iservice.callPlayMusic();
                    } else {
                        Toast.makeText(this, "权限被拒绝,请在设置手动打开", Toast.LENGTH_SHORT).show();
                    }
                    break;
            }
        }
    
        // 暂停
        public void click2(View view) {
            iservice.callPauseMusic();
        }
    
        public void click3(View view) {
            iservice.callStopMusic();
        }
    
    
        // 监听服务的状态
        private class MyConn implements ServiceConnection {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                iservice = (Iservice) service;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        }
    }
    

     MusicSevice.java

    import android.app.Service;
    import android.content.Intent;
    import android.media.MediaPlayer;
    import android.os.Binder;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.Message;
    import android.util.Log;
    
    import java.io.IOException;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class MusicSevice extends Service {
        private static final String TAG = "MusicSevice";
        private MediaPlayer mediaPlayer;
        private Timer timer;
        private TimerTask task;
    
        public MusicSevice() {
            Log.d(TAG, "构造: ");
            initMediaPlayer();
        }
    
        // 把定义的中间人返回
        @Override
        public IBinder onBind(Intent intent) {
            return new MyBinder();
        }
    
        // 服务已开启就执行这个方法
        @Override
        public void onCreate() {
            Log.d(TAG, "onCreate: ");
            super.onCreate();
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    
        // 播放音乐的方法
        public void playMusic() {
            // 准备播放
            // mediaPlayer.prepare();
            if (mediaPlayer == null) {
                Log.d(TAG, "mediaplayer确实为null");
                initMediaPlayer();
                mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { // 准备完成的监听器
                    @Override
                    public void onPrepared(MediaPlayer mp) {
                        // 开始播放
                        mediaPlayer.start();
                        // 更新进度条
                        updateSeekBar();
                    }
                });
            }
            if (!mediaPlayer.isPlaying()) {
                Log.d(TAG, "没有正在播放,开始操作");
                mediaPlayer.start();
                // 更新进度条
                updateSeekBar();
                Log.d(TAG, "音乐播放了");
            }
        }
    
        private void initMediaPlayer() {
            // 初始化mediaplayer
            try {
                mediaPlayer = new MediaPlayer();
                // 设置要播放的资源位置path,可以是网络路径,也可以是本地路径
                // /mnt/sdcard/yinyue.mp3
                mediaPlayer.setDataSource("http://192.168.164.1:8080/yinyue.mp3");
                mediaPlayer.prepareAsync();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void updateSeekBar() {
            // 获取当前歌曲总长度
            final int duration = mediaPlayer.getDuration();
            // 使用Timer 定时器去获取当前进度
            timer = new Timer();
            task = new TimerTask() {
                @Override
                public void run() {
                    // 一秒钟获取一次当前进度
                    int currentPosition = mediaPlayer.getCurrentPosition();
                    // 拿到我们在MainActivity创建的handler发消息,消息可以携带数据
                    Message message = Message.obtain();
                    //obj只能携带一个数据
                    Bundle bundle = new Bundle(); // map
                    bundle.putInt("duration", duration);
                    bundle.putInt("currentPosition", currentPosition);
                    message.setData(bundle);
                    // 发送一条消息,MainActivity里面的handlemessage就会执行
                    MainActivity.handler.handleMessage(message);
                }
            };
            // 100ms后每间隔1s、执行一次run方法
            timer.schedule(task, 100, 1000);
            // 设置播放完成的监听
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    Log.d(TAG, "歌曲播放完成");
                    timer.cancel();
                    task.cancel();
                }
            });
        }
    
        // 实现指定播放的位置
        public void seekTo(int position) {
            mediaPlayer.seekTo(position);
        }
    
        // 暂停音乐的方法
        public void pauseMusic() {
            if (mediaPlayer.isPlaying()) {
                // 暂停音乐
                mediaPlayer.pause();
                timer.cancel();
                Log.d(TAG, "音乐暂停了");
            }
        }
    
        public void stopMusic() {
            mediaPlayer.stop();
            // 如果不取消定时任务,mediaplayer停止就崩了
            timer.cancel();
            task.cancel();
            mediaPlayer = null;
        }
    
        // 在服务的内部定义一个中间人对象(IBinder)
        private class MyBinder extends Binder implements Iservice {
    
            @Override
            public void callPlayMusic() {
                playMusic();
            }
    
            @Override
            public void callPauseMusic() {
                pauseMusic();
            }
    
            @Override
            public void callStopMusic() {
                stopMusic();
            }
    
            @Override
            public void callSeekTo(int position) {
                seekTo(position);
            }
        }
    }
    

    Iservice.java

    public interface Iservice {
        // 把想暴露的方法都定义在接口中
        public void callPlayMusic();
        public void callPauseMusic();
        public void callStopMusic();
        public void callSeekTo(int position);
    }
    

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="click"
            android:text="播放" />
    
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="click2"
            android:text="暂停" />
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="click3"
            android:text="停止"
        />
        <SeekBar
            android:id="@+id/seekBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"/>
    </LinearLayout>

    =========================Talk is cheap, show me the code=========================

    CSDN博客地址:https://blog.csdn.net/qq_34115899
  • 相关阅读:
    LeetCode 172. Factorial Trailing Zeroes
    C++primer 练习12.27
    C++primer 练习12.6
    C++primer 练习11.33:实现你自己版本的单词转换程序
    77. Combinations
    75. Sort Colors
    74. Search a 2D Matrix
    73. Set Matrix Zeroes
    71. Simplify Path
    64. Minimum Path Sum
  • 原文地址:https://www.cnblogs.com/lcy0515/p/10807862.html
Copyright © 2020-2023  润新知