一、 基本介绍
简单的音乐播放器实例, 提供播放、停止、暂停、退出音乐操作。 先由Activity 实现,再引出由Service(本地Service)实现。
二、Acitivity实现
1. 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Play"
android:onClick="playMusic"/>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Stop"
android:onClick="stopMusic"/>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Pause"
android:onClick="pauseMusic"/>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="Exit"
android:onClick="exitMusic"/>
</LinearLayout>
2. Activity中实现音乐操作
package com.example.musicplayer;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private MediaPlayer mPlayer = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void playMusic(View view) {
log("playMusic");
if (mPlayer == null) {
mPlayer = MediaPlayer.create(this, R.raw.simple_music);
}
mPlayer.start();
log("playMusic done");
}
public void stopMusic(View view) {
log("stopMusic");
if (mPlayer != null) {
mPlayer.stop();
mPlayer.reset(); //重置(进度)
mPlayer.release(); //释放资源
mPlayer = null;
log("stopMusic done");
}
}
public void pauseMusic(View view) {
log("pause Music");
if (mPlayer != null && mPlayer.isPlaying()) {
mPlayer.pause();
log("pause Music done");
}
}
public void exitMusic(View view) {
log("exitMusic");
finish(); // 相当于back key返回, 音乐仍在播放,直到清理后台
log("exitMusic Music done");
}
@Override
protected void onResume() {
super.onResume();
log("onResume");
}
@Override
protected void onPause() {
super.onPause();
log("onPause");
}
@Override
protected void onStop() {
super.onStop();
log("onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
log("onDestroy");
}
private void log(String message) {
Log.d(TAG, message);
}
}
其中,
(1)simple_music 为一首歌曲,可以是mp3格式的。
(2)playMusic / stopMusic/ pauseMusic 都是调用MediaPlayer 对应的方法start /stop/pause。
(4) 需要注意的是,在playMusic 中,需要先判断 MediaPlayer 对象mPlayer 是否存在,如果不存在,才去调用 MediaPlayer.create() 创建。
(5) 在pauseMusic 中,需判断mPlayer是否为空,并且是否正在播放
(6)在stopMusic 中, 除了调用MediaPlayer.stop 外, 还要调用reset 重置音乐进度 以及 release 释放资源。
(7) 在音乐播放时, 调用exitMusic 后, 虽然 Activity 的onDestroy会被回调,但是音乐还是会在后台播放,无法再控制它,直到这个应用进程被杀死。
三、Service 实现后台播放
基于在Activity 退出后,无法控制音乐播放的问题, 可以通过Service 实现后台播放。
此时需要了解两种创建Service方式的生命周期: 使用startService()启动的, 在启动后,再调用 startService(), 仅会调用之前创建的Service的 onStartCommand,而不会再重现创建。
因此,可以通过startService() , 传入不同参数的intent, 使得Service 对应处理播放、停止、暂停音乐的操作, 把MediaPlayer 的操作放在这个Service 类中。
1. 创建MusiceService, 继承Service
package com.example.musicplayer;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.Nullable;
public class MusicService extends Service {
private static final String TAG = "MusicService";
private MediaPlayer mPlayer = null;
@Override
public void onCreate() {
super.onCreate();
log("onCreate");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int ret = super.onStartCommand(intent, flags, startId);
String action = intent.getStringExtra("action");
log("onStartCommand, action=" + action);
if (action == null) {
log("onStartCommand, return because action is null");
return ret;
}
switch (action) {
case "play":
playMusic();
break;
case "stop":
stopMusic();
break;
case "pause":
pauseMusic();
break;
default:
break;
}
return ret;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
log("onBind");
return null; //没有用到,因此返回空
}
@Override
public boolean onUnbind(Intent intent) {
log("onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
log("onDestroy");
}
private void playMusic() {
if (mPlayer == null) {
mPlayer = MediaPlayer.create(this, R.raw.simple_music);
}
if (mPlayer.isPlaying()) {
log("playMusic return because already playing");
return;
}
mPlayer.start();
log("playMusic done");
}
private void stopMusic() {
if (mPlayer == null || !mPlayer.isPlaying()) {
log("pauseMusic return because player is null or not playing ");
return;
}
mPlayer.stop();
mPlayer.reset(); //重置(进度)
mPlayer.release(); //释放资源
mPlayer = null;
log("stopMusic done");
}
private void pauseMusic() {
if (mPlayer == null || !mPlayer.isPlaying()) {
log("pauseMusic return because player is null or not playing ");
return;
}
mPlayer.pause();
log("pauseMusic done");
}
private void log(String message) {
Log.d(TAG, message);
}
}
当然,可以把播放音乐的操作放在一个子线程中,这也是官方文档建议的。
2. Activity 启动Service(通过Intent传入对应参数)
package com.example.musicplayer;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
log("onCreate");
Intent intent = new Intent(this, MusicService.class);
startService(intent);
}
//通过startService 调用, 如果service 已经启动了,则会调用onStartCommand (在onCreate已经启动了)
public void playMusic(View view) {
log("button click to playMusic");
Intent intent = new Intent(this, MusicService.class);
intent.putExtra("action", "play");
startService(intent);
}
public void stopMusic(View view) {
log("button click to stopMusic");
Intent intent = new Intent(this, MusicService.class);
intent.putExtra("action", "stop");
startService(intent);
}
public void pauseMusic(View view) {
log("button click to pauseMusic");
Intent intent = new Intent(this, MusicService.class);
intent.putExtra("action", "pause");
startService(intent);
}
public void exitMusic(View view) {
log("button click to exitMusic");
finish(); // 相当于back key返回, 音乐仍在播放,直到清理后台
log("exitMusic (exit activity only) done");
}
@Override
protected void onResume() {
super.onResume();
log("onResume");
}
@Override
protected void onPause() {
super.onPause();
log("onPause");
}
@Override
protected void onStop() {
super.onStop();
log("onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
log("onDestroy");
//不要在这里停止服务,否则界面销毁,服务也停止了(音乐停止)
// Intent intent = new Intent(this, MusicService.class);
// stopService(intent);
}
private void log(String message) {
Log.d(TAG, message);
}
}
Activity 里操作相对简单,都是通过Service去实现
3. 测试结果:
//1.第一次进入Activity, 并启动service
2020-11-28 22:11:57.628 16521-16521/com.example.musicplayer D/MainActivity: onCreate
2020-11-28 22:11:57.712 16521-16521/com.example.musicplayer D/MainActivity: onResume
2020-11-28 22:11:57.763 16521-16521/com.example.musicplayer D/MusicService: onCreate
2020-11-28 22:11:57.763 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=null
2020-11-28 22:11:57.763 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, return because action is null
//2.点击播放音乐
2020-11-28 22:12:17.786 16521-16521/com.example.musicplayer D/MainActivity: button click to playMusic
2020-11-28 22:12:17.797 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=play
2020-11-28 22:12:17.964 16521-16521/com.example.musicplayer D/MusicService: playMusic done
//再尝试点击播放音乐(不操作,因为当前已经播放)
2020-11-28 22:12:26.275 16521-16521/com.example.musicplayer D/MainActivity: button click to playMusic
2020-11-28 22:12:26.279 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=play
2020-11-28 22:12:26.279 16521-16521/com.example.musicplayer D/MusicService: playMusic return because already playing
//3.点击暂停,音乐暂停
2020-11-28 22:12:36.828 16521-16521/com.example.musicplayer D/MainActivity: button click to pauseMusic
2020-11-28 22:12:36.832 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=pause
2020-11-28 22:12:36.834 16521-16521/com.example.musicplayer D/MusicService: pauseMusic done
//4.点击播放音乐
2020-11-28 22:13:07.032 16521-16521/com.example.musicplayer D/MainActivity: button click to playMusic
2020-11-28 22:13:07.035 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=play
2020-11-28 22:13:07.038 16521-16521/com.example.musicplayer D/MusicService: playMusic done
//5.点击停止音乐,音乐停止
2020-11-28 22:13:19.317 16521-16521/com.example.musicplayer D/MainActivity: button click to stopMusic
2020-11-28 22:13:19.323 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=stop
2020-11-28 22:13:19.608 16521-16521/com.example.musicplayer D/MusicService: stopMusic done
//6.点击播放音乐
2020-11-28 22:13:55.917 16521-16521/com.example.musicplayer D/MainActivity: button click to playMusic
2020-11-28 22:13:55.923 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=play
2020-11-28 22:13:56.028 16521-16521/com.example.musicplayer D/MusicService: playMusic done
//7.点击退出音乐,此时服务没停止,音乐继续播放
2020-11-28 22:14:08.569 16521-16521/com.example.musicplayer D/MainActivity: button click to exitMusic
2020-11-28 22:14:08.584 16521-16521/com.example.musicplayer D/MainActivity: exitMusic (exit activity only) done
2020-11-28 22:14:08.589 16521-16521/com.example.musicplayer D/MainActivity: onPause
2020-11-28 22:14:09.542 16521-16521/com.example.musicplayer D/MainActivity: onStop
2020-11-28 22:14:09.550 16521-16521/com.example.musicplayer D/MainActivity: onDestroy
//8.再重现进入界面,音乐仍继续播放
2020-11-28 22:14:30.737 16521-16521/com.example.musicplayer D/MainActivity: onCreate
2020-11-28 22:14:30.752 16521-16521/com.example.musicplayer D/MainActivity: onResume
020-11-28 22:14:30.776 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=null
2020-11-28 22:14:30.776 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, return because action is null
//9.点击停止音乐,音乐停止
2020-11-28 22:15:01.957 16521-16521/com.example.musicplayer D/MainActivity: button click to stopMusic
2020-11-28 22:15:01.960 16521-16521/com.example.musicplayer D/MusicService: onStartCommand, action=stop
2020-11-28 22:15:02.242 16521-16521/com.example.musicplayer D/MusicService: stopMusic done
可以看到, 只要后台不杀掉这个进程, 这个Servcie 一直存在, 即使应用退出了。 再次进入应用,仍然能启动同一个Service