activity 被销毁时如何显示来自服务的通知
How to display a notification from service when activity gets destroyed
这里是我从 Activity 开始服务的场景,它在后台播放音乐。当我按下 activity 上的后退按钮时,activity 被摧毁。但服务仍在后台 运行。我想在这个 activity 被销毁时向用户显示通知,以便他们可以 play/pause/stop 通知中的音频。但我不想在服务启动时显示通知。
下面是我的 activity 代码:
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class PlayBackgroundAudioActivity extends AppCompatActivity {
private AudioServiceBinder audioServiceBinder = null;
private Handler audioProgressUpdateHandler = null;
// Show played audio progress.
private ProgressBar backgroundAudioProgress;
private TextView audioFileUrlTextView;
// This service connection object is the bridge between activity and background service.
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// Cast and assign background service's onBind method returned iBander object.
audioServiceBinder = (AudioServiceBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_background_audio);
setTitle("dev2qa.com - Play Audio Use Background Service");
// Bind background audio service when activity is created.
bindAudioService();
final String audioFileUrl = "http://www.dev2qa.com/demo/media/test.mp3";
backgroundAudioProgress = (ProgressBar)findViewById(R.id.play_audio_in_background_service_progressbar);
// Get audio file url textview.
audioFileUrlTextView = (TextView)findViewById(R.id.audio_file_url_text_view);
if(audioFileUrlTextView != null)
{
// Show web audio file url in the text view.
audioFileUrlTextView.setText("Audio File Url. \r\n" + audioFileUrl);
}
// Click this button to start play audio in a background service.
Button startBackgroundAudio = (Button)findViewById(R.id.start_audio_in_background);
startBackgroundAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Set web audio file url
audioServiceBinder.setAudioFileUrl(audioFileUrl);
// Web audio is a stream audio.
audioServiceBinder.setStreamAudio(true);
// Set application context.
audioServiceBinder.setContext(getApplicationContext());
// Initialize audio progress bar updater Handler object.
createAudioProgressbarUpdater();
audioServiceBinder.setAudioProgressUpdateHandler(audioProgressUpdateHandler);
// Start audio in background service.
audioServiceBinder.startAudio();
backgroundAudioProgress.setVisibility(ProgressBar.VISIBLE);
Toast.makeText(getApplicationContext(), "Start play web audio file.", Toast.LENGTH_LONG).show();
}
});
// Click this button to pause the audio played in background service.
Button pauseBackgroundAudio = (Button)findViewById(R.id.pause_audio_in_background);
pauseBackgroundAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
audioServiceBinder.pauseAudio();
Toast.makeText(getApplicationContext(), "Play web audio file is paused.", Toast.LENGTH_LONG).show();
}
});
// Click this button to stop the media player in background service.
Button stopBackgroundAudio = (Button)findViewById(R.id.stop_audio_in_background);
stopBackgroundAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
audioServiceBinder.stopAudio();
backgroundAudioProgress.setVisibility(ProgressBar.INVISIBLE);
Toast.makeText(getApplicationContext(), "Stop play web audio file.", Toast.LENGTH_LONG).show();
}
});
}
// Bind background service with caller activity. Then this activity can use
// background service's AudioServiceBinder instance to invoke related methods.
private void bindAudioService()
{
if(audioServiceBinder == null) {
Intent intent = new Intent(PlayBackgroundAudioActivity.this, AudioService.class);
// Below code will invoke serviceConnection's onServiceConnected method.
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
// Unbound background audio service with caller activity.
private void unBoundAudioService()
{
if(audioServiceBinder != null) {
unbindService(serviceConnection);
}
}
@Override
protected void onDestroy() {
// Unbound background audio service when activity is destroyed.
unBoundAudioService();
super.onDestroy();
}
// Create audio player progressbar updater.
// This updater is used to update progressbar to reflect audio play process.
private void createAudioProgressbarUpdater()
{
/* Initialize audio progress handler. */
if(audioProgressUpdateHandler==null) {
audioProgressUpdateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// The update process message is sent from AudioServiceBinder class's thread object.
if (msg.what == audioServiceBinder.UPDATE_AUDIO_PROGRESS_BAR) {
if( audioServiceBinder != null) {
// Calculate the percentage.
int currProgress =audioServiceBinder.getAudioProgress();
// Update progressbar. Make the value 10 times to show more clear UI change.
backgroundAudioProgress.setProgress(currProgress*10);
}
}
}
};
}
}
@Override
public void onBackPressed() {
startActivity(new Intent(PlayBackgroundAudioActivity.this,ForeGroundService.class));
finish();
}
}
下面是我的服务代码:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class AudioService extends Service {
private AudioServiceBinder audioServiceBinder = new AudioServiceBinder();
public AudioService() {
}
@Override
public IBinder onBind(Intent intent) {
return audioServiceBinder;
}
}
下面是我的音频活页夹class:
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import java.io.IOException;
/**
* Created by Jerry on 2/15/2018.
*/
public class AudioServiceBinder extends Binder {
// Save local audio file uri ( local storage file. ).
private Uri audioFileUri = null;
// Save web audio file url.
private String audioFileUrl = "";
// Check if stream audio.
private boolean streamAudio = false;
// Media player that play audio.
private MediaPlayer audioPlayer = null;
// Caller activity context, used when play local audio file.
private Context context = null;
// This Handler object is a reference to the caller activity's Handler.
// In the caller activity's handler, it will update the audio play progress.
private Handler audioProgressUpdateHandler;
// This is the message signal that inform audio progress updater to update audio progress.
public final int UPDATE_AUDIO_PROGRESS_BAR = 1;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public String getAudioFileUrl() {
return audioFileUrl;
}
public void setAudioFileUrl(String audioFileUrl) {
this.audioFileUrl = audioFileUrl;
}
public boolean isStreamAudio() {
return streamAudio;
}
public void setStreamAudio(boolean streamAudio) {
this.streamAudio = streamAudio;
}
public Uri getAudioFileUri() {
return audioFileUri;
}
public void setAudioFileUri(Uri audioFileUri) {
this.audioFileUri = audioFileUri;
}
public Handler getAudioProgressUpdateHandler() {
return audioProgressUpdateHandler;
}
public void setAudioProgressUpdateHandler(Handler audioProgressUpdateHandler) {
this.audioProgressUpdateHandler = audioProgressUpdateHandler;
}
// Start play audio.
public void startAudio()
{
initAudioPlayer();
if(audioPlayer!=null) {
audioPlayer.start();
}
}
// Pause playing audio.
public void pauseAudio()
{
if(audioPlayer!=null) {
audioPlayer.pause();
}
}
// Stop play audio.
public void stopAudio()
{
if(audioPlayer!=null) {
audioPlayer.stop();
destroyAudioPlayer();
}
}
// Initialise audio player.
private void initAudioPlayer()
{
try {
if (audioPlayer == null) {
audioPlayer = new MediaPlayer();
if (!TextUtils.isEmpty(getAudioFileUrl())) {
if (isStreamAudio()) {
audioPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
audioPlayer.setDataSource(getAudioFileUrl());
} else {
audioPlayer.setDataSource(getContext(), getAudioFileUri());
}
audioPlayer.prepare();
// This thread object will send update audio progress message to caller activity every 1 second.
Thread updateAudioProgressThread = new Thread()
{
@Override
public void run() {
while(true)
{
// Create update audio progress message.
Message updateAudioProgressMsg = new Message();
updateAudioProgressMsg.what = UPDATE_AUDIO_PROGRESS_BAR;
// Send the message to caller activity's update audio prgressbar Handler object.
audioProgressUpdateHandler.sendMessage(updateAudioProgressMsg);
// Sleep one second.
try {
Thread.sleep(1000);
}catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
}
};
// Run above thread object.
updateAudioProgressThread.start();
}
}catch(IOException ex)
{
ex.printStackTrace();
}
}
// Destroy audio player.
private void destroyAudioPlayer()
{
if(audioPlayer!=null)
{
if(audioPlayer.isPlaying())
{
audioPlayer.stop();
}
audioPlayer.release();
audioPlayer = null;
}
}
// Return current audio play position.
public int getCurrentAudioPosition()
{
int ret = 0;
if(audioPlayer != null)
{
ret = audioPlayer.getCurrentPosition();
}
return ret;
}
// Return total audio file duration.
public int getTotalAudioDuration()
{
int ret = 0;
if(audioPlayer != null)
{
ret = audioPlayer.getDuration();
}
return ret;
}
// Return current audio player progress value.
public int getAudioProgress()
{
int ret = 0;
int currAudioPosition = getCurrentAudioPosition();
int totalAudioDuration = getTotalAudioDuration();
if(totalAudioDuration > 0) {
ret = (currAudioPosition * 100) / totalAudioDuration;
}
return ret;
}
}
基本上您应该重新考虑显示通知的时间。自从 onDestroy method of an activity may not be always called, I would prefer to use another point when to display your notification. You should be also aware of the new restrictions regarding background services 以来 android 8 (API 26),如果你不明确地 标记 它们为前台服务,那么它们可能会被杀死在应用程序处于后台时按 OS。
因此,为了您的目的,可以选择使用方法 startForeground with a notification id and call it when your activity is going to the background (e.g. at onStop), then it will display the notification (which is the requirement for keeping the service in the foreground). If you decide to go back to your activity after some time, you may call stopForeground 停止前台模式并关闭您的通知。
这里是我从 Activity 开始服务的场景,它在后台播放音乐。当我按下 activity 上的后退按钮时,activity 被摧毁。但服务仍在后台 运行。我想在这个 activity 被销毁时向用户显示通知,以便他们可以 play/pause/stop 通知中的音频。但我不想在服务启动时显示通知。
下面是我的 activity 代码:
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
public class PlayBackgroundAudioActivity extends AppCompatActivity {
private AudioServiceBinder audioServiceBinder = null;
private Handler audioProgressUpdateHandler = null;
// Show played audio progress.
private ProgressBar backgroundAudioProgress;
private TextView audioFileUrlTextView;
// This service connection object is the bridge between activity and background service.
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
// Cast and assign background service's onBind method returned iBander object.
audioServiceBinder = (AudioServiceBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_play_background_audio);
setTitle("dev2qa.com - Play Audio Use Background Service");
// Bind background audio service when activity is created.
bindAudioService();
final String audioFileUrl = "http://www.dev2qa.com/demo/media/test.mp3";
backgroundAudioProgress = (ProgressBar)findViewById(R.id.play_audio_in_background_service_progressbar);
// Get audio file url textview.
audioFileUrlTextView = (TextView)findViewById(R.id.audio_file_url_text_view);
if(audioFileUrlTextView != null)
{
// Show web audio file url in the text view.
audioFileUrlTextView.setText("Audio File Url. \r\n" + audioFileUrl);
}
// Click this button to start play audio in a background service.
Button startBackgroundAudio = (Button)findViewById(R.id.start_audio_in_background);
startBackgroundAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Set web audio file url
audioServiceBinder.setAudioFileUrl(audioFileUrl);
// Web audio is a stream audio.
audioServiceBinder.setStreamAudio(true);
// Set application context.
audioServiceBinder.setContext(getApplicationContext());
// Initialize audio progress bar updater Handler object.
createAudioProgressbarUpdater();
audioServiceBinder.setAudioProgressUpdateHandler(audioProgressUpdateHandler);
// Start audio in background service.
audioServiceBinder.startAudio();
backgroundAudioProgress.setVisibility(ProgressBar.VISIBLE);
Toast.makeText(getApplicationContext(), "Start play web audio file.", Toast.LENGTH_LONG).show();
}
});
// Click this button to pause the audio played in background service.
Button pauseBackgroundAudio = (Button)findViewById(R.id.pause_audio_in_background);
pauseBackgroundAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
audioServiceBinder.pauseAudio();
Toast.makeText(getApplicationContext(), "Play web audio file is paused.", Toast.LENGTH_LONG).show();
}
});
// Click this button to stop the media player in background service.
Button stopBackgroundAudio = (Button)findViewById(R.id.stop_audio_in_background);
stopBackgroundAudio.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
audioServiceBinder.stopAudio();
backgroundAudioProgress.setVisibility(ProgressBar.INVISIBLE);
Toast.makeText(getApplicationContext(), "Stop play web audio file.", Toast.LENGTH_LONG).show();
}
});
}
// Bind background service with caller activity. Then this activity can use
// background service's AudioServiceBinder instance to invoke related methods.
private void bindAudioService()
{
if(audioServiceBinder == null) {
Intent intent = new Intent(PlayBackgroundAudioActivity.this, AudioService.class);
// Below code will invoke serviceConnection's onServiceConnected method.
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
// Unbound background audio service with caller activity.
private void unBoundAudioService()
{
if(audioServiceBinder != null) {
unbindService(serviceConnection);
}
}
@Override
protected void onDestroy() {
// Unbound background audio service when activity is destroyed.
unBoundAudioService();
super.onDestroy();
}
// Create audio player progressbar updater.
// This updater is used to update progressbar to reflect audio play process.
private void createAudioProgressbarUpdater()
{
/* Initialize audio progress handler. */
if(audioProgressUpdateHandler==null) {
audioProgressUpdateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// The update process message is sent from AudioServiceBinder class's thread object.
if (msg.what == audioServiceBinder.UPDATE_AUDIO_PROGRESS_BAR) {
if( audioServiceBinder != null) {
// Calculate the percentage.
int currProgress =audioServiceBinder.getAudioProgress();
// Update progressbar. Make the value 10 times to show more clear UI change.
backgroundAudioProgress.setProgress(currProgress*10);
}
}
}
};
}
}
@Override
public void onBackPressed() {
startActivity(new Intent(PlayBackgroundAudioActivity.this,ForeGroundService.class));
finish();
}
}
下面是我的服务代码:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class AudioService extends Service {
private AudioServiceBinder audioServiceBinder = new AudioServiceBinder();
public AudioService() {
}
@Override
public IBinder onBind(Intent intent) {
return audioServiceBinder;
}
}
下面是我的音频活页夹class:
import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import java.io.IOException;
/**
* Created by Jerry on 2/15/2018.
*/
public class AudioServiceBinder extends Binder {
// Save local audio file uri ( local storage file. ).
private Uri audioFileUri = null;
// Save web audio file url.
private String audioFileUrl = "";
// Check if stream audio.
private boolean streamAudio = false;
// Media player that play audio.
private MediaPlayer audioPlayer = null;
// Caller activity context, used when play local audio file.
private Context context = null;
// This Handler object is a reference to the caller activity's Handler.
// In the caller activity's handler, it will update the audio play progress.
private Handler audioProgressUpdateHandler;
// This is the message signal that inform audio progress updater to update audio progress.
public final int UPDATE_AUDIO_PROGRESS_BAR = 1;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public String getAudioFileUrl() {
return audioFileUrl;
}
public void setAudioFileUrl(String audioFileUrl) {
this.audioFileUrl = audioFileUrl;
}
public boolean isStreamAudio() {
return streamAudio;
}
public void setStreamAudio(boolean streamAudio) {
this.streamAudio = streamAudio;
}
public Uri getAudioFileUri() {
return audioFileUri;
}
public void setAudioFileUri(Uri audioFileUri) {
this.audioFileUri = audioFileUri;
}
public Handler getAudioProgressUpdateHandler() {
return audioProgressUpdateHandler;
}
public void setAudioProgressUpdateHandler(Handler audioProgressUpdateHandler) {
this.audioProgressUpdateHandler = audioProgressUpdateHandler;
}
// Start play audio.
public void startAudio()
{
initAudioPlayer();
if(audioPlayer!=null) {
audioPlayer.start();
}
}
// Pause playing audio.
public void pauseAudio()
{
if(audioPlayer!=null) {
audioPlayer.pause();
}
}
// Stop play audio.
public void stopAudio()
{
if(audioPlayer!=null) {
audioPlayer.stop();
destroyAudioPlayer();
}
}
// Initialise audio player.
private void initAudioPlayer()
{
try {
if (audioPlayer == null) {
audioPlayer = new MediaPlayer();
if (!TextUtils.isEmpty(getAudioFileUrl())) {
if (isStreamAudio()) {
audioPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
audioPlayer.setDataSource(getAudioFileUrl());
} else {
audioPlayer.setDataSource(getContext(), getAudioFileUri());
}
audioPlayer.prepare();
// This thread object will send update audio progress message to caller activity every 1 second.
Thread updateAudioProgressThread = new Thread()
{
@Override
public void run() {
while(true)
{
// Create update audio progress message.
Message updateAudioProgressMsg = new Message();
updateAudioProgressMsg.what = UPDATE_AUDIO_PROGRESS_BAR;
// Send the message to caller activity's update audio prgressbar Handler object.
audioProgressUpdateHandler.sendMessage(updateAudioProgressMsg);
// Sleep one second.
try {
Thread.sleep(1000);
}catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
}
};
// Run above thread object.
updateAudioProgressThread.start();
}
}catch(IOException ex)
{
ex.printStackTrace();
}
}
// Destroy audio player.
private void destroyAudioPlayer()
{
if(audioPlayer!=null)
{
if(audioPlayer.isPlaying())
{
audioPlayer.stop();
}
audioPlayer.release();
audioPlayer = null;
}
}
// Return current audio play position.
public int getCurrentAudioPosition()
{
int ret = 0;
if(audioPlayer != null)
{
ret = audioPlayer.getCurrentPosition();
}
return ret;
}
// Return total audio file duration.
public int getTotalAudioDuration()
{
int ret = 0;
if(audioPlayer != null)
{
ret = audioPlayer.getDuration();
}
return ret;
}
// Return current audio player progress value.
public int getAudioProgress()
{
int ret = 0;
int currAudioPosition = getCurrentAudioPosition();
int totalAudioDuration = getTotalAudioDuration();
if(totalAudioDuration > 0) {
ret = (currAudioPosition * 100) / totalAudioDuration;
}
return ret;
}
}
基本上您应该重新考虑显示通知的时间。自从 onDestroy method of an activity may not be always called, I would prefer to use another point when to display your notification. You should be also aware of the new restrictions regarding background services 以来 android 8 (API 26),如果你不明确地 标记 它们为前台服务,那么它们可能会被杀死在应用程序处于后台时按 OS。
因此,为了您的目的,可以选择使用方法 startForeground with a notification id and call it when your activity is going to the background (e.g. at onStop), then it will display the notification (which is the requirement for keeping the service in the foreground). If you decide to go back to your activity after some time, you may call stopForeground 停止前台模式并关闭您的通知。