只是熟悉MediaPlayer用法
简易功能是:播放暂停,停止后从头播放,进度条跟随音乐进度,拖动进度条音乐也随之改变播放进度。
网上音乐接口百度能够搜到,这里就只写一点原理。
官方文档地址:https://developer.android.google.cn/reference/android/media/MediaPlayer
Demo地址:https://github.com/liuchenyang0515/MusicBox
状态图:
运行效果图:
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=========================