Activity 具有绑定服务的生命周期
Activity lifecycle with bound service
我正在尝试学习 Android 服务,但遇到了一些麻烦。
我有一个带有 MediaPlayer
的简单服务,可以播放来自互联网的一些流媒体。我将服务绑定到我的 MainActivity
,在服务上设置 URL 并开始播放流。这很好用。该服务立即成为带有通知的前台服务。我可以成功地从 MainActivity
更改 URL,然后开始新的流。但是,有几件事我想实现。
我不希望在 MainActivity
对用户可见时收到通知,仅当用户按下主页按钮或后退按钮时我希望服务开始在前台播放。当用户单击通知时,我希望 MainActivity
重新打开并且流不间断。这是否意味着我的 MainActivity
永远不会被摧毁?
截至目前,当我按下主页按钮时,流继续播放并且单击通知使 MainActivity
重新创建服务(流停止并再次开始播放)。我实际上希望服务永远不会停止播放,除非用户通过在多任务处理中滑动它来杀死应用程序 window(就像 Spotify 那样)。
我的服务代码如下:
public class StreamService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
private MediaPlayer player;
private final IBinder musicBind = new StreamBinder();
private StreamInfo mCurrentStream;
private static final int NOTIFY_ID = 1;
public void onCreate() {
super.onCreate();
initMusicPlayer();
}
public void initMusicPlayer() {
player = new MediaPlayer();
player.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
public class StreamBinder extends Binder {
public StreamService getService() {
return StreamService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return musicBind;
}
@Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public void playStream() {
player.reset();
try {
player.setDataSource(this, mCurrentStream.getUrl());
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
player.prepareAsync();
}
public void setStream(StreamInfo stream) {
mCurrentStream = stream;
}
@Override
public void onCompletion(MediaPlayer mp) {
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
Intent notIntent = new Intent(this, MainActivity.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0,
notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.ic_action_navigation_chevron_right)
.setTicker(mCurrentStream.getTitle())
.setOngoing(true)
.setContentTitle("Playing")
.setContentText(mCurrentStream.getTitle());
Notification not = builder.build();
startForeground(NOTIFY_ID, not);
}
@Override
public void onDestroy() {
stopForeground(true);
}
}
还有我的MainActivity
:
public class MainActivity extends AppCompatActivity implements ServiceConnection {
private static final String TAG = MainActivity.class.getSimpleName();
private StreamService mStreamService;
private boolean musicBound = false;
private Intent playIntent;
@Override
protected void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(this, StreamService.class);
bindService(playIntent, this, MainActivity.BIND_AUTO_CREATE);
startService(playIntent);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startStreaming();
}
private void startStreaming() {
mStreamService.setStream(getSelectedStream());
mStreamService.playStream();
}
@Override
protected void onDestroy() {
stopService(playIntent);
mStreamService = null;
super.onDestroy();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
StreamService.StreamBinder binder = (StreamService.StreamBinder) service;
mStreamService = binder.getService();
musicBound = true;
startStreaming();
}
@Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
public StreamInfo getSelectedStream() {
//Returns some stream from a widget
}
}
Of course there is a widget in my MainActivity
with a listener and when the selection changes the startStreaming()
method is called.
任何人都可以指出正确的方向吗?
I do not want a notification when the MainActivity is visible to the
user, only when the user presses the home button or back button I want
the service to start playing in the foreground.
在您的服务中保留一个布尔标志以指示是否绑定了某些内容。在显示通知之前检查标志。例如:
@Override
public IBinder onBind(Intent intent) {
mBound = true;
hideNotifications();
return musicBind;
}
@Override
public void onRebind(Intent intent) {
mBound = true;
hideNotifications();
}
When the user clicks
on the notification I want the MainActivity to reopen and the stream
uninterrupted. Does this mean my MainActivity can never be destroyed?
您需要解绑您的activityonStop()
。
As of now, when I press the home button the stream keeps playing and
clicking the notification makes the MainActivity recreate the service
(the stream stops and starts to play again). I actually want the
Service to never stop playing unless the user kills the app by swiping
it in the multitasking window (like Spotify does it).
您的 Activity 的 onStart()
检查服务是否 运行 并重新绑定到它而不是重新创建它。如果不是 运行 - 创建并绑定。
我正在尝试学习 Android 服务,但遇到了一些麻烦。
我有一个带有 MediaPlayer
的简单服务,可以播放来自互联网的一些流媒体。我将服务绑定到我的 MainActivity
,在服务上设置 URL 并开始播放流。这很好用。该服务立即成为带有通知的前台服务。我可以成功地从 MainActivity
更改 URL,然后开始新的流。但是,有几件事我想实现。
我不希望在 MainActivity
对用户可见时收到通知,仅当用户按下主页按钮或后退按钮时我希望服务开始在前台播放。当用户单击通知时,我希望 MainActivity
重新打开并且流不间断。这是否意味着我的 MainActivity
永远不会被摧毁?
截至目前,当我按下主页按钮时,流继续播放并且单击通知使 MainActivity
重新创建服务(流停止并再次开始播放)。我实际上希望服务永远不会停止播放,除非用户通过在多任务处理中滑动它来杀死应用程序 window(就像 Spotify 那样)。
我的服务代码如下:
public class StreamService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {
private MediaPlayer player;
private final IBinder musicBind = new StreamBinder();
private StreamInfo mCurrentStream;
private static final int NOTIFY_ID = 1;
public void onCreate() {
super.onCreate();
initMusicPlayer();
}
public void initMusicPlayer() {
player = new MediaPlayer();
player.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}
public class StreamBinder extends Binder {
public StreamService getService() {
return StreamService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return musicBind;
}
@Override
public boolean onUnbind(Intent intent) {
player.stop();
player.release();
return false;
}
public void playStream() {
player.reset();
try {
player.setDataSource(this, mCurrentStream.getUrl());
} catch (Exception e) {
Log.e("MUSIC SERVICE", "Error setting data source", e);
}
player.prepareAsync();
}
public void setStream(StreamInfo stream) {
mCurrentStream = stream;
}
@Override
public void onCompletion(MediaPlayer mp) {
}
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
Intent notIntent = new Intent(this, MainActivity.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0,
notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.ic_action_navigation_chevron_right)
.setTicker(mCurrentStream.getTitle())
.setOngoing(true)
.setContentTitle("Playing")
.setContentText(mCurrentStream.getTitle());
Notification not = builder.build();
startForeground(NOTIFY_ID, not);
}
@Override
public void onDestroy() {
stopForeground(true);
}
}
还有我的MainActivity
:
public class MainActivity extends AppCompatActivity implements ServiceConnection {
private static final String TAG = MainActivity.class.getSimpleName();
private StreamService mStreamService;
private boolean musicBound = false;
private Intent playIntent;
@Override
protected void onStart() {
super.onStart();
if (playIntent == null) {
playIntent = new Intent(this, StreamService.class);
bindService(playIntent, this, MainActivity.BIND_AUTO_CREATE);
startService(playIntent);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startStreaming();
}
private void startStreaming() {
mStreamService.setStream(getSelectedStream());
mStreamService.playStream();
}
@Override
protected void onDestroy() {
stopService(playIntent);
mStreamService = null;
super.onDestroy();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
StreamService.StreamBinder binder = (StreamService.StreamBinder) service;
mStreamService = binder.getService();
musicBound = true;
startStreaming();
}
@Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
public StreamInfo getSelectedStream() {
//Returns some stream from a widget
}
}
Of course there is a widget in my MainActivity
with a listener and when the selection changes the startStreaming()
method is called.
任何人都可以指出正确的方向吗?
I do not want a notification when the MainActivity is visible to the user, only when the user presses the home button or back button I want the service to start playing in the foreground.
在您的服务中保留一个布尔标志以指示是否绑定了某些内容。在显示通知之前检查标志。例如:
@Override
public IBinder onBind(Intent intent) {
mBound = true;
hideNotifications();
return musicBind;
}
@Override
public void onRebind(Intent intent) {
mBound = true;
hideNotifications();
}
When the user clicks on the notification I want the MainActivity to reopen and the stream uninterrupted. Does this mean my MainActivity can never be destroyed?
您需要解绑您的activityonStop()
。
您的 Activity 的As of now, when I press the home button the stream keeps playing and clicking the notification makes the MainActivity recreate the service (the stream stops and starts to play again). I actually want the Service to never stop playing unless the user kills the app by swiping it in the multitasking window (like Spotify does it).
onStart()
检查服务是否 运行 并重新绑定到它而不是重新创建它。如果不是 运行 - 创建并绑定。