在上一篇 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(); } }