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

2023-05-06 19:10:27 浏览数 (1)

只是熟悉MediaPlayer用法

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

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

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

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

状态图:

运行效果图:

MainActivity.java

代码语言:javascript复制
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

代码语言:javascript复制
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

代码语言:javascript复制
public interface Iservice {
    // 把想暴露的方法都定义在接口中
    public void callPlayMusic();
    public void callPauseMusic();
    public void callStopMusic();
    public void callSeekTo(int position);
}

activity_main.xml

代码语言:javascript复制
<?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>

0 人点赞