In the application that I am developing there is a bound service, that uses TCP socket connection. The requirement for the service is to keep the socket connection for a while after last Activity bound to the service is shut down, say for 1 minute. This is to avoid unneeded socket reconnections when another activity connects to the service just after one has unbound from it.
I have searched and found similar issue ( Service, Rebind or not bound at all? ) suggesting using started service, but current application architecture uses bound connections, and I would not like to redesign if it is not needed. Hence I am looking for other options.
My question is, can I somehow postpone bound service destruction or the only good approach is to rewrite the communication to the service using intents, thus converting it to started service?
public class SocketService extends Service {
private static final String LOG_TAG = SocketService.class.getSimpleName();
#Override
public final IBinder onBind(final Intent intent) {
Log.d(LOG_TAG, "onBind()");
return new LocalBinder<SocketService>(this);
}
#Override
public boolean onUnbind(Intent intent) {
Log.d(LOG_TAG, "onUnbind");
return super.onUnbind(intent);
}
#Override
public void onCreate() {
Log.d(LOG_TAG, "onCreate");
// create socket connection here
// ...
//
super.onCreate();
}
#Override
public void onDestroy() {
Log.d(LOG_TAG, "onDestroy");
// close socket connection
// ...
//
super.onDestroy();
}
// Other socket sending and receiving logic
// ...
//
}
Thanks in advance for you time.
Keep the First Activity you launch bound to the service , don't destroy it when you launch another one .
When you want to exit the application from your current activity send a signal to the service which will notify all activities to close .
OnCreate is only called on the first bind to the service (when the service doesn't exist ) , the bind that follows should not trigger the onCreate function .
Related
First question here, but I've been around for a while.
What do I have:
I'm building an Android app which plays audio streams and online playlists. Everything is working fine now, but I'm having issues in communicating with my service.
The music is playing in a Service, started with startForeground, so it doesn't gets killed.
I need to communicate from my activity with the service, for getting the track name, image, and a couple of things more.
Whats my issue:
I think I need to start my service with bindService (instead of my current startService) so the activity can talk to it.
However, when I do that, my service gets killed after closing the Activity.
How can I get both? Binding and foreground service?
Thanks!
No. bindService will not start a service . It will just bind to the Service with a service connection, so that you will have the instance of the service to access/control it.
As per your requirement I hope you will have the instance of MediaPlayer in service . You can also start the service from Activity and then bind it. If the service is already running onStartCommand() will be called, and you can check if MediaPlayer instance is not null then simply return START_STICKY.
Change you Activity like this..
public class MainActivity extends ActionBarActivity {
CustomService customService = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// start the service, even if already running no problem.
startService(new Intent(this, CustomService.class));
// bind to the service.
bindService(new Intent(this,
CustomService.class), mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
customService = ((CustomService.LocalBinder) iBinder).getInstance();
// now you have the instance of service.
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
customService = null;
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (customService != null) {
// Detach the service connection.
unbindService(mConnection);
}
}
}
I have similar application with MediaPlayer service. let me know if this approach doesn't help you.
Quoting Android documentation:
A bound service is destroyed once all clients unbind, unless the service was also started
And about the difference between started and bound just take a look to https://developer.android.com/guide/components/services.html
So, you have to create the Service using startService and then bindService, like #Libin does in his/her example. Then, the service will run until you use stopService or stopSelf or until Android decides that it needs resources and kills you.
The app has a service which has to detect how many minutes the app is running and based on that, the service will initiate misc actions.
What is the proper way to implement this?
How can I be sure the service is running ONLY when the app is running in front of the user?
Starting the service seems easy - just start it on splash loading. But the harder part is ending it. I cannot just end it when the user press Back button on the last screen. How to handle situation when a user presses Home screen or some other other app (like phone call, or viber popup, or...) takes over the screen?
I tried taking suggestions from the other theme (How to start a android service from one activity and stop service in another activity?), but this does not handle the situation with Home button or other app taking over the screen.
The app has in total around 10 activities. Is it a proper way to bind this service to all 10 activities and when all are off, the service then turn itself off?
Make a BaseActivity for all of your Activities. In the BaseActivity, do the following:
public class MyActivity extends Activity implements ServiceConnection {
//you may add #override, it's optional
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, MyService.class);
bindService(intent, this, 0);
}
//you may add #override, it's optional
protected void onStop() {
super.onStop();
unbindService(this);
}
public void onServiceConnected(ComponentName name, IBinder binder) {};
public void onServiceDisconnected(ComponentName name) {};
/* lots of other stuff ... */
}
Your BaseActivity will need to implement ServiceConnection interface (or you can use an anonymous inner class), but you can leave those methods empty.
In your Service class, you need to implement the onBind(Intent) method and return an IBinder. The easiest way to do that is like so:
public class MyService extends Service {
private final IBinder localBinder = new LocalBinder();
public void onCreate() {
super.onCreate();
// first time the service is bound, it will be created
// you can start up your timed-operations here
}
public IBinder onBind(Intent intent) {
return localBinder;
}
public void onUnbind(Intent intent) {
// called when the last Activity is unbound from this service
// stop your timed operations here
}
public class LocalBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
}
Bound Service is define specifically for this purpose, you can bind Activities to it, and when all the Activities are gone, it will be stopped as well. The link should contain enough detail for you to implement.
I am using intent service to periodically send queries to my server to check if there are any updates. In the intent service there is a timer task, which queries the server every 3 seconds, This starts running when the application is closed.
Now when the user again comes back in to the application I want to stop the service.
How should I do this? how can an intent service which is doing timertask be stopped form another activity?
Please give suggestions for Intent Service because that is what I am using.
Maybe it will be better to use just service instead of IntentService.
public class UpdateService extends Service {
public class LocalBinder extends Binder {
public void startUpdates() {
// start updateThread if it not started, or
// notify about resuming probes
}
public void stopUpdates() {
// make updateThread to wait, until startUpdates
// called again.
//
// REMEMBER this method can be called when startUpdates didnt called.
}
}
// For simplicity we will use local binder.
private final IBinder binder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return binder;
}
private Thread updateThread = new Thread() {
#Override
public void run() {
while (true) {
// Do updates. Sleep/awake managment.
}
}
};
}
Just bind to service (with AUTO_CREATE_FLAG) and start updates when you are need.
When your activity shows just bind again to service and make it stop updates.
First of all,IntentService cannot be stopped.It will stop itself only after it has completed all the tasks present in its queue.Hence,IntentService should not be used here.
I just want to know could I bind a service from another service. For example, currently I have an activity A starting a service B and now I just want service B to bind and start another service C. So does anybody know how to do that? That means could I use the same method for activity A to start a service on a service to start another service?
You can call bindService from a Service exactly the same way you can call it from an Activity. You'll notice from the javadoc that the only place you can't call bindService is in a BroadcastReceiver. You can use a ServiceConnection as well to receive the Binder.
This works for me. If I call bindService from onCreate then onServiceConnected is in a race with the first call to onHandleIntent, so re-submit the intent if it arrives too soon. My code is roughly like this.
class MyService extends IntentService implements ServiceConnection {
IMyOtherService iService;
#Override
void onCreate() {
bindService(intent);
}
#Override
void onServiceConnected(ComponentName name, IBinder service) {
iService = IMyService.Stub.asInterface(service);
}
#Override
void onHandleIntent(Intent intent) {
if (iService == null) {
/* onHandleIntent has lost the race with onServiceConnected
* so wait 250 ms and resend the Intent.
*/
try { System.getCurrentThread().sleep(250); } catch (InterruptedException e) { }
startService(intent);
}
iService->method1();
}
I make login in my main activity.
If the login is correct, I wanna see my profile and download some data from server, but I cannot keep the connection, if I change activity.
How can I do this?
Once you verify the login is correct (by testing the connection to the server and
authenticating), you can store the login details in SharedPreferences or something similar.
Then you can just make subsequent requests using those login details (no matter which activity you are in). This is, of course, assuming the server accepts authentication this way.
You could also use a Service. Services are long running background tasks that live through Activities.
In your login form you could send a startService intent similar to starting a new activity. This calls onStartCommand on the Service class where you can create the connection and store it in the Service object itself. When you require information from the connection, you can acquire the service instance using bindService call.
Below is an example of a basic login service adapted from Google's basic example.
public class LocalService extends Service {
private MyConnection conn;
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
* IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent i = new Intent("my.service.connected");
try {
conn = new MyConnection(
intent.getStringExtra("username"),
intent.getStringExtra("password"));
i.putExtra("succeeded", true);
sendBroadcast(i);
} catch (ConnectionException ex) {
i.putExtra("succeeded", false);
sendBroadcast(i);
}
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// This is the object that receives interactions from clients. See
// RemoteService for a more complete example.
private final IBinder mBinder = new LocalBinder();
}
The Android.Manifest.xml also needs <service android:name="LocalService"></service> element inside the <application />.
Services don't come without their downsides though. Generally you should pass the startup parameters in an Intent which means they need to be serializable or parcelable. This prevents you from creating the connection in your Activity, checking that login succeeded and sending it the service.* Instead you need to send the login parameters to the service and perform the login there.
The complications come when you need to resolve whether the connection succeeded and you should proceed to the next activity or if it failed and you should show an error message. There are several solutions, but all of them require some setup. Especially when you consider that the user may pause your activity between pressing 'Login' and actually logging in.
As shown above, one option is to notify the activity by sending broadcasts from the Service. Below is an activity that launches the above service and listens to the broadcasts. The below activity does not take into account the possibility that the activity may be paused at any moment.
public class LoginActivity extends Activity {
class MyReceiver extends BroadcastReceiver {
#Override
public final void onReceive(Context ctx, Intent i) {
if (i.getBooleanExtra("succeeded", false)) {
startMyMainActivity();
} else {
showErrorMessage();
}
}
}
private BroadcastReceiver bcReceiver;
private void doLogin(String username, String password) {
// Register receiver that listens for connected messages.
bcReceiver = new MyReceiver();
IntentFilter ifilter = new IntentFilter("my.service.connected");
registerReceiver(bcReceiver, ifilter);
// Send command to start service (connects)
Intent i = new Intent(this, LocalService.class);
i.putExtra("username", username);
i.putExtra("password", password);
startService(i);
}
#Override
protected void onPause() {
if (bcReceiver != null) unregisterReceiver(bcReceiver);
super.onPause();
}
}
When the Activity should handle paused activity gracefully you should keep track of its state in a shared variable. If it was previously in a state where it tracked the Service it should bind to the Service and check its state when resuming. If the service is still connecting (as opposed to connected or disconnected) it should re-register the broadcast listener.
I wonder why you need to keep the connection. Maybe you can just reopen the connection and login again?
But if you really need to keep the connection open: You could can keep the connection in the Application. But make sure you close the connection when the user goes away from your app! I really don't recommend this. Or set a timeout for the connection to be closed like after 5 minutes of inactivity.
http://developer.android.com/reference/android/app/Application.html
You will have to say in the AndroidManifest which Application your App uses.
http://developer.android.com/guide/topics/manifest/application-element.html#nm (see andriod:name).
Then you can get the application from the activity through: getApplication(). Don't forget to cast it. Than add functions where you can login and so on.
Edit:
A little more detailed:
You will have to create a class extending Application. The name of this class you enter in the android:name of the application in the manifest. In that class you write functions with which you handle the connection. Like: startConnection() doLogin() stopConnection() ... In the Activities you call:
NameOfYouApplication app = (NameOfYouApplication) getApplication();
app.startConnection();
app.doLogin();
kind of like that. But beware that even if the Activity is closed your Application will stay alive. You better find a good time to close the connection.