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.
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.
I looked up on the internet, but couldn't find an example covering my scenario. What I am trying to do is:
1) To start and bind to a service as soon as my activity starts (done)
2) The service then binds itself to another service looking for a user
input from a connected device, and saves a string a string to a variable (done)
3) I would like to send back this string to the activity, so I can check what it
is and based on it to make a network call.
Now number 3) is my challenge. I managed to do it with a Timer that runs for one second and then checks the value written in the service, but somehow this doesn't seem to be the right way and I think that there might be a more mature solution. However, I can't seem to figure it out.
I've taken the code from the documentation and only added the timer. It is just one service in this example that just generates a random number (this will normally be replaced by my second service).
This is the code for the service:
public class LocalService extends Service {
private final IBinder mBinder = new LocalBinder();
private final Random mGenerator = new Random();
public class LocalBinder extends Binder {
LocalService getService() {
return LocalService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public int getRandomNumber() {
return mGenerator.nextInt(100);
}
}
And this is the code in my activity:
public class MainActivity extends AppCompatActivity {
LocalService mService;
boolean mBound = false;
Timer timer;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
timer = new Timer();
}
#Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
timer.schedule(new MyTimerTask(new Handler(), this), 1000, 1000); // run on every second
}
#Override
protected void onStop() {
super.onStop();
if (mBound) {
unbindService(mConnection);
mBound = false;
}
timer.cancel();
timer.purge();
}
private class MyTimerTask extends TimerTask {
Handler handler;
MainActivity ref;
public MyTimerTask(Handler handler, MainActivity ref) {
super();
this.handler = handler;
this.ref = ref;
}
#Override
public void run() {
handler.post(new Runnable() {
#Override
public void run() {
if (mBound) {
int num = ref.mService.getRandomNumber();
// just as an example, raise a toast to see if it works
// but otherwise the value will be handled
Toast.makeText(ref, "number: " + num, Toast.LENGTH_SHORT).show();
}
}
});
}
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
mService = binder.getService();
mBound = true;
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
My question is: is this a good approach (it works) or is it bad and what's the alternative?
You can use LocalBroadcastManager to send broadcasts from your Service to your Activity. For example, in your Service declare:
public static final String BROADCAST_INTENT = "broadcast_intent";
public static final String BROADCAST_VALUE = "broadcast_value";
private LocalBroadcastManager broadcastManager;
public void onCreate() {
super.onCreate();
broadcastManager = LocalBroadcastManager.getInstance(this);
}
Now whenever you want to send a String to your Activity you can do so like this:
private void sendBroadcast(String value) {
Intent intent = new Intent(BROADCAST_INTENT);
intent.putExtra(BROADCAST_VALUE, value);
broadcastManager.sendBroadcast(intent);
}
In your Activity declare a BroadcastReceiver:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
handleIntent(intent);
}
};
Register the receiver when you bind to your Service:
IntentFilter broadcastIntentFilter = new IntentFilter();
broadcastIntentFilter.addAction(StreamService.BROADCAST_INTENT);
LocalBroadcastManager.getInstance(context).registerReceiver((broadcastReceiver), broadcastIntentFilter);
And unregister where you unbind from your Service:
LocalBroadcastManager.getInstance(context).unregisterReceiver(broadcastReceiver);
Now when your service sends the broadcast you can handle it in your Activity:
private void handleIntent(Intent intent) {
if (intent.getAction().equals(StreamService.BROADCAST_INTENT)) {
String value = intent.getStringExtra(StreamService.BROADCAST_VALUE, "default");
}
}
I would like to send back this string to the activity, so I can check what it is and based on it to make a network call.
Use LocalBroadcastManager, greenrobot's EventBus, Square's Otto, or some other in-process event bus implementation. Raise an event when you have changed data. Have the activity register with the bus to find out about the event. Have the activity use the changed data when the change occurs.
is this a good approach
No.
To communicate with Service, what is the different between bindService() and create a instance of service? Why should need to use bindService() to communicate with service? I was confused by it.
(1)
public class BLEService extends Service {
private static BLEService sService;
#Override
public void onCreate() {
super.onCreate();
sService = this;
}
public static BLEService getInstance() {
return sService;
}
}
public class HeartRateActivity extends Activity {
private BLEService mBLEService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBLEService = BLEService.getInstance();
}
}
(2)
public class BLEService extends Service {
private final IBinder mBinder = new LocalBinder();
private BLEService mBLEService;
public class LocalBinder extends Binder {
public MyleService getServerInstance() {
return MyleService.this;
}
}
}
public class HeartRateActivity extends Activity {
private BLEService mBLEService;
private boolean mBounded;
#Override
protected void onStart() {
super.onStart();
Intent mIntent = new Intent(this, BLEService.class);
bindService(mIntent, mConnection, BIND_AUTO_CREATE);
}
ServiceConnection mConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
mBounded = false;
mBLEService = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
mBounded = true;
LocalBinder mLocalBinder = (LocalBinder)service;
mBLEService = mLocalBinder.getServerInstance();
}
};
}
Thanks
Edit: Remove new operator in onCreate() of service
You would not instantiate a Service object via its constructor using the new keyword. A service is intended to be a long-running process that is not necessarily tied to the lifetime of the Activity that wants access to it. As such, services are something that you use an Intent to signal to Android that you wish to run them in the same way that you use Intent objects to signal that you wish to start a new Activity.
Using .bindService() you can signal to Android that you want to attach to a running service (and to implicitly start that service if it isn't running already). Once bound, you can communicate with the service via whichever interfaces it has available.
I've implemented and aidl file as follows:
interface ITaskService{
void addGetNamesJob();
void addUploadJob();
}
I then in my service have done the following:
#Override
public IBinder onBind(Intent intent) {
return new ITaskService.Stub() {
#Override
public void addBeerNamesJob() throws RemoteException {
jobManager.addJobInBackground(new GetNamesTask());
}
#Override
public void addUploadJob() throws RemoteException {
jobManager.addJobInBackground(new UploadRatingTask(wifi, twitter, TWITTER_CONSUMER, TWITTER_CONSUMER_SECRET, /*ctx,*/ accessToken, accessTokenSecret ));
}
};
}
then in my main activity the service is bound as follows:
private void initService() {
Log.i(TAG, "initService()" );
mServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
service = ITaskService.Stub.asInterface((IBinder)iBinder);
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
bound = false;
service = null;
}
};
if(service == null){
Intent it = new Intent();
it.setAction("com.company.taskservice");
bindService(it, mServiceConnection, Context.BIND_AUTO_CREATE);
bound = true;
}
}
private void releaseService() {
if(bound && mServiceConnection!=null && service!=null){
unbindService(mServiceConnection);
}
bound = false;
service = null;
Log.d(TAG, "releaseService(): unbound.");
}
This all works perfectly, I can call on the methods in the service via the interface. However, what I would like to do now is on device boot is start the service, which I written like this:
public class DeviceBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if("android.intent.action.BOOT_COMPLETED".equals(intent.getAction())) {
Intent ratingUpload = new Intent(context, RatingUploaderService.class);
//Toast.makeText(context, "------------->>>>DEVICE BOOT RECEIVER CALLED ---- CONNECTED", Toast.LENGTH_LONG).show();
context.startService(ratingUpload);
}
}
}
I don't want the service to die if the app is A. sent to the background or B. killed. So if it's possible to start the service on boot, it will run independently of the application. However, can I through my aidl file access the methods without binding and if so can someone show me how?
thanks in advance
I'm implementing Service that establishes TCP connection with server and then allows clients to pass messages through this connection. Clients connect to service with bindService call. As a result onServiceConnected called in clients ServiceConnection object. The problem is that onServiceConnected called right after return from bindService, but my Service was not established connection with server at this moment. Can I somehow delay onServiceConnected call while connection was not established? If it is not possible please suggest some good pattern for my case. Thank you.
You should do it as follows:
Service code:
class MyService implements Service {
private boolean mIsConnectionEstablished = false;
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder {
public MyService getService() {
// Return this instance of LocalService so clients can call public
// methods
return MyService.this;
}
}
public interface OnConnectionEstablishedListener {
public void onConnectionEstablished();
}
private OnConnectionEstablishedListener mListener;
#Override
public void onCreate() {
super.onCreate();
new Thread( new Runnable() {
#Override
void run() {
//Connect to the server here
notifyConnectionEstablished();
}
}).start();
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private void notifyConnectionEstablished() {
mIsConnectionEstablished = true;
if(mListener != null) {
mListener.onConnectionEstablished();
}
}
public void setOnConnectionEstablishedListener(
OnConnectionEstablishedListener listener) {
mListener = listener
// Already connected to server. Notify immediately.
if(mIsConnectionEstablished) {
mListener.onConnectionEstablished();
}
}
}
Activity code:
class MyActivity extends Activity implements ServiceConnection,
OnConnectionEstablishedListener {
private MyService mService;
private boolean mBound;
#Override
public void onCreate() {
super.onCreate();
//bind the service here
Intent intent = new Intent(this, MyService.class);
bindService(intent, this, BIND_AUTO_CREATE);
}
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
mService.setOnConnectionEstablishedListener(this);
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
#Override
public void onConnectionEstablished() {
// At this point the service has been bound and connected to the server
// Do stuff here
// Note: This method is called from a non-UI thread.
}
}