I have a trouble with screen orientation when using AsyncTask even it's inside Service.
My service look like:
public class RequestService extends Service {
private MyBinder binder;
public RequestService(){
binder = new MyBinder(RequestService.this);
}
#Override
public IBinder onBind(Intent intent) {
return binder;
}
public class MyBinder extends Binder{
private final RequestService service;
public MyBinder(RequestService service){
this.service = service;
}
public RequestService getService(){
return this.service;
}
}
public <T> void sendRequest(Request<T> task, INotifyRequest<T> notify){
// Call excute the asynctask and notify result in onPostExcute
new TaskExecutor<T>(task, notify).execute();
}
}
Update: I use my Service like this:
// start the service
final Intent intent = new Intent(context, serviceClass);
context.startService(intent);
// then bound the service:
final Intent intentService = new Intent(context, serviceClass);
// Implement the Service Connection
serviceConnection = new RequestServiceConnection();
context.getApplicationContext().bindService(intentService, serviceConnection,
Context.BIND_AUTO_CREATE);
When orientation changing, the service is unbound then re-bound, the AsyncTask doesn't notify to update UI. I wonder why it could happen even AsyncTask inside the Service?
I have read this post, but I don't want to lock the screen orientation or something like that. I prefer the Service than IntentService as Service 's flexible, I can use it with the Binder to get Service instance.
So, the question is, is there any way to do thread safe inside the Service rather than AsyncTask?
If you use a bound service keep in mind, that the Service will be destroyed if no Activity is bound. I don't know if you unbind in onPause(), but this would destroy your Service at an orientation change.
Because of this you will loose the Service and the reference to the AsyncTask. Furthermore there is no onRetainInstanceState() available for a Service, to save the AsyncTask and grab it again.
Think about an IntentService in this case it would be the proper way. Or if you wanna keep the Service use startService(), to keep it alive while no Activity is bound. Then you can still bind and unbind from the service the way you want.
The next point is to keep a reference of the AsyncTask. Because you have to set your callback again if the Activity was destroyed. Because the callback reference will still be set to the old Activity.
Hope this helps.
Edit:
Well if you read that maybe you consider using a IntentService or something..
Keep a instance of the AsyncTask in the Service and define a setter in your Task for your callback.
If your Activity binds to the service after the orientation change check, whether the AsyncTask is running. If it's running update the callback. You can use your Binder for that.
Related
I have a simple activity which binds a connection to a IntnetService. The service just plays a MediaPlayer. I have implemented onDestroy inside the service. However after pressing back in main activity, I was expecting the service to run as usual as IntentService supposes to run in background in separated thread, but after debugging I realized that onDestory activity calls in IntentService class and destroys the Media Player in it. Where I am wrong probably?
This is my Activity
public class MainActivity extends AppCompatActivity{
private void startAudio() {
Intent intent = new Intent(Intent.ACTION_SYNC, null, this, PlayerService.class);
bound = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
This is my Service:
public class PlayerService extends IntentService{
#Override
public void onDestroy() {
mediaPlayer.pause();
mediaPlayer.reset();
mediaPlayer.release();
}
}
bindService is used to bind to service and service stops itself when it has no clients/connections left.Here,as activity is destroyed,service is also getting destroyed
IntentService is supposed to run in background in separated thread until and unless it has work.But here,it is not getting work and hence getting destroyed.
Intent service has a queue which queues the incoming intents and performs the actions requested by these intents one by one. Once, the queue of this service is empty i.e. it has completed all of the actions requested by the intents it stops and starts again when it receives another intent.
Since, your service does not have any intent to be processed it ceases to exist. What you need here is a Sticky Service which continues even if it does not have any action to perform.
I am totally confused with bound services. My questions are:
What is the meaning of binding?
What does the Binder class do?
What is meant by "returns an IBinder for interacting with the service"?
What is the IBinder object?
How does the onBind() method work?
These are the a questions on bound services. Please explain in detail. I have already read the documentation, but it is still unclear to me.
Bound service:
A bound service is one that allows application components to bind to it by calling bindService() to create a long-standing connection.
Create a bound service when you want to interact with the service from activities and other components in your application or to expose some of your application's functionality to other applications through interprocess communication (IPC).
To create a bound service, implement the onBind() callback method to return an IBinder that defines the interface for communication with the service. Other application components can then call bindService() to retrieve the interface and begin calling methods on the service. The service lives only to serve the application component that is bound to it, so when there are no components bound to the service, the system destroys it. You do not need to stop a bound service in the same way that you must when the service is started through onStartCommand().
IBinder:
To create a bound service, you must define the interface that specifies how a client can communicate with the service. This interface between the service and a client must be an implementation of IBinder and is what your service must return from the onBind() callback method. After the client receives the IBinder, it can begin interacting with the service through that interface.
onBind():
The system invokes this method by calling bindService() when another component wants to bind with the service (such as to perform RPC). In your implementation of this method, you must provide an interface that clients use to communicate with the service by returning an IBinder. You must always implement this method; however, if you don't want to allow binding, you should return null.
this is an example works as completion to the answer above
inside your service class, initialize the IBinder interface with the object created by our inner class (check step 2)
create an inner class extends Binder that has a getter function, to gain access to the service class
in your service class ovveride onBind function, and use it to return the instance we created in step 1
**The code will clear it for you **
public class MyServiceClass extends Service {
//binder given to client
private final IBinder mBinder = new LocalBinder();
//our inner class
public LocalBinder extends Binder {
public MyServiceClass getService() {
return MyServiceClass.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public void doSomeWork(int time){ //we will call this function from outside, which is the whole idea of this **Binding**}
}
Next step is binding itself
in your MainClass or whatever where you want to bind your service,
Defines callbacks for service binding, passed to bindService()
private ServiceConnection serviceConnection = new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
MyServiceClass.LocalBinder binder =(MyServiceClass.LocalBinder)service;
timerService = binder.getService();
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
//what to do if service diconnect
}
};
the moment of binding
Intent intent = new Intent(this, MyServiceClass.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
to unbind the service
unbindService(serviceConnection);
then you call the public function we created before in the Service class using the help of [timerService = binder.getService();]
timerService.doSomeWork(50);
Right now I have an Activity and a local Service. I need to be able to call a service method asynchronously from the activity. The only ways I know of communicating between a service and an activity is through Intents, binding, and AIDL.
I tried binding, but bound service calls are synchronous.
Using intents (modifying the intent passed to startService) doesn't work either because onStartCommand is called on the main thread.
I'd rather not resort to AIDL because it seems rather complicated. Any alternatives I'm missing?
btw Making Asynchronous Service calls in Android does not answer my question
PROLOGUE
Following CommonWare's answer I decided to use a service with a ThreadPoolExecuter
The only ways I know of communicating between a service and an activity is through Intents, binding, and AIDL.
AIDL is binding. AIDL makes it possible to bind between apps.
I tried binding, but bound service calls are synchronous.
Make the call in a background thread, then.
Using intents (modifying the intent passed to startService) doesn't work either because onStartCommand is called on the main thread.
The service can use a background thread, then. For example, an IntentService supplies a background thread, passing the Intent delivered originally to onStartCommand() on to your onHandleIntent() method.
Any alternatives I'm missing?
None that avoid the background thread.
Turns out, an IntentService did not work for me because I needed persistent objects and IntentServices close after all jobs are finished. Instead, I used a service with an Executor, which I made to run all my service requests on a single background thread.
handleMessageHelper handles all requests on a background thread. I use a Messenger to recieve requests, and use a custom Runnable to pass the msg to my handleMessageHelper method
MyService.java (snippet)
ExecutorService background = Executors.newSingleThreadExecutor();
//handles messages from client
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
background.execute(new MessengerRunnable(msg));
super.handleMessage(msg);
}
class MessengerRunnable implements Runnable {
public Message msg;
public MessengerRunnable(Message m) {
super();
msg = m;
}
#Override
public void run() {
handleMessageHelper(msg);
}
}
}
//called from handleMessage in IncomingHandler
//should be run on `background` ExecutorService
public void handleMessageHelper(Message msg) {
Log.d("CASE: ","" + msg.what);
switch (msg.what) {
case SET_ACCOUNT:
Bundle b = msg.getData();
String accountName = b.getString("accountName");
if (accountName != null) {
setAccount(accountName);
}
break;
}
}
final Messenger mMessenger = new Messenger(new IncomingHandler());
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 have been looking for some way to get the ServiceConnection when I start my Service using startService(...).
I haven't found a way, so I have been searching a bit and found this:
Does each Activity need to Bind to a Service & What happens when it was created with startService()
There, Commonsware says that it doesn't matter if I call bindService after the startService call.
So I thought that I first run startService(...) and then directly after do a bindService(...) (so that onServiceConnected is called). But then the Service.onCreate is executed twice. Probably because startService isn't "finished" yet...?
Question is: How do I get a reference to my Service (the IBinder), ie. how do I get the onServiceConnected to fire if I start my Service with startService?
--- EDIT ---
I still do want to know any answers and ideas you might have. I made a "hack" to get around this:
I simply made a static reference (in SRef.java I have public static IBinder myBinder = null), and in my Service.onCreate I simple do
SRef.myBinder = myBinder;
This doesn't seem right to me, so any other ideas on how it is supposed to work would be appreciated.
I use the exact same technique (a samba client service), onCreate is never called twice for me and I get the binder (by connection callback) as I would expect. A new start of activity doesn't fire a onCreate either because previous startService performed the startup of the service already.
Here is my code (might be trivial, but maybe it helps):
Activity (onCreate):
startService(new Intent(this, SambaService.class));
bindService(new Intent(this, SambaService.class), sambaServiceConnection,
Context.BIND_AUTO_CREATE);
Service:
private ServiceBinder mServiceBinder = new ServiceBinder();
public class ServiceBinder extends Binder {
public SambaService getService() {
return SambaService.this;
}
}
public IBinder onBind(Intent intent) {
return mServiceBinder;
}