Android foreground service which can be bound

2019-02-03 17:26发布

问题:

What is the right way of doing foreground service which I can later bind to it? I have followed Android API demo which includes an example how to create foreground service. There is no example about start service in bind to it at same time.

I want to see one good example of music player service with activity "bound" to it.

Is there any?

I want to do something like:

  • When a program is first (first means that service isn't started yet) started I want to start foreground service which do all the job. User Interface (activity) is just to control that job
  • If user presses home button service must stay up and running (and notification in bar must be present)
  • Now if user click on notification in notification bar activity should start and bind to service (or something like that, the right way) and user takes control over the job.
  • If activity is active and user presses back button activity should be destroyed and also service should be destroyed.

What methods do I have to override to accomplish this task? What is the Android way of doing this?

回答1:

before froyo there was setForeground(true) in Service which was easy, but also easy to abuse.

Now there is startForeGround services which requires a notification to be activated (so the user can see there is a foregroundservice running).

i made this class to control it:

public class NotificationUpdater {
    public static void turnOnForeground(Service srv,int notifID,NotificationManager mNotificationManager,Notification notif) {
        try {
            Method m = Service.class.getMethod("startForeground", new Class[] {int.class, Notification.class});
            m.invoke(srv, notifID, notif);
        } catch (Exception e) {
            srv.setForeground(true);
            mNotificationManager.notify(notifID, notif);
        }
    } 

    public static void turnOffForeground(Service srv,int notifID,NotificationManager mNotificationManager) {
        try {
            Method m = Service.class.getMethod("stopForeground", new Class[] {boolean.class});
            m.invoke(srv, true);
        } catch (Exception e) {
            srv.setForeground(false);
            mNotificationManager.cancel(notifID);
        }
    }
}

then for my media player this update the notification - note the foreground service is only required while media is playing and should be left on after it stops, it a bad practice.

private void updateNotification(){
    boolean playing = ((mFGPlayerBean.getState()==MediaPlayerWrapper.STATE_PLAYING) || 
                        (mBGPlayerBean.getState()==MediaPlayerWrapper.STATE_PLAYING));
    if (playing) {
        Notification notification = getNotification();
        NotificationUpdater.turnOnForeground(this,Globals.NOTIFICATION_ID_MP,mNotificationManager,notification);
    } else {
        NotificationUpdater.turnOffForeground(this,Globals.NOTIFICATION_ID_MP,mNotificationManager);
    }
}

as for binding - you just bind in the normal way in your activity onStart you just make a bindService call as you would bind to any service (it doesnt matter weather it foreground or not)

MediaPlayerService mpService=null;
@Override
protected void onEWCreate(Bundle savedInstanceState) {
     Intent intent = new Intent(this, MediaPlayerService.class);
     startService(intent);
}

@Override
protected void onStart() {
    // assume startService has been called already
    if (mpService==null) {
        Intent intentBind = new Intent(this, MediaPlayerService.class);
        bindService(intentBind, mConnection, 0);
    }
}

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        mpService = ((MediaPlayerService.MediaBinder)service).getService();
    }

    public void onServiceDisconnected(ComponentName className) {
        mpService = null;
    }
};


回答2:

To accomplish asked task the only thing I have to do was add following property to AndroidManifest.xml to my activity definition

android:launchMode="singleTop"

That was it.

Regards