I have a long-running service that is bound to a loading screen. I know this is suboptimal, but an AsyncTask did not work for me as it keeps running (data leaks) even after the Activity is ended forcefully. Regardless, want to understand what is happening here. The basic layout never shows up (black screen), but the service runs as intended. A (bound) service lives and runs on the activity thread, of course. But why does it not inflate the UI first? onCreate gets called before bindService afaik and as the Log confirmed.
ServiceConnection _connection = new ServiceConnection() {
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
_service = myService.Stub.asInterface(iBinder);
runStuff();
}
public void onServiceDisconnected(ComponentName componentName) {
_service = null;
}
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent();
serviceIntent.setPackage(myService.class.getPackage().getName());
serviceIntent.setAction(myService.class.getName() + ".ACTION_BIND");
bindService(serviceIntent, _connection, BIND_AUTO_CREATE);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loadingscreen);
findViewsById();
setDefaultVisibility();
[...]
Edit: As additional info, it actually takes a long time to display the (very simple) loading screen even with the AsyncTask. But even that weird delay should still not matter, because the service should not bind before then. What am I missing?
Edit2: I added timestamps. The service starts immediately. If the service is in an AsyncTask, the UI still takes the same time to load. From the button tap on the previous activity to onPostCreate it takes about 400ms, not the 3-4 s I see looking at the UI. The AsyncTask does update the UI as expected, but apart from onProgressUpdate, there is nothing pertaining to UI after onCreate or before any service calls. What could cause such a delay? I don't even have that on activities in the same app that are heavy on DB and GUI.
Put your code in a seperate method, I dont know if this will work but i have had success like this
private void start(){
Intent serviceIntent = new Intent();
serviceIntent.setPackage(myService.class.getPackage().getName());
serviceIntent.setAction(myService.class.getName() + ".ACTION_BIND");
bindService(serviceIntent, _connection, BIND_AUTO_CREATE);
}
and then start that method in your oncreate the service will then start after creation like
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loadingscreen);
findViewsById();
setDefaultVisibility();
start();
[...]
Related
I'm using bound service so that I am able to communicate between an activity and a service.
I'm binding to a service in onStart:
#Override
protected void onStart() {
super.onStart();
Intent bindIntent = new Intent(this, MusicService.class);
bindService(bindIntent, this, BIND_AUTO_CREATE);
}
waiting for service to bind:
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMusicService = ((MusicService.LocalBinder) service).getService();
mMusicService.setCallback(this);
}
handling disconnect from service:
#Override
public void onServiceDisconnected(ComponentName name) {
mMusicService = null;
}
unbinding from service in onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
if (mMusicService != null && isFinishing()) {
mMusicService.setCallback(null);
unbindService(this);
}
}
My problem is that when app is minimized, onDestroy gets called immediately and then onUnbind in Service gets called and music is stopped.
Here is onUnbind method (where mPlayer is MediaPlayer):
#Override
public boolean onUnbind(Intent intent) {
mPlayer.stop();
mPlayer.release();
mPlayer = null;
return super.onUnbind(intent);
}
If I don't implement onUnbind music continues to play (sometimes and sometimes it stops after some time) and when I open the app again (from minimized applications) I am able to play another song and then those two song play at same time.
I've red couple articles about music players and services on android and I thought that this was correct approach (thinking that onDestroy will be called when OS is out of memory).
Any ideas how I can re-implement my app workflow so that I will work as expected?
EDIT
At first I thought that "Don't keep activities" under developer options is a problem, but problem is still there even if I uncheck it.
And if some code from my service is needed please say I will edit my question (there's a lot of code and I'm not sure which part is important for this issue)
SOLUTION:
startForeground(<notification id>, <notification>);
to run service even if app gets killed. And when user dismisses the notification:
stopForeground(true);
stopSelf();
More about startForeground here.
It's not obvious, but you should start a Thread that runs in background and use the service to control it's state.
A service isn't a thread that hold some run state like a thread. Unless it's an IntentService. (Correct me if I'm wrong)
While activity can (and probably will) get destroyed, your app will keep running.
Activity:
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, BackgroundService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
finishOnPause = true;
}
#Override
protected void onStop() {
super.onStop();
unbindService(mServiceConnection);
}
Service:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
ensureServiceThread();
if (intent != null) {
}
return START_NOT_STICKY;
}
private void ensureServiceThread() {
if (service_thread == null) {
service_thread = new ServiceThread(this);
service_thread.start();
}
}
#Override
public void onDestroy() {
stopServiceThread();
super.onDestroy();
}
private void stopServiceThread() {
if (service_thread != null) {
service_thread.interrupt();
service_thread = null;
}
}
And you should do your work inside the Thread.
If you need context, it's your Service.
If you need to do something on Main thread - create a Handler in Service.OnCreate and do a handler.postRunnable inside a worker thread safely.
What I would do is:
- Create a service
- Create a thread
- Create a media player inside a thread (if possible, otherwise on Service creation and pass it to thread)
- Inside a thread - continuously poll media player state
- On song finished send an intent to service that change track
- stop thread/service if needed.
On Android, I have an Activity called FirstActivity which starts a Service named MyService to do networking stuff in the background. The Activity and the Service communicate with each other all the time by calling methods.
Now when the user navigates from FirstActivity to SecondActivity, the background service should not be killed or re-created, but kept alive and passed to SecondActivity which will now be the one communicating with the service.
In other words, the Service shall be running as long as one of the two Activitys is running, and it should not stop while the user navigates between the two Activitys.
One of the Activitys will always be in the foreground and during this time, the service should (optimally) never get killed. I think this should not be a problem because one of those two Activitys is always active and thus Android knows the service is important and not something that must be killed.
(If there was no way to prevent Android from killing and re-creating the service from time to time, I would need a way to restore the full state of the service gracefully.)
To sum up, the Service should have the same lifespan as the two Activitys "combined". It should start with the first of them and stop not before both of them have been destroyed.
So is the following code correct for that setup and goals?
public class MyService extends Service {
public class LocalBinder extends Binder {
public MyService getService() {
return MyService.this;
}
}
...
}
public class FirstActivity extends Activity {
private MyService mMyService;
private ServiceConnection mMainServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyService mainService = ((LocalBinder) service).getService();
mMyService = mainService;
mMyService.setCallback(FirstActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
#Override
public void onCreate(Bundle savedInstanceState) {
...
startService(new Intent(FirstActivity.this, MyService.class));
}
#Override
protected void onResume() {
super.onResume();
bindService(new Intent(FirstActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
if (mMainServiceConnection != null) {
unbindService(mMainServiceConnection);
}
if (mMyService != null) {
mMyService.setCallback(null);
}
if (!isUserMovingToSecondActivity) {
stopService(new Intent(FirstActivity.this, MyService.class));
}
}
#Override
public void onBackPressed() {
stopService(new Intent(FirstActivity.this, MyService.class));
super.onBackPressed();
}
...
}
public class SecondActivity extends Activity {
private MyService mMyService;
private ServiceConnection mMainServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyService mainService = ((LocalBinder) service).getService();
mMyService = mainService;
mMyService.setCallback(SecondActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName className) {
mMyService = null;
}
};
#Override
protected void onResume() {
super.onResume();
bindService(new Intent(SecondActivity.this, MyService.class), mMainServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
if (mMainServiceConnection != null) {
unbindService(mMainServiceConnection);
}
}
#Override
protected void onDestroy() {
...
stopService(new Intent(SecondActivity.this, MyService.class));
}
...
}
Is this the best way to guarantee a long-lasting service in the background of the Activitys that will not be killed or re-created?
What about Context.BIND_AUTO_CREATE? Is it correct to have this flag set here? What about Context.BIND_ADJUST_WITH_ACTIVITY and Context.BIND_WAIVE_PRIORITY -- do I need these?
(Many thanks to #corsair992 for his useful pointers!)
If the activities are always called in that order (i.e. FirstActivity starts SecondActivity, and never the other way around, then you should, basically, attempt to "tie" the Service's life-cycle to FirstActivity's lifecycle.
In general (see caveats later), this means:
Call startService() in FirstActivity.onCreate().
Call stopService() in FirstActivity.onDestroy().
Call bindService()/unbindService() in the onStart()/onStop() methods of both Activities (to get access to the Binder object, and be able to call methods on it).
A service started this way will be alive until stopService() is called and every client unbinds, see Managing the Lifecycle of a Service:
These two paths are not entirely separate. That is, you can bind to a
service that was already started with startService(). (...) In cases like this, stopService() or
stopSelf() does not actually stop the service until all clients
unbind.
and:
When the last client unbinds from the service, the system destroys the
service (unless the service was also started by startService()).
With this basic strategy, the Service will live as long as FirstActivity is around (i.e. it is not destroyed). However, an important point still remains: in the event of a configuration change (e.g. a screen rotation) that is not handled explicitly will cause the activity to restart itself, and the service will be destroyed (since we're calling stopService() in onDestroy()).
To prevent this, you can check isChangingConfigurations() before actually stopping the service (since an onDestroy() callback occurring due to this reason means that although this particular instance of the Activity is being destroyed, it will be recreated afterwards.
Hence, the full solution would be something like:
public class FirstActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
}
private ServiceConnection mServiceConnection = new ServiceConnection() { ... }
#Override
protected void onStart() {
super.onStart();
bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
unbindService(mServiceConnection);
super.onStop();
}
#Override
protected void onDestroy() {
if (!isChangingConfigurations())
stopService(new Intent(this, MyService.class));
super.onDestroy();
}
While SecondActivity would only implement the onStart()/onStop() methods (in the same way).
A couple of notes about your particular implementation:
It's not necessary to override onBackPressed(), since if the activity is destroyed the necessary lifecycle methods will be called (plus, it could be finished without pressing the back button, for example if calling finish() on it).
Stopping the service in onDestroy() instead of onPause() saves you from having to check for isUserMovingToSecondActivity.
My activity attempts to start and then bind a service that's supposed to run independently of it. This service turns the GPS on.
onCreate calls getApplicationContext.StartService, and onResume calls getApplicationContext.BindService. OnPause calls getApplicationContext.unbindService, although it never appears to run properly (the service connection never logs an unbind, although binds are logged when I treat it similarly).
Unfortunately, when I open up my Recents list, and slide the activity away, the Service stops and then re-starts itself almost immediately, dropping the GPS connection. What would cause this sort of behavior?
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
// Start up the service
Intent intent = new Intent(getApplicationContext(), LockService.class);
getApplicationContext().bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
Log.i(tag, "onPause");
if (isBound) {
getApplicationContext().unbindService(myConnection);
}
}
...
// Bound service stuff
LockService myService;
boolean isBound = false;
private ServiceConnection myConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LockBinder binder = (LockBinder) service;
myService = binder.getService();
isBound = true;
final boolean status = myService.getStatus();
Log.i(tag, "service bound");
}
#Override
public void onServiceDisconnected(ComponentName className) {
isBound = false;
Log.i(tag, "service unbound");
}
};
Edit: I checked into this answer, but this just prevents the service from restarting immediately (it still gets hung up, and re-opening the activity re-initializes it):
Android onCreate Service called when Activity onDestroy called
Edit 2: I had gotten my hopes up a bit too far with this answer. It doesn't seem to fix anything either.
How to force the onServiceDisconnected() get called?
Edit 3: This might be a KitKat thing.
In android 4.4, swiping app out of recent tasks permanently kills application with its service . Any idea why?
Because swiping an app out of the Recents list kills it in Android version 4.4 (KK), I have opted to simply not show my app in the Recents list at all.
Oh well, it didn't really need to live there anyway. It exists quite happily inside the notification bar. I pity anyone who isn't so lucky, and needs to forcibly restart the service via a timer and some hackneyed code.
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.
I'm trying to make a music player. To do that I'm creating a service that manages a MediaPlayer object and plays the songs. But every time I reopen my music application it starts a new Service and keeps playing two songs simultaneously. How can I make my Activity bind to the already running service?
I noticed that when I don't call unbindService (...) in the OnStop() method my application runs OK. But I don't know if its right to not unbind when the activity stops.
I'm binding like that:
#Override
protected void onStart() {
super.onStart();
Intent service = new Intent(this,MyService.class);
bindService(service, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
if(mBound) {
unbindService(mConnection);
mBound = false;
}
}
You need to check on onStart() that is service running or not. If service is already running, then you need to just bind it. But in your current code you creating service everytime.
You should try to use Service.START_STICKY for checking that service is already running or not. You can see :
Service
, start Sticky or start not sticky
I think it will help you to update your service class.
I could fix it add startService(...) to the OnStart( ) method. I didnt understand why this fixed, but seems that I need to do that. If anyone knows why please add. Thanks for everyone help.
this is how OnStart got:
#Override
protected void onStart() {
super.onStart();
Intent service = new Intent(this,MyService.class);
bindService(service, mConnection, Context.BIND_AUTO_CREATE);
startService(service);
}