在画布与绘制的部分就已经学习了如何实现简易的视频播放、暂停、停止
https://blog.csdn.net/nishigesb123/article/details/89468251#t7
而这部分关于安卓中的多媒体播放,利用这部分知识,可以实现一个“真正”的媒体播放器。
安卓多媒体播放涉及的内容有:(包括但不仅仅)
MediaPlayer(播放声音和视频的类,本篇的重点)
AudioManager(管理音频和音频输出设备)
SurfaceView(即开头提到的)
VideoView
TextureView
当然还有若干的第三方框架
可能需要的权限有:(包括但不仅仅)本篇及后续文章默认添加了下述权限~
如果你的播放应用需要阻止屏幕变暗或阻止处理器睡眠,或使用MediaPlayer.setScreenOnWhilePlaying()或 MediaPlayer.setWakeModel方法你必须请求此权限
安卓多媒体框架中最重要的组件之一就是Mediaplayer类。
需要在res目录下创建raw目录,并放入对应的音频文件
//播放本地资源文件
public void playFromRes(View v) {
MediaPlayer mp = MediaPlayer.create(this, R.raw.test);
mp.start();
}
需要在sdcard目录下的Music目录下加入对应的资源文件,并且需要读取外部存储器权限
//播放系统文件
public void playFromSys(View v) {
MediaPlayer mp = new MediaPlayer();
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getPath() + "/test.wav";
try {
//设置数据源
mp.setDataSource(this, Uri.parse(path));
mp.prepare();
mp.start();
} catch (IOException e) {
e.printStackTrace();
}
}
这里注意:
preparel()调用可能很耗时,因为它可能需要获取并打开解码媒体数据,会执行很长时间,此时你就不能从你的应用的UI线程中调用它,这会导致U挂起,选择prepareAsync()替代它。
需要在配置清单加入网络权限
//播放网络资源文件
public void playFromNet(View v) {
String path = "http://gddx.sc.chinaz.net/Files/DownLoad/sound1/201902/11213.wav";
MediaPlayer mp = new MediaPlayer();
try {
mp.setDataSource(this, Uri.parse(path));
mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
//异步缓冲 调用立即开始用另一个线程取缓存
mp.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
测试效果就没办法了,都是音频,截个图意思意思
MediaPlayer有一个内部的状态,特定的操作只能在特定的状态时才有效。如果你在错误的状态下执行一个操作,系统可能抛出一个异常或导致一个意外的行为。
本节内容所有图片出处:https://blog.csdn.net/shulianghan/article/details/38487967
mediaPlayer.release();
mediaPlayer = null;
核心是四个按钮及对应点击事件,图片仅仅是装饰品
用到了读取外部存储,所以需要给对应的权限。
主要实现
package com.example.a4_24musicplayer;
import android.media.MediaPlayer;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
//简单播放器
public class MainActivity extends AppCompatActivity implements View.OnClickListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener {
private MediaPlayer mp;
//表示当前要播放音乐的索引 0即第一首
private int index = 0;
//音乐文件列表(存音乐文件地址)
private ArrayList musicList = new ArrayList<>();
//按钮
private Button button_play, button_pause, button_next, button_last;
//是否处于暂停播放状态
private boolean isPause = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initMusic();
mp = new MediaPlayer();
//注册
mp.setOnPreparedListener(this);
mp.setOnErrorListener(this);
mp.setOnCompletionListener(this);
}
private void initMusic() {
String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getPath();
//separator系统的默认名称分隔符
musicList.add(root + File.separator + "a1.mp3");
musicList.add(root + File.separator + "a2.mp3");
musicList.add(root + File.separator + "a3.mp3");
musicList.add(root + File.separator + "a4.mp3");
}
private void initView() {
button_play = findViewById(R.id.play);
button_pause = findViewById(R.id.pause);
button_next = findViewById(R.id.next);
button_last = findViewById(R.id.last);
button_play.setOnClickListener(this);
button_pause.setOnClickListener(this);
button_next.setOnClickListener(this);
button_last.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.play:
play();
break;
case R.id.pause:
pause();
break;
case R.id.next:
next();
break;
case R.id.last:
last();
break;
default:
break;
}
}
//开始播放
private void play() {
if (isPause) {
mp.start();
isPause = false;
} else {
restart();
}
}
//从头开始播放音乐
private void restart() {
if (index < musicList.size()) {
//如果正在播放就停止
if (mp.isPlaying()) mp.stop();
mp.reset();
String musicPath = musicList.get(index);
try {
mp.setDataSource(musicPath);
mp.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//暂停播放
private void pause() {
if (mp.isPlaying()) {
mp.pause();
isPause = true;
}
}
//播放上一首
private void last() {
if (index - 1 >= 0) {
index--;
} else {
index = musicList.size() - 1;
}
restart();
}
//播放下一首
private void next() {
if (index + 1 < musicList.size()) {
index++;
} else {
index = 0;
}
restart();
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return true;
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
@Override
public void onCompletion(MediaPlayer mp) {
next();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mp != null) {
if (mp.isPlaying()) mp.stop();
mp.release();
}
}
}
如果你希望你的媒体在你的应用不出现在屏幕上时仍能在后台播放。
即,你希望当用户与其它应用交互时仍能继续播放,那么你必须启动一个Service并且通过它来控制MediaPlayer实例。
先介绍一个可能用到的东西。
注意:你应该保守的使用wakelocks,并且仅在真证需要时才持有它,因为它们会显著的减少设备电池的寿命
需要权限
创建一个MusicService
package com.example.a4_24service_mediaplayer;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayer;
import android.net.wifi.WifiManager;
import android.os.Environment;
import android.os.IBinder;
import android.os.PowerManager;
import java.io.File;
import java.io.IOException;
public class MusicService extends Service implements MediaPlayer.OnPreparedListener {
//将操作定义成公共常量
public static final String ACTION_PLAY = "com.example.ACTION_PLAY";
public static final String ACTION_PAUSE = "com.example.ACTION_PAUSE";
public static final String ACTION_EXIT = "com.example.ACTION_EXIT";
private WifiManager.WifiLock lock;
private MediaPlayer mediaPlayer;
public MusicService() {
}
@Override
public void onCreate() {
super.onCreate();
//创建实例
mediaPlayer = new MediaPlayer();
//注册
mediaPlayer.setOnPreparedListener(this);
//保持CPU正常工作,设置模式
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
//保持WIFI达到不被休眠
WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
lock = wifiManager.createWifiLock("mylock");
//得到锁
lock.acquire();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent.getAction();
//判断操作
if (ACTION_PLAY.equals(action)) {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + File.separator + "a1.mp3");
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
} else if (ACTION_PAUSE.equals(action)) {
if (mediaPlayer.isPlaying()) mediaPlayer.pause();
} else if (ACTION_EXIT.equals(action)) {
if (mediaPlayer.isPlaying()) mediaPlayer.stop();
mediaPlayer.release();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
//释放锁
lock.release();
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
MainActivity
package com.example.a4_24service_mediaplayer;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void play(View v) {
Intent intent = new Intent(this, MusicService.class);
intent.setAction(MusicService.ACTION_PLAY);
startService(intent);
}
public void pause(View v) {
Intent intent = new Intent(this, MusicService.class);
intent.setAction(MusicService.ACTION_PAUSE);
startService(intent);
}
public void exit(View v) {
Intent intent = new Intent(this, MusicService.class);
intent.setAction(MusicService.ACTION_EXIT);
startService(intent);
}
}
需要在配置清单里进行注册
在MusicService服务里也进行一些修改
创建一个notification(),并且在onCreate()调用。
private void notification() {
Notification.Builder builder = new Notification.Builder(this);
builder.setTicker("音乐播放器");
builder.setSmallIcon(R.mipmap.test);
builder.setContentTitle("我的音乐播放器");
builder.setContentInfo("正在播放");
PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pi);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = builder.build();
startForeground(0, notification);
nm.notify(0,notification);
}
最后onDestroy()的时候删除前台通知
@Override
public void onDestroy() {
super.onDestroy();
stopForeground(true);
//释放锁
lock.release();
}
效果如?
在安卓8.0及以上在通知栏的政策有一定修改。
可以参考https://blog.csdn.net/u010231682/article/details/80732879
提供了一种新版本的写法,可以参考?
private void notification() {
NotificationChannel channel = new NotificationChannel("Notify","Notify", NotificationManager.IMPORTANCE_LOW);
Notification.Builder builder = new Notification.Builder(this,"Notify");
builder.setTicker("音乐播放器");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("我的音乐播放器");
builder.setContentInfo("正在播放");
PendingIntent pi = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pi);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = builder.build();
startForeground(0, notification);
nm.notify(0,notification);
}