IntentService and Binding Pattern - android

I have IntentService that should use reference from another service via binding:
public class BaseIntentService extends IntentService implements ServiceConnection {
protected NetworkApi network;
public BaseIntentService() {
super("BaseIntentService");
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
network = ((NetworkApiBinder) service).getApi();
// never be invoked
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
#Override
public void onCreate() {
super.onCreate();
bindService(new Intent(this, NetworkApi.impl), this, BIND_AUTO_CREATE);
}
#Override
public void onDestroy() {
super.onDestroy();
unbindService(this);
}
#Override
protected void onHandleIntent(Intent intent) {
// network always null!!!
}
}
But when I'm using binding like this onServiceConnected never be invoked. I know that IntentService not designed for the binding pattern, but may be there is a common solution for such tasks?
Thanks!

But when I'm using binding like this onServiceConnected never be invoked
That is because your IntentService is destroyed before the binding request even begins. An IntentService is automatically destroyed when onHandleIntent() completes all outstanding commands.
but may be there is a common solution for such tasks
Don't have two services. Get rid of the IntentService and move its logic into the other service.

Related

Android - Start background service in a correct manner

I've read the documentation about services and many examples on the web. However, most examples just include the same code and I still don't understand the life cyle of a background service completely.
Here is what I'm trying to do:
Start Activity
Activity starts a service, receiving location data
Exit Activity
Service keeps collecting
Start Activity
Bind to service and do some stuff, e.g. display some results
And this is what I've done to achive it:
Implemented a service (not an IntentService)
tried to start it in two ways:
Start the service by binding to it:
bindService(intent, myLocationService, Context.BIND_AUTO_CREATE);
This results in the service beeing created when the activity starts and beeing destroyed, when I call unbindService(...), e.g. in the onStop() method of the activity.
Start the service by creating it, explicitly:
Intent intent = new Intent(this, MyLocationService.class);
startService(intent);
This results in the service beeing created when the activity starts. When exiting the activity, the services crashes (although, I've implemented all cleaning up) and gets started again. When starting the activity again, the services starts once more, too.
So, how can I start, bind and unbind a serivce gracefully, without all that crashing and restarting behaviour?
These are the relevant code lines of the service:
public class MyLocationService extends Service {
final IBinder myServiceBinder = new MyServiceBinder();
public static class MyServiceConnection implements ServiceConnection {
MyLocationService service;
boolean bound = false;
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
MyServiceBinder binder = (MyServiceBinder)iBinder;
service = binder.getService();
bound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
}
public MyLocationService getService() {
return service;
}
public boolean isBound() {
return bound;
}
}
/**
* Binder for GPSService
*/
public class MyServiceBinder extends Binder {
MyLocationService getService() {
return MyLocationService.this;
}
}
public MyLocationService() {
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return myServiceBinder;
}
}
These are the relevant code lines of the activity:
public class MyMainActivity extends AppCompatActivity {
MyLocationService.MyServiceConnection myServiceConnection;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MyLocationService.class);
startService(intent);
}
#Override
protected void onStart() {
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
Intent intent = new Intent(this, MyLocationService.class);
bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onPause() {
super.onPause();
unbindService(myServiceConnection);
}
#Override
protected void onStop() {
super.onStop();
}
}
https://developer.android.com/guide/components/activities/activity-lifecycle.html
Depending on what your requirements are, should be onPause() onResume() or onStart() and onStop().
If you want the service to run in the background you could use
/**
* Called when all clients have disconnected from a particular interface
* published by the service. The default implementation does nothing and
* returns false.
*
* #param intent The Intent that was used to bind to this service,
* as given to {#link android.content.Context#bindService
* Context.bindService}. Note that any extras that were included with
* the Intent at that point will <em>not</em> be seen here.
*
* #return Return true if you would like to have the service's
* {#link #onRebind} method later called when new clients bind to it.
*/ public boolean onUnbind(Intent intent) {
return false; }
onRebind() to do something.

Android problems binding started service

I start my service in onCreate() (if onCreate is called the first time) and call bindService in onStart(). The service probaply works, but after calling bindService my local instance of the service is still null. Furthermore, is seems to be that getService() is not called.?
Here is some code:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
if(savedInstanceState == null){
final Intent i = new Intent(this, HostService.class);
startService(i);
}
}
protected void onStart(){
super.onStart();
bindService(new Intent(this, StartGameActivity.class), connection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection connection = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
HostBinder binder = (HostBinder) arg1;
hostService = binder.getService();
isBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
and in HostService:
...
private HostBinder binder = new HostBinder();
...
public class HostBinder extends Binder{
HostService getService(){
Log.d(TAG, "getService");
return HostService.this;
}
}
#Override
public IBinder onBind(Intent arg0) {
return binder;
}
Why is hostService still null, after onStart() is called and why is getService is not getting called ("getService" is not print in LogCat)?
thx & regards
You don't need to call startService(); it will be started by bindService().
In your onStart() method, you're creating an intent to launch StartGameActivity.class. Was that what you wanted, or did you mean to launch HostService.class?
If neither of those are your problem, we need more to go on. Can you put a logging statement inside your onServiceConnected() and onBind() methods so you can be sure they're called?
Any logcat messages that look interesting?
Are you talking to your service via the binder, or via messaging?

Android: How to safely unbind a service

I have a service which is binded to application context like this:
getApplicationContext().bindService(
new Intent(this, ServiceUI.class),
serviceConnection,
Context.BIND_AUTO_CREATE
);
protected void onDestroy() {
super.onDestroy();
getApplicationContext().unbindService(serviceConnection);
}
For some reason, only sometimes the application context does not bind properly (I can't fix that part), however in onDestroy() I do unbindservice which throws an error
java.lang.IllegalArgumentException: Service not registered: tools.cdevice.Devices$mainServiceConnection.
My question is: Is there a way to call unbindservice safely or check if it is already bound to a service before unbinding it?
Thanks in advance.
Try this:
boolean isBound = false;
...
isBound = getApplicationContext().bindService( new Intent(getApplicationContext(), ServiceUI.class), serviceConnection, Context.BIND_AUTO_CREATE );
...
if (isBound)
getApplicationContext().unbindService(serviceConnection);
Note:
You should use same context for binding a service and unbinding a service. If you are binding Service with getApplicationContext() so you should also use getApplicationContext.unbindService(..)
Here you can find a nice explanation and source codes how to work with bound services. In your case you should override methods (onServiceConnected and onServiceDisconnected) of ServiceConnection object. Then you can just check mBound variable in your code.
Doing exactly what Andrey Novikov proposed didn't work for me.
I simply replaced:
getApplicationContext().unbindService(serviceConnection);
With:
unbindService(serviceConnection);
I found there are two issues. Attempting to bind more than once and also attempting to unbind more than once.
Solution:
public class ServiceConnectionManager implements ServiceConnection {
private final Context context;
private final Class<? extends Service> service;
private boolean attemptingToBind = false;
private boolean bound = false;
public ServiceConnectionManager(Context context, Class<? extends Service> service) {
this.context = context;
this.service = service;
}
public void bindToService() {
if (!attemptingToBind) {
attemptingToBind = true;
context.bindService(new Intent(context, service), this, Context.BIND_AUTO_CREATE);
}
}
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
attemptingToBind = false;
bound = true;
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
}
public void unbindFromService() {
attemptingToBind = false;
if (bound) {
context.unbindService(this);
bound = false;
}
}
}
Why do we get this error?
When you try to unregister a service which is not registered.
What are some common examples?
Binding and Unbinding a service with different Context.
calling unBind(mserviceConnection) more than bind(...)
First point is self explanatory. Lets explore the second source of error more deeply. Debug your bind() and unbind() calls. If you see calls in these order then your application will end up getting the IllegalArgumentException.
How can we avoid this?
There are two ways you should consider to bind and unbind a service in Activity. Android docs recommend that
If you want to interact with a service only when the Activity is visible then
bindService() in onStart() and unbindService() in onStop()
Your Activity {
#Override
public void onStart(){
super.onStart();
bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
}
#Override
public void onStop(){
super.onStop();
unbindService(mConnection);
}
}
If you want to interact with a service even an Activity is in Background then
bindService() in onCreate() and unbindService() in onDestroy()
Your Activity {
#Override
public void onCreate(Bindle sis){
super.onCreate(sis);
....
bindService(intent, mConnection , Context.BIND_AUTO_CREATE);
}
#Override
public void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
I think that guide is not completely correct as mentioned here Surya Wijaya Madjid. Memory leaks can occur when bound service is destryed and not re-connected yet.
I think that this approach is needed:
Service mService;
private final ServiceConnection mServiceConnection = new ServiceConnection()
{
boolean bound = false;
#Override
public void onServiceDisconnected(ComponentName name)
{
mService = null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
mService = ((MyService.ServiceBinder) service).getService();
if (!bound)
{
// do some action - service is bound for the first time
bound = true;
}
}
};
#Override
public void onDestroy()
{
if (mService != null)
{
// do some finalization with mService
}
if (mServiceConnection.bound)
{
mServiceConnection.bound = false;
unbindService(mServiceConnection);
}
super.onDestroy();
}
public void someMethod()
{
if (mService != null)
{
// to test whether Service is available, I have to test mService, not mServiceConnection.bound
}
}
Use a variable to record if you have ever bind to a service, and unbind it if the variable is true.
See android official example :
http://androidxref.com/9.0.0_r3/xref/development/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.java#376
Not sure about all the above answers, it seemed far too complicated while none of these would fit the issue I had.
Only binding/unbinding once at a time, and the service was definitely bound at the time of the unbind() call. Don't want to leak anything, so I just made sure I was using the same context for the bind() and unbind() calls and that solved it permanently! Doing something like this:
any_context.getApplicationContext().bind(...);
...
another_context.getApplicationContext().unbind(...);

Sticky Service Management

I've got a Sticky Service (returns START_STICKY from onStartCommand) which executes some code in an AsyncTask, but I'm having some problems with how and when to start, bind, stop, unbind. I only want the service around whilst the parent activity is alive, I don't want it hanging around in the background when the app has been closed, but I need the service to survive an orientation change. I currently don't need the service to be active for the entire duration of the activity being active, so I call stopSelf() after the main work is done in my AsyncTask in the Service and then start the Service again when needed. Sometimes I'll need to interrupt the work the service is doing, cancel the AsyncTask and start again with different data. The problem is that no matter what I do - I can't seem to get it solid throughout all the different possible scenarios. Can anyone have a look through and tell me what I'm doing wrong?
My Service is :
public class ChordCalculatorService extends Service {
private final IBinder mBinder = new LocalBinder();
private AsyncTask<SearchData, SearchStatusData, List<Item>> currentTask;
#Override
public void onCreate() {}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
/**
* 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 {
public ChordCalculatorService getService() {
return ChordCalculatorService.this;
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public SearchData getSearchData() {
return searchData;
}
public void startWork() {
if (currentTask != null && currentTask.getStatus() == Status.RUNNING) {
currentTask.cancel(true);
}
if(searchData != null) {
Worker task = new Worker();
currentTask = task.execute(new SearchData[] { searchData });
} else {
Message msg = handler.obtainMessage(ERROR, "No search data set");
handler.sendMessage(msg);
}
}
class Worker extends AsyncTask<SearchData, SearchStatusData, List<Item>> {
// ... code ...
#Override
protected void onPostExecute(List<Item> result) {
Message msg = handler.obtainMessage(COMPLETE, new StatusData(Status.STATUS_FINISHED, result));
handler.sendMessage(msg);
stopSelf();
}
}
}
Currently I have the Service being started when my custom View is created:
public class MyCustomView extends BasicFretBoardView {
private ServiceConnection conn;
private MyService myService;
private boolean isServiceStarted;
private boolean isServiceBound;
public MyCustomView(Context context, AttributeSet attr) {
super(context, attr);
startService();
}
public void startService() {
Intent serviceIntent = new Intent(getContext(), MyService.class);
conn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = ((LocalBinder) service).getService();
myService.registerHandler(serviceHandler);
}
#Override
public void onServiceDisconnected(ComponentName name) {
myService = null;
}
};
// Explicitly start the service. Don't use BIND_AUTO_CREATE, since it
// causes an implicit service stop when the last binder is removed.
getContext().startService(serviceIntent);
getContext().bindService(serviceIntent, conn, 0);
isServiceStarted = true;
isServiceBound = true;
}
public void stopService() {
if (isServiceStarted) {
Intent serviceIntent = new Intent(getContext(), MyService.class);
getContext().stopService(serviceIntent);
isServiceStarted = false;
}
unBindService();
}
public void unBindService() {
if(isServiceBound) {
getContext().unbindService(conn);
isServiceBound = false;
}
}
// gets called based on some user interaction
private void startServiceWork() {
if(!isServiceStarted) {
startService();
} else {
myService.cancelCalcalation();
}
myService.setData(data);
myService.startWork();
}
}
and stopping the service is handled in the Activity:
public class CustomChordActivity extends Activity {
// ... code ...
#Override
public void onBackPressed() {
super.onBackPressed();
}
#Override
protected void onPause() {
if(isFinishing()) {
chordsView.stopService();
}
super.onPause();
}
#Override
protected void onStop() {
super.onStop();
}
#Override
protected void onDestroy() {
chordsView.unBindService();
super.onDestroy();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
}
}
It seems that you want your task to run on demand, maybe an IntentService would be a more suitable option. When you need work to be done, (startServiceWork()), you just start the service and that kicks off your AsyncTask. The service will then finish after the task has finished.
Now, regarding orientation changes, you would have to implement a Broadcast Receiver whose intent filter is "android.intent.action.CONFIGURATION_CHANGED". (I assume that you want the service to do work when the orientation changes) Place the Broadcast Receiver, within your activity/main ui thread. This will in effect make the hosting process of your Broadcast Receiver to be the main application process making it safer to start the service from within the Broadcast Receiver.

Why remote service is destroyed when main activity is closed?

I wrote an android program that: has a main activity for UI, and it starts a service. The service timely callbacks the UI activity to update views. It works fine except: if the activity is closed (with BACK) and start again, the service will also be started again (The service plays audio file, so there are two overlapped sounds).
I use bindService with BIND_AUTO_CREATE flag to start and connect to service. According to the document, it should create service only if it doesn't exist, but obviously it starts another instance when opened second time.
All I want is when the activity is closed, the service goes on running, and when the activity opens again, it can reconnect to the service. Is that possible? Or I just misunderstand the usage of service?
Tried more:
Use ICountService (described in .aidl) instead of CountService in bindService Intent. It's onDestroyed is called when the activity is closed.
Below is code of service creating if it helps.
ServiceConnection conn = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName c, IBinder b) {
Log.d("TK","Connected");
//binder = (ICountService.Stub) b;
service = ICountService.Stub.asInterface(b);
try {
service.setCallback(new ICountCallback.Stub(){
#Override
public void alert() {
Log.d("TK","alert!");
}
#Override
public void updateTime(final int sec) {
handler.post(new Runnable(){
#Override
public void run() {
indicator.setText(toText(sec));
}
});
}
});
} catch (RemoteException e) {
e.printStackTrace();
}
}
#Override
public void onServiceDisconnected(ComponentName c) {
Log.d("TK","Disconnected");
}
};
private void startCountService(){
Intent i = new Intent(ICountService.class.getName());
boolean ok = context.bindService(i, conn, Context.BIND_AUTO_CREATE);
Log.d("TK", "bindService="+ok);
}
According to the document, it should create service only if it doesn't exist, but obviously it starts another instance when opened second time.
bindService() will create the service instance if the service is not running. unbindService() will destroy the service instance if there are no other bound connections and nobody called startService(). If the service is destroyed on unbindService(), then a subsequent bindService() will create a new service instance.
IMHO, ideally, unbindService() would not immediately destroy the service, but let it linger for a few seconds first, in case there is a bindService() shortly after the unbindService(). However, that is not how they implemented it.
All I want is when the activity is closed, the service goes on running, and when the activity opens again, it can reconnect to the service.
You should be using startService() and stopService() instead of (or conceivably in addition to) bindService() and unbindService().
This was worked for me.
Main.java
public class Main extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(new Intent(Main.this, MyService.class));
try{
MyService.setMainActivity(this);
}catch(Exception e){
//t.setText(e.getMessage());
}
}
}
MyService.java
public class MyService extends Service {
private Context ctx;
public static Main main;
#Override public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
public void onCreate()
{
super.onCreate();
ctx = this;
startService();
}
#Override public void onDestroy() {
// TODO Auto-generated method stub
Log.d("ASAS","Destroy");
super.onDestroy();
}
public static void setMainActivity(Main activity) {
main = activity;
}
private void startService()
{
//timer.scheduleAtFixedRate(new checkdata(),0,30000);
}
}

Categories

Resources