I have an android device with an integrated barcode scanner. I'm setting up the service as follows:
public class BarcodeService extends Service {
private final LocalBinder binder = new LocalBinder();
public class LocalBinder extends Binder {
public BarcodeService getService() {
return BarcodeService.this;
}
}
#Override
public IBinder onBind(Intent arg0) {
return binder;
}
#Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("ServiceStartArguments");
thread.start();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Get scanner
}
}
The service is also in the AndroidManifest.xml. The class that makes use of this service is:
public class BarcodeReader extends Activity {
private BarcodeService barcodeService;
private boolean isBound = false;
private ServiceConnection barcodeServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
barcodeService = ((BarcodeService.LocalBinder)service).getService();
isBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
barcodeService = null;
isBound = false;
}
};
#Override
protected void onStart() {
super.onStart();
if (!isBound) {
Intent intent = new Intent(this, BarcodeService.class);
startService(intent);
bindService(intent, barcodeServiceConnection, Context.BIND_AUTO_CREATE);
}
}
#Override
protected void onStop() {
super.onStop();
if (isBound) {
unbindService(barcodeServiceConnection);
}
}
}
However the service is not binding, ie. barcodeService is always null. The code never reaches onServiceConnected.
What am I missing? And is it necessary to use a class that extends Activity?
Common Android Service troubleshooting
Just some general remarks and stuff to check if your service is not starting.
Service class defined in Manifest
Common mistake is not to have the service in manifest (android doesn't warn you about that) or have it there but misspelled the class name.
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
Or you might have it in the manifest (or one of the manifests) but the final manifest after merging that is used within the apk doesn't have the service definition. For that check:
project_folder/app_folder/build/intermediates/manifests/full/...
A project clean and rebuild might help.
Check bindService return value
When debugging check the boolean return value on the bindService call to see if service was started successfully or not.
Debug Activity and Service implementation
Also the service might be running but not bind or might not execute anything hence have no visual effect that it's running in the background. For that use the debugger on both the bound Activity and the Service itself.
Check onBind, onStartCommand in Service class or even the onCreate there.
In Activity check bindService, ServiceConnection and so.
Resources
also check https://developer.android.com/guide/components/services.html
Related
I had a Singleton object that had a bound service. I wanted it to restart, and when I start my application from launcher, singleton object will initialize and bind to this existing instance of service.
Here is the code for creating and binding service in singleton:
public class MyState {
private static MyState sState;
private MyService mService;
private Context mContext;
private boolean mBound = false;
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyService.MyBinder binder = (MyService.MyBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mBound = false;
}
};
public static MyState get(Context context) {
if (sState == null) {
sState = new MyState(context);
}
return sState;
}
public MyState(Context context) {
mContext = context.getApplicationContext();
startService();
}
private void startService() {
Intent intent = new Intent(mContext, MyService.class);
mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
// this won't create another instance of service, but will call onStartCommand
mContext.startService(intent);
}
}
And here is the code insice Service
public class MyService extends Service {
private final IBinder mBinder = new MyBinder();
public class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
// this method is called by singleton object to stop service manually by user
public void stop() {
stopSelf();
}
#Override
public void onDestroy() {
super.onDestroy();
// some cleanup code here
}
}
Unfortunately, when I swipe away my app in task list this service never restarts. Service's onDestroy method is never called in this case.
I moved the binding to an activity at which user can interact with service, and surprisingly it started working as I expected.
I tried to call service creation using application context inside my activity, and it still works.
Is starting service from activity different from starting it from a regular java object?
As you are returning START_STICKY this service will stop whenever you close/kill the app because after the App closed all the Reference/Value will become null for all Intent as well as variables and so STICKY service will not able to get Intent value. if you want to restart the service after app kills use return START_REDELIVER_INTENT
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_REDELIVER_INTENT;
}
this will restart the service in 5-10 seconds after app killed.
Issues
Service is NOT running always even after I have used START_STICKY.
Sometimes I dont get any Toast Action for Outgoing call, is that mean service stops after some time ?
My Requirment
Application shows a Toast whenever user makes a outgoing call from the phone. For this I am using a BroadcastReceiver to tap the call action and a service (to run Receiver always). once I start this activity, it starts showing toast when a outgoing call get initiated ..but not Always.
Below is the complete code -
MainActivity.class
public class MainActivity extends Activity
{
CallNotifierService m_service;
boolean isBound = false;
private ServiceConnection m_serviceConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName className, IBinder service)
{
m_service = ((CallNotifierService.MyBinder)service).getService();
Toast.makeText(MainActivity.this, "Service Connected", Toast.LENGTH_LONG).show();
isBound = true;
Intent intent = new Intent(MainActivity.this, CallNotifierService.class);
startService(intent);
}
#Override
public void onServiceDisconnected(ComponentName className)
{
m_service = null;
isBound = false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, CallNotifierService.class);
bindService(intent, m_serviceConnection, Context.BIND_AUTO_CREATE);
}
.
.
.
}
CallNotifierService.class
public class CallNotifierService extends Service
{
private final IBinder myBinder = new MyBinder();
private static final String ACTION_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
private CallBr br_call;
#Override
public IBinder onBind(Intent arg0)
{
return myBinder;
}
#Override
public void onDestroy()
{
Log.d("service", "destroy");
this.unregisterReceiver(this.br_call);
Toast.makeText(CallNotifierService.this, "Receiver Un-Registered", Toast.LENGTH_LONG).show();
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUTGOING_CALL);
this.br_call = new CallBr();
this.registerReceiver(this.br_call, filter);
Toast.makeText(CallNotifierService.this, "onStartCommand Called", Toast.LENGTH_LONG).show();
return START_STICKY;
}
public class MyBinder extends Binder
{
CallNotifierService getService()
{
return CallNotifierService.this;
}
}
public class CallBr extends BroadcastReceiver
{
public CallBr() {}
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, "Action:"+intent.getAction(), Toast.LENGTH_LONG).show();
}
}
}
You are getting the wrong approach here, by mixing a simple idea (that would work if done correctly) with more complicated ideas (that cannot work).
Keep in mind: services are not "always running" components, even when using START_STICKY.
The Android system will not hesitate to kill your service if it needs memory somewhere else. START_STICKY only means that the Android system will re-start your service when it can, calling onStartCommand as specified in the documentation.
If you need a service to really stick around, then you must use a foreground service. But it will have consequences on the UI (annoying notification icon always showing), and battery life, and you do not need this here.
Now here is the magic trick: your app does not need to be running for your BroadcastReceiver to work. All you need to do is to register it in your AndroidManifest.xml with the correct intent-filter:
<receiver android:name=".broadcastreceivers.CallBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
(also make sure your app has the required permissions, namely PROCESS_OUTGOING_CALLS).
Then all you need in code is:
public class CallBroadcastReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, "Action: " + intent.getAction(), Toast.LENGTH_LONG).show();
}
}
No activity (except to ask for the PROCESS_OUTGOING_CALLS permission on Android 6+), no service, nothing. Simple and battery-efficient !
The service does get re-created, not not re-started.
If you override the onCreate and do a Log.d or a Toast, you will see that it gets called after your app is destroyed.
So the trick to keep it running after it is recreated is to do your code on the onCreate method and use the onStartCommand just to return START_STICKY.
I am stuck with getting my Android Service running and would really need some help.
I started off with Vogella's Tutorial on how to bind a Service and ended up trying out pretty much every approach which came to my mind and on the web to solve my problem.
"onStart()" of my main Activity gets called and no Exception (creating Intent, "bindService()") is thrown. But, also, no Service is started (apiService == null).
I know it shouldn't be hard to get it working, but sadly I am stuck with this for over two hours already. Any kind of help, pointers, etc. is really appreciated.
[EDIT]
Breakpoints set in the service class don't get hit. Also, Log.d() entries won't print in Logcat.
Service extending Android.Service and implementing my own Interface:
public class APIService extends Service implements APICall{
private final IBinder binder = new APIBinder();
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId){
super.onStartCommand(intent, flags, startId);
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class APIBinder extends Binder{
public APIService getService(){
return APIService.this;
}
}
//implementation of Interface...
Main Activity / binding of Service:
public class MainActivity extends Activity {
private APIService apiService = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onStart() {
super.onStart();
Intent serviceIntent = new Intent(this, APIService.class);
bindService(serviceIntent, apiServiceConnection, Context.BIND_AUTO_CREATE);
};
#Override
protected void onDestroy(){
super.onDestroy();
unbindService(apiServiceConnection);
}
private ServiceConnection apiServiceConnection = new ServiceConnection(){
public void onServiceConnected(ComponentName className, IBinder binder) {
APIBinder localBinder = (APIBinder)binder;
apiService = localBinder.getService();
}
public void onServiceDisconnected(ComponentName className) {
apiService = null;
}
};
//click handler, etc...
Manifest file entry:
<service android:name=".APIService"/>
You should add
startService(serviceIntent);
before
bindService(serviceIntent, apiServiceConnection, Context.BIND_AUTO_CREATE);
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(...);
Ok, I'm new to android development and am trying to bind to a service so that I can call methods on the service once it's been started. The Activity and Service described below are both part of the same application so there shouldn't be any problems there, but everytime I run my app I get the following error:
java.lang.ClassCastException: android.os.BinderProxy
The line this happens on is:
LocalBinder binder = (LocalBinder) service;
My Activity code (simplified is):
public class Main extends Activity {
boolean gpsBound = false;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
/** Called whenever the activity is started. */
#Override
protected void onStart() {
super.onStart();
// Bind to GPSService
Intent i = new Intent(this, GPSService.class);
startService(i);
bindService(i, connection, Context.BIND_AUTO_CREATE);
}
/** service binding */
private ServiceConnection connection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// After binding to GPSService get the instance of it returned by IBinder
LocalBinder binder = (LocalBinder) service;
gpsBound = true;
}
public void onServiceDisconnected(ComponentName className) {
gpsBound = false;
}
};
}
Service:
public class GPSService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public IBinder onBind(Intent i) {
// TODO Auto-generated method stub
return new LocalBinder<GPSService>(this);
}
/**
* Our implementation of LocationListener that handles updates given to us
* by the LocationManager.
*/
public class CustomLocationListener implements LocationListener {
DBHelper db;
CustomLocationListener() {
super();
}
// Overridden methods here...
}
}
And finally my LocalBinder:
/**
* A generic implementation of Binder to be used for local services
* #author Geoff Bruckner 12th December 2009
*
* #param <S> The type of the service being bound
*/
public class LocalBinder<S> extends Binder {
private String TAG = "LocalGPSBinder";
private WeakReference<S> mService;
public LocalBinder(S service){
mService = new WeakReference<S>(service);
}
public S getService() {
return mService.get();
}
}
I understand the meaning of the ClassCast Exception but cannot understand what to do! I've followed the example in the google documentation but it's still not working. Can anyone shed any light on what might be causing this?
Thanks in advance!
Delete attribute process in your AndroidManifest.xml of your service.
Had same error. I had added the android:process=":process_description" attribute in the manifest. When you add it, your service is created as separate process and hence you get instance of binderProxy (Hence the class cast exception)
If you are trying to bind to a local service than yes, you can just cast it. However if you are trying to bind to a remote (separate process) service you must use the AIDL method as prescribed in this article.
http://developer.android.com/guide/components/aidl.html
the LocalBinder passed in onServiceConnected has a generic type argument, while your local variable LocalBinder binder does not have one.
Resolve this one way or another, either by removing the generic type from the definition of LocalBinder, or by adding one to your declaration of your local variable binder in onServiceConnected
class MyBoundService extends Service{
private final IBinder mBinder = new MyBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class MyBinder extends Binder{
public void doStuff(){
//Stuff
}
//More Binder Methods
}
}
class MyActivity extends Activity{
private MyBinder mBinder;
#Override
protected void onStart(){
Intent intent = new Intent(this, MyBoundService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop(){
unbindService(mConnection);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
mBinder = (TaskBinder) service;
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
private void doStuff(){
if (mBound)
mBinder.doStuff();
}
}
No real need to fiddle around with weak references and whatnot. just be sure to unbind (I didn't in the sample)
If you want to invoke service methods ASAP, just put calls in onServiceConnected, after you set mBinder. otherwise, just invoke from other callbacks (onClick events and whatnot).