I have a service running in my Android application which contains a HashMap that I would like to use in an Activity. So I bound the service to my activity and created a link between to two in order to use the data structure.
This is how I did it so far:
Activity (relevant code):
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
imService = ((IMService.IMBinder) service).getService();
}
public void onServiceDisconnected(ComponentName className) {
imService = null;
Toast.makeText(ViewFlipperTest.this,
R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bindService(new Intent(ViewFlipperTest.this, IMService.class),
mConnection, Context.BIND_AUTO_CREATE);
.
.
.
}
protected void onResume() {
super.onResume();
bindService(new Intent(ViewFlipperTest.this, IMService.class),
mConnection, Context.BIND_AUTO_CREATE);
}
private void getMap(ViewFlipper flipper2) {
Map<Integer, internalChatObj> tempMap = imService.getMapwData();
}
Service (relevant code):
public class IMService extends Service implements IAppManager{
.
.
.
public Map getMapwData(){
if(getNumberOpenChats()>0){
return openChatMapwData;
}
else
return null;
}
}
Each time I try and run the method getMap(), I get a null pointer. I even put a check to see if the imService was null and it was null each time. It seems that it is trying to obtain the data from the Service but the binding to the service is null, is that right? I have a check in the Service to send null if the map is empty, so that shouldn't be a problem.
Any help?
Edit based on Will's tips
New code
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
imService = ((IMService.IMBinder) service).getService();
dialog.dismiss();
}
public void onServiceDisconnected(ComponentName className) {
imService = null;
Toast.makeText(ViewFlipperTest.this,
R.string.local_service_stopped, Toast.LENGTH_SHORT).show();
}
};
public void onCreate(Bundle savedInstanceState) {
dialog = ProgressDialog.show(getApplicationContext(), null,"Loading..", true, false);
}
Edit II
IMBinder Class (Seems standard, no?)
public class IMBinder extends Binder {
public IAppManager getService() {
return IMService.this;
}
}
Is it necessary to have IMService.this instead of this?
It really seems like a timing issue; you shouldn't call the getMap function unless you are certain the imService variable exists, which is after a call to onServiceConnected.
Related
In this, the service is been create repeated times but I need to get the instance of running service so that I can start the timer task. The object of this service can get from any activity. The basic aim is to start the timer task after the particular button click.
public class MainActivity extends Activity {
MyService mService;
boolean mBound = false;
int b=0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_start_timer).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mBound) {
mService.runTimerTask(++b);
}
}
});
}
#Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, MyService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
#Override
protected void onStop() {
super.onStop();
unbindService(mConnection);
mBound = false;
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
MyService.LocalBinder binder = (MyService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
You could create a ServiceManager class and route all methods through that and store it in the application:
public class YourApplication extends Application {
...
private ServiceManager serviceManager = ...;
public ServiceManager getServiceManager() {
return serviceManager;
}
}
In MainActivity:
ServiceManager serviceManager = ((YourApplication)getApplication()).getServiceManager();
MyService runningService = serviceManager.getRunningService();
The ServiceManager would handle unbinding etc and all methods that involve the state of the service. So you just ask the ServiceManager for the running service. Where you have ServiceConnection::onServiceConnected doing the work, delegate that work to the ServiceManager so it can keep state and share it with the rest of the application:
public void onServiceConnected(ComponentName className,
IBinder service) {
((YourApplication)getApplication()).getServiceManager().bind(service);
}
It's a general technique for sharing object state across an application but you'd need to tailor it to fit.
Good Day.I have the simplest scenario which could ever be!I have simple service connection which taken from google in app purchases example and this is my own code
public class MainActivity extends Activity {
IInAppBillingService mService;
ServiceConnection mServiceConn;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
Toast.makeText(MainActivity.this,"disconnected",Toast.LENGTH_LONG).show();
}
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
Toast.makeText(MainActivity.this,"connected",Toast.LENGTH_LONG).show();
}
};
}
#Override
public void onDestroy() {
super.onDestroy();
if (mService != null) {
unbindService(mServiceConn);
}
}
}
For first run it worked and it toasted.But afterwards it will never connect will never trigger onserviceconnected and onservicedisconnected.Doing researches in google i did not found any solution but only questions of familiar without any responsible answers.Thank you beforehand and please tell me what the heck to do?
You did not call bind Service:
create intent and call bindService
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mServiceConn = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
Toast.makeText(MainActivity.this,"disconnected",Toast.LENGTH_LONG).show();
}
#Override
public void onServiceConnected(ComponentName name,
IBinder service) {
mService = IInAppBillingService.Stub.asInterface(service);
Toast.makeText(MainActivity.this,"connected",Toast.LENGTH_LONG).show();
}
};
IntentFilter filter1 = new IntentFilter("IntentFilterName");
bindService(filter1, mConnection, Context.BIND_AUTO_CREATE);
}
I have seen several similar examples here but can't seem to get my service to bind with activity.
I am getting the error
"android.os.binderproxy cannot be cast to IC_CommissaryService".
My service looks like this:
public class IC_CommissaryService extends Service
{
#Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
IC_CommissaryService getService()
{
return IC_CommissaryService.this;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
}
private boolean SendOrderToServer(int orderID)
{
/* do stuff*/
}
}
and my activity looks like this:
public class SubmitOrders extends Activity
{
IC_CommissaryService ICservice;
#Override
public void onCreate(Bundle savedInstanceState)
{
Intent serviceintent = new Intent(this, IC_CommissaryService.class);
serviceintent.putExtra("binded", true);
bindService(serviceintent, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName className, IBinder service)
{
Log.e("TEST", "SERVICE CONNECTED");
try
{
ICservice =(IC_CommissaryService.LocalBinder)service).getService();
for(int i = 0; i < Submitorders.size(); i++)
{
ICservice.SendOrderToServer(Submitorders.get(i).intValue());
}
}
catch(Exception ex)
{
Log.e("Error", "Error connecting service: " + ex.getMessage());
}
}
#Override
public void onServiceDisconnected(ComponentName className)
{
}
};
}
I am getting the error in my activity on the line ICservice =(IC_CommissaryService.LocalBinder)service).getService();
I think I have done the same as people already suggested in other posts so any help please?
thanks
I had the same kind of problem. I just figured it out today. Please look at the parts annotated with <<===== below. I hope it helps.
public class PracticeServiceBindingActivity extends ListActivity {
private MyService.MyBinder service; <<====
....
private ServiceConnection connection = new ServiceConnection( ){
public void onServiceConnected (ComponentName name, IBinder service) {
setService(MyService.MyBinder) service; <<====
....
}
}
public void onCreate(....) {
...
public MyService.MyBinder getService(){ <<=====
return service;
}
public void setService(MyService.MyBinder service) { <<=====
this.service = service;
}
}
}
Quote from the Bound Services documentation:
If your service is private to your own application and runs in the
same process as the client (which is common), you should create your
interface by extending the Binder class and returning an instance of
it from onBind().
Remove the android:process attribute in in AndroidManifest.ml to make the service run in the same process. I had the same problem today it did the trick.
These are the abstract classes that I use to solve this problem:
https://gist.github.com/frenchie4111/6086c6e4327d7936364a
Just extend both these classes with your service and activity (You can change the fragment from a fragment to an activity with ease). And make sure that in your service/fragment onCreate you set the serviceClass like so:
public void onCreate( Bundle savedInstance ) {
super.onCreate( savedInstance );
this.serviceClass = IC_CommissaryService.class;
}
can you give me a simple example of an application with background service which uses bind/unbind methods to start and stop it? I was googling for it for a half-hour, but those examples use startService/stopService methods or are very difficult for me. thank you.
You can try using this code:
protected ServiceConnection mServerConn = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(LOG_TAG, "onServiceConnected");
}
#Override
public void onServiceDisconnected(ComponentName name) {
Log.d(LOG_TAG, "onServiceDisconnected");
}
}
public void start() {
// mContext is defined upper in code, I think it is not necessary to explain what is it
mContext.bindService(intent, mServerConn, Context.BIND_AUTO_CREATE);
mContext.startService(intent);
}
public void stop() {
mContext.stopService(new Intent(mContext, ServiceRemote.class));
mContext.unbindService(mServerConn);
}
Add these methods to your Activity:
private MyService myServiceBinder;
public ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
myServiceBinder = ((MyService.MyBinder) binder).getService();
Log.d("ServiceConnection","connected");
showServiceData();
}
public void onServiceDisconnected(ComponentName className) {
Log.d("ServiceConnection","disconnected");
myService = null;
}
};
public Handler myHandler = new Handler() {
public void handleMessage(Message message) {
Bundle data = message.getData();
}
};
public void doBindService() {
Intent intent = null;
intent = new Intent(this, BTService.class);
// Create a new Messenger for the communication back
// From the Service to the Activity
Messenger messenger = new Messenger(myHandler);
intent.putExtra("MESSENGER", messenger);
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
}
And you can bind to service by ovverriding onResume(), and onPause() at your Activity class.
#Override
protected void onResume() {
Log.d("activity", "onResume");
if (myService == null) {
doBindService();
}
super.onResume();
}
#Override
protected void onPause() {
//FIXME put back
Log.d("activity", "onPause");
if (myService != null) {
unbindService(myConnection);
myService = null;
}
super.onPause();
}
Note, that when binding to a service only the onCreate() method is called in the service class.
In your Service class you need to define the myBinder method:
private final IBinder mBinder = new MyBinder();
private Messenger outMessenger;
#Override
public IBinder onBind(Intent arg0) {
Bundle extras = arg0.getExtras();
Log.d("service","onBind");
// Get messager from the Activity
if (extras != null) {
Log.d("service","onBind with extra");
outMessenger = (Messenger) extras.get("MESSENGER");
}
return mBinder;
}
public class MyBinder extends Binder {
MyService getService() {
return MyService.this;
}
}
After you defined these methods you can reach the methods of your service at your Activity:
private void showServiceData() {
myServiceBinder.myMethod();
}
and finally you can start your service when some event occurs like _BOOT_COMPLETED_
public class MyReciever extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals("android.intent.action.BOOT_COMPLETED")) {
Intent service = new Intent(context, myService.class);
context.startService(service);
}
}
}
note that when starting a service the onCreate() and onStartCommand() is called in service class
and you can stop your service when another event occurs by stopService()
note that your event listener should be registerd in your Android manifest file:
<receiver android:name="MyReciever" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
First of all, two things that we need to understand,
Client
It makes request to a specific server
bindService(new Intent("com.android.vending.billing.InAppBillingService.BIND"),
mServiceConn, Context.BIND_AUTO_CREATE);
here mServiceConn is instance of ServiceConnection class(inbuilt) it is actually interface
that we need to implement with two (1st for network connected and 2nd network not connected) method to monitor network connection state.
Server
It handles the request of the client and makes replica of its own which is private to client only who send request and this raplica of server runs on different thread.
Now at client side, how to access all the methods of server?
Server sends response with IBinder Object. So, IBinder object is our handler which accesses all the methods of Service by using (.) operator.
.
MyService myService;
public ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder binder) {
Log.d("ServiceConnection","connected");
myService = binder;
}
//binder comes from server to communicate with method's of
public void onServiceDisconnected(ComponentName className) {
Log.d("ServiceConnection","disconnected");
myService = null;
}
}
Now how to call method which lies in service
myservice.serviceMethod();
Here myService is object and serviceMethod is method in service.
and by this way communication is established between client and server.
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).