在上一篇 Android-bindService本地服务-音乐播放-上,博客中是不能在后台中播放到,这次博客增加了一个后台播放
通常情况下,Activity切换到后台,Service提升到前台进程,既然Service提升到前台进程就会有一个通知。
Activity ---> moveTaskToBack(true);
Service ---> startForeground(1, builder1.getNotification());
进程优先级别:前台进程,可视进程,服务进程,后台进程,空进程 (前台进程是最稳定,系统内存不足是先回收 空进程)
为什么要把服务Service提升为前台进程,在内存不足时,前台进程不会那么容易被系统回收
把 服务进程 提升到 前台进程 会自动绑定通知
UI相关,当在播放当过程中点击返回键,就需要告诉用户是否在后台运行
点击后台播放,Activity就会被切换到后台,想要再次启动APP就可以点击通知进入:
由于这个MainActivity会被多次启动,为了保证单任务,需要设置启动模式:android:launchMode="singleTask"
<activity android:name=".MainActivity5" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
定义Binder扩展接口:
package liudeli.service1.service.inter; import android.media.MediaPlayer; public interface IMusicPlay { /** * 播放音乐 * @param musicPath 音乐文件的路径 */ void playMusic(String musicPath); /** * 暂停播放 */ void pausedPlay(); /** * 继续播放 */ void continuePlay(); /** * 停止播放 */ void stopPlay(); /** * 让Activity可以获取到服务使用到MediaPlayer * @return */ MediaPlayer getMediaPlayer(); }
Service控制播放:
package liudeli.service1.service; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaPlayer; import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.support.annotation.RequiresApi; import android.util.Log; import java.io.IOException; import liudeli.service1.MainActivity5; import liudeli.service1.R; import liudeli.service1.service.inter.IMusicPlay; public class MyService5 extends Service { private final String TAG = MyService5.class.getSimpleName(); @RequiresApi(api = Build.VERSION_CODES.O) @Override public void onCreate() { super.onCreate(); /** * 进程优先级别:前台进程,可视进程,服务进程,后台进程,空进程 (前台进程是最稳定,系统内存不足是先回收 空进程) * * 为什么要把服务Service提升为前台进程,在内存不足时,前台进程不会那么容易被系统回收 * * 把 服务进程 提升到 前台进程 会自动绑定通知 */ // 需要用到通知,用户点击通知栏,就计划APP-->Activity // 这是以前到写法,已经过时 /*Notification notification = new Notification(R.mipmap.ic_launcher, "我的音乐播放器", System.currentTimeMillis());*/ // 设置事件信息,点击通知可以跳转到指定Activity Intent intent = new Intent(this, MainActivity5.class); // 设置事件信息,点击通知可以跳转到指定Activity NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // 设置通知显示相关信息 Notification.Builder builder1 = new Notification.Builder(this); builder1.setSmallIcon(R.mipmap.ic_launcher); //设置图标 /*builder1.setTicker("显示第二个通知");*/ builder1.setContentTitle("播放中"); //设置标题 builder1.setContentText("我的音乐播放器"); //消息内容 builder1.setWhen(System.currentTimeMillis()); //发送时间 builder1.setDefaults(Notification.DEFAULT_ALL); //设置默认的提示音,振动方式,灯光 builder1.setAutoCancel(true);//打开程序后图标消失 // 延时意图,所谓延时意图就是不是马上执行,需要用户去点击后才执行,其实就是对Intent对封装 PendingIntent pendingIntent =PendingIntent.getActivity(this, 0, intent, 0); builder1.setContentIntent(pendingIntent); Notification notification1 = builder1.build(); notificationManager.notify(124, notification1); // 通过通知管理器发送通知 // id=通知到唯一标示 notification=通知 startForeground(1, builder1.getNotification()); } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "绑定成功"); return new PlayMusicBinder(); } private MediaPlayer mediaPlayer; /** * 增强版Binder,扩展出播放音乐🎵行为 */ class PlayMusicBinder extends Binder implements IMusicPlay { public PlayMusicBinder() { mediaPlayer = new MediaPlayer(); } /** * 播放音乐 * * @param musicPath 音乐文件的路径 */ @Override public void playMusic(String musicPath) { try { mediaPlayer.reset(); mediaPlayer.setDataSource(musicPath); mediaPlayer.prepare(); mediaPlayer.start(); } catch (IOException e) { e.printStackTrace(); } } /** * 暂停播放 */ @Override public void pausedPlay() { mediaPlayer.pause(); } /** * 继续播放 */ @Override public void continuePlay() { mediaPlayer.start(); } /** * 停止播放 */ @Override public void stopPlay() { mediaPlayer.stop(); } /** * 让Activity可以获取到服务使用到MediaPlayer * * @return */ @Override public MediaPlayer getMediaPlayer() { return mediaPlayer; } } @Override public boolean onUnbind(Intent intent) { Log.d(TAG, "解绑成功"); // 为什么解绑服务了,音乐还在播放,应该MediaPlay内部是一个服务 if (mediaPlayer != null) { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } mediaPlayer.release(); // 释放硬件播放资源 } return super.onUnbind(intent); } }
MainActivity调用Service代码:
package liudeli.service1; import android.app.Activity; import android.app.AlertDialog; import android.content.ComponentName; import android.content.DialogInterface; import android.content.Intent; import android.content.ServiceConnection; import android.media.MediaPlayer; import android.os.Bundle; import android.os.Environment; import android.os.IBinder; import android.view.KeyEvent; import android.view.View; import android.widget.Button; import android.widget.Toast; import liudeli.service1.service.MyService5; import liudeli.service1.service.inter.IMusicPlay; public class MainActivity5 extends Activity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main5); initView(); initListener(); } @Override protected void onStart() { super.onStart(); // 绑定服务 bindService(new Intent(this, MyService5.class), connection, BIND_AUTO_CREATE); } private Button btPlayMusic; private Button btPausedContinue; private Button btStop; private void initView() { btPlayMusic = findViewById(R.id.bt_play_music); btPausedContinue = findViewById(R.id.bt_paused_continue); btStop = findViewById(R.id.bt_stop); } private void initListener() { btPlayMusic.setOnClickListener(this); btPausedContinue.setOnClickListener(this); btStop.setOnClickListener(this); } private IMusicPlay iMusicPlay; private ServiceConnection connection = new ServiceConnection() { /** * 连接到服务 * @param name * @param service */ @Override public void onServiceConnected(ComponentName name, IBinder service) { iMusicPlay = (IMusicPlay) service; } /** * 断开连接 * @param name */ @Override public void onServiceDisconnected(ComponentName name) { } }; @Override protected void onDestroy() { super.onDestroy(); // 解绑服务:注意bindService后 必须要解绑服务,否则会报 连接资源异常 if (null != connection) { unbindService(connection); } } // 音乐文件到路径 private final String MUSIC_PATH = Environment.getExternalStorageDirectory() + "/cjyy.mp3"; @Override public void onClick(View v) { if (iMusicPlay == null) { Toast.makeText(this, "音乐播放服务连接失败", Toast.LENGTH_LONG).show(); return; } switch (v.getId()) { case R.id.bt_play_music: iMusicPlay.playMusic(MUSIC_PATH); break; case R.id.bt_paused_continue: if ("暂停播放".equals(btPausedContinue.getText().toString())) { btPausedContinue.setText("继续播放"); iMusicPlay.pausedPlay(); } else { btPausedContinue.setText("暂停播放"); iMusicPlay.continuePlay(); } break; case R.id.bt_stop: iMusicPlay.stopPlay(); break; default: break; } } /** * 用户按返回键,系统会调用到此方法 * @param keyCode * @param event * @return */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { super.onKeyDown(keyCode, event); // 判断是否在播放,如果在播放中,才告知用户 弹出对话框 if (iMusicPlay == null) { return true; } MediaPlayer mediaPlayer = iMusicPlay.getMediaPlayer(); if(mediaPlayer.isPlaying()) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: showAlertDialog(); break; } } return true; } /** * 弹出对话框 */ private void showAlertDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity5.this); builder.setTitle("提示"); builder.setMessage("确定要关闭音乐播放?"); builder.setNegativeButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }); builder.setNeutralButton("后台播放", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 既然是后台播放,就是要把当前Activity切换到后台 moveTaskToBack(true); } }); builder.setPositiveButton("取消", null); AlertDialog dialog = builder.create(); dialog.show(); } }