Android Service gets destroyed before publishing the result - android

I have one Activity(MainActivity) which starts a Service (FirstService) and FirstService starts another Service (SsecondService). The scenario is that MainActivity should wait for result from FirstService, but this will only send a result when it receives something from SecondService.
The problem is that the "onDestroy()" method of FirstService is called and unregisters SecondService before MainActivity gets the final result.
>> I/FirstService: On create...
>> I/FirstService: Handling intent...
>> I/SecondService: On create...
>> I/SecondService: On handling intent..
>> I/FirstService: OnDestroy receiver...
>> I/SecondService: Handling result ...
>> I/SecondService: Publishing result ...
In my Activity:
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_home) {
// Handle the camera action
} else if (id == R.id.nav_camera) {
} else if (id == R.id.nav_pictures) {
startAction();
}
drawer.closeDrawer(GravityCompat.START);
return true;
}
public void startAction {
Log.i("MainActivity", "Starting FirstService");
Intent intent = new Intent(this, FirstService.class);
intent.setAction(FirstService.ACTION_FETCH_PICTURES);
intent.putExtra(FirstService.EXTRA_ACCOUNT, account);
this.startService(intent);
}
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("MainActivity", "Waiting for onReceive");
Bundle bundle = intent.getExtras();
if (bundle != null) {
resultCode = bundle.getInt(FirstService.RESULT);
switch (resultCode) {
case FirstService.RESULT_CODE_OK: [...]
}
[...]
}
}
}
}
};
#Override
protected void onResume() {
super.onResume();
Log.i("MainActivity", "on resume");
registerReceiver(receiver, new IntentFilter(FirstService.NOTIFICATION));
}
#Override
protected void onPause() {
super.onPause();
Log.i("MainActivity", "Unregister receiver...");
unregisterReceiver(receiver);
}
My FirstService:
#Override
public void onCreate() {
super.onCreate();
Log.i("FirstService", "On create...");
registerReceiver(secondReceiver, new IntentFilter(SecondService.NOTIFICATION_SECOND));
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i("SecondService", "Handling intent...");
if (intent != null) {
final String action = intent.getAction();
// .... some more code
Intent intent = new Intent(this, SecondService.class);
intent.setAction(SecondService.ACTION_FETCH_VALIDITY);
intent.putExtra(SecondService.EXTRA_ACCOUNT, accNumber);
this.startService(intent);
}
}
private void publishResults(int result) {
Log.i("FirstService", "Publishing result...");
Intent intent = new Intent(NOTIFICATION);
intent.putExtra(RESULT, result);
sendBroadcast(intent);
}
public BroadcastReceiver secondReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("SecondService", "On receiving result...");
Bundle bundle = intent.getExtras();
if (bundle != null) {
result = bundle.getInt(SecondService.RESULT);
switch (result) {
//[....]
}
publishResults(result);
}
}
};
#Override
public void onDestroy() {
super.onDestroy();
Log.i("SecondService", "OnDestroy receiver...");
unregisterReceiver(secondReceiver);
}
I am struggling with this for hours now and couldn't find a solution. What am I doing wrong and what should I do to make it work? Any ideas are well welcomed.
Cheers!

Basically, you have two options:
Use a static BroadcastReceiver
You can work with a standalone BroadcastReceiver. It has to be registered in the application Manifest. Whenever your SecondService wants to publish something it broadcasts the results just like before. The receiver could then start another Service to process the results.
See also the documentation
If you read the link you'll find out that there are disadvantages to using a static BroadcastReceiver. So why don't you just skip the receiver and start some (third?) Service directly from SecondService ?
Which brings us to the second option:
Implement the first Service as "normal" Service
Right now, FirstService is extending IntentService.
If FirstService was implemented as a started Service then you could make sure it remains alive as long as needed by returning START_STICKY from Service.onStartCommand(). You'd continue using a dynamic BroadcastReceiver or even access the Service directly via onStartcommand(). And as soon as it is finished your Service would unregister the receiver and call stopSelf().
The advantage of an IntentService is that it does not do its work on the UI thread. But a started Service can use AsyncTask, so it can avoid freezing the UI just as well.

You should not use IntentService. If you look at the documentation of IntentService, service starts and do its work in onHandleIntent and then finishes. It does not wait for anything. That's why FirstService is being destroyed because it has finished its work. Instead, use the Simple Service and in handler thread do your work. This is the case your service will continue to run.

Related

Broadcast Receiver null pointer Android

I keep getting the following error:
> java.lang.NullPointerException: Attempt to invoke virtual method 'void android.content.BroadcastReceiver.onReceive(android.content.Context, android.content.Intent)' on a null object reference
When I try to receive a broadcast from my service class.
Service:
#Override
public void onDestroy(){
super.onDestroy();
Intent intent = new Intent("UpdateLocation");
intent.putExtra("Location",journ);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
That is the code to send the broadcast sending a custom object(Journ) to my main activity.
Main:
#Override
protected void onCreate(Bundle savedInstanceState)
LocalBroadcastManager.getInstance(this).registerReceiver(
mReceiver, new IntentFilter("UpdateLocation"));
//Listen for service to send location data
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
temp= intent.getExtras().getParcelable("Location");
}
};
}
#Override
public void onPause() {
if (!tracking) {
finish();
}
//Run service in background to keep track of location
startService(new Intent(this, locationService.class));
super.onPause();
}
#Override
public void onResume() {
if (!tracking) {
return;
}
if (mGoogleApiClient != null) {
//Reconnect to google maps
mGoogleApiClient.reconnect();
}
stopService(new Intent(this, locationService.class));
super.onResume();
}
I am not sure how to go about doing this, I am trying to pass the object from my service class which runs when my app is in the background and when it resumes the service should stop and sends the data it gathered to my main activity.
This, however, doesn't work, any ideas?
In case people were wondering my on create method does contain more code, but I didn't think it necessary to include.
In onCreate mReciever is a null object (if you didn't assign it earlier), so you should assign it before registering a receiver.
Change this part:
if (mReceiver == null) {
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
temp= intent.getExtras().getParcelable("Location");
}
};
}
//Listen for service to send location data
LocalBroadcastManager.getInstance(this)
.registerReceiver(mReceiver, new IntentFilter("UpdateLocation"));

Android asynchronous UI Updating from service fails

I have a very strange problem for updating UI. I have a foreground started bounded service which my main process in background. When I start app, I like to check if service is already running and change state of a toggle button. For this problem, I bind to my started service when starting app in OnResume() and service sends a value back to my app which shows running status of service and I update UI based on this value. But the problem is that UI is not updated in this situation.
Because this bug is shown in a very complex situation, I have written a sample code that reproduce this problem. Here are these codes (sorry for bad names and missing a lot of error checks, I have quickly written this code just to reproduce problem). I have discussed each code a little as an overview.
activity_main layout:
<ToggleButton
android:id="#+id/ui_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOff="Off State"
android:textOn="On State"
android:checked="false" />
<Button
android:id="#+id/start_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Start"/>
MyTestService.java
At first, this is my sample foreground started bounded service. As you see, when we start service, we create a foreground service which just runs a small thread that toggles a mStatus variable every 10 seconds for 10 times and then stops. Whenever we bind to this service, we use ResultReceiver which is sent through binding intent in order to send mStatus to app. We also allow rebinding, because app may be closed several times and reopened again.
public class MyTestService extends Service {
private volatile boolean mStatus = false;
private MyThread mTh = new MyThread();
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
mTh.start();
Intent notintent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notintent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentText("Test").setContentIntent(pendingIntent).setContentTitle("title").setSmallIcon(R.drawable.ic_launcher);
Notification notification = builder.build();
startForeground(100, notification);
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
return null;
}
#Override
public boolean onUnbind(Intent intent) {
return true;
}
#Override
public void onRebind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
}
public class MyThread extends Thread {
#Override
public void run() {
try {
for (int i = 0; i < 10; i++ ) {
Thread.sleep(10000);
mStatus = !mStatus;
Log.i("ASD", String.format("%d", mStatus? 1 : 0));
}
}catch (Exception e) {
}
stopSelf();
}
}
}
MyServiceAccessClass.java
This class is used for accessing service. start() starts service, bind() and unbind() are using for binding and unbinding service. mRecv is the ResultReceiver which send to service while binding and is used for getting status. When status is received after binding, ResultReceiver updates UI via a callback.
public class MyServiceAccessClass {
private MyResultRecv mRecv = new MyResultRecv(new Handler(Looper.getMainLooper()));
private OnUpdateRequest mCallback = null;
private Context mCtx = null;
private ServiceConnection mCon = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {}
#Override
public void onServiceDisconnected(ComponentName name) {}
};
public MyServiceAccessClass(Context ctx) {
mCtx = ctx;
mCallback = (OnUpdateRequest)ctx;
}
public void bind() {
Intent intent = new Intent(mCtx, MyTestService.class);
intent.setAction("checkstatus");
intent.putExtra("myrecvextra", mRecv);
mCtx.bindService(intent, mCon, Context.BIND_AUTO_CREATE);
}
public void unbind() {
mCtx.unbindService(mCon);
}
public void start() {
Intent intent = new Intent(mCtx, MyTestService.class);
mCtx.startService(intent);
}
private class MyResultRecv extends ResultReceiver {
public MyResultRecv(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == 0) {
mCallback.updateUi(resultData.getBoolean("status"));
}
}
}
}
MainActivity.java
This is main class of test app. Start button starts service. and this class binds in OnResume() and unbinds in OnPause(). If app is run when service is already running and its mStatus is true, then updateUi will be called with true value and sets status of toggle button.
interface OnUpdateRequest {
public void updateUi(boolean state);
}
public class MainActivity extends AppCompatActivity implements OnUpdateRequest{
private MyServiceAccessClass mTest = new MyServiceAccessClass (this);
private ToggleButton mBtn = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn = (ToggleButton)findViewById(R.id.ui_btn);
((Button)findViewById(R.id.start_btn)).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTest.start();
}
});
}
#Override
protected void onResume() {
super.onResume();
mTest.bind();
}
#Override
protected void onPause() {
super.onPause();
mTest.unbind();
}
#Override
public void updateUi(boolean state) {
mBtn.setChecked(state);
}
}
Ok, now in theory everything is all right. But if you try to use this code, when service is started and mStatus is true, toggle button's setChecked() will be called with true (which is correct till now) but UI will not be updated to show correct text and status. Funny part is that if you run isChecked for this toggle button, it will return true, but UI shows something else.
Any idea why this happens? Sorry for a lot of codes, this problem occured is this complex situation.
Update
I noticed something that I should mention. if I use isChecked right after setCheck, I get true which is correct. But if I use isChecked again some time later (for example in another button event handler), it returns false while I have not called setChecked anymore. I think this situation is related to my problem but I don't know how this is happened.
In addition, I think this problem is related to updating UI when you are in binding process to a service. because if I try to update app main UI with same ResultReceiver when I'm not in binding process, everything works correctly.
Possibly need to call View.requestLayout() or View.forceLayout() on the buttons View to refresh the buttons state.
I finally found the problem with my code. It took me a lot of time to solve this problem, so I post it here for other android developers.
Sending result back from a service is somehow obvious via ResultReceiver. But most of examples in internet does not show service rebinding, and I never found sending result back after rebinding service.
OK, now what is the problem? look at the following part of code from my service:
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
return null;
}
#Override
public boolean onUnbind(Intent intent) {
return true;
}
#Override
public void onRebind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
ResultReceiver recv = (ResultReceiver)intent.getParcelableExtra("myrecvextra");
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
recv.send(0, data);
}
}
This is a common approach for making a rebinding service, based on simple binding to service that you find in internet. This has been done with returning true in onUnbind() and using onRebind(). But this approach is completely wrong.
Why? Because of a strange design in android. In Service OnRebind(), there is a small 18 word comment:
Note that any extras that were included with the Intent at that point
will not be seen here.
Now What this means? It means that extra which carries ResultReceiver will not be available on rebinding which in turn means that results will not be sent back after rebinding. But for unknown reason, this code does not make any exception and you even see result back in app while debugging, so it is super ambiguous why this code does not work.
Now what is the solution? Never send ResultReceiver when you bind to a service with bindService() intent. Even though this is correct for a non-rebinding service, but I highly suggest to avoid it. Send ResultReceiver via a separate message to service when onServiceConnected is called and then everything works like a piece of cake. Here are my modifications for code:
MyTestService.java
public static int SERVICE_SET_RECV = 1;
public static String SERVICE_RECV = "SERVICE_RECV";
private Messenger mMessenger = new Messenger(new MyHandler(this));
private ResultReceiver mRecv = null;
#Nullable
#Override
public IBinder onBind(Intent intent) {
if (intent != null && intent.getAction().equals("checkstatus")) {
return mMessenger.getBinder();
}
return null;
}
#Override
public boolean onUnbind(Intent intent) {
mRecv = null;
return true;
}
#Override
public void onRebind(Intent intent) {}
public void setRecv(ResultReceiver recv) {
mRecv = recv;
// Example to send some result back to app
Bundle data = new Bundle();
data.putBoolean("status", mStatus);
mRecv.send(0, data);
}
private static class MyHandler extends Handler {
private final WeakReference<MyTestService> mService;
public MyHandler(MyTestService service) {
mService = new WeakReference<>(service);
}
#Override
public void handleMessage(Message msg) {
MyTestService service = mService.get();
Bundle data = msg.getData();
switch (msg.what) {
case SERVICE_SET_RECV: {
ResultReceiver recv = data.getParcelable(SERVICE_RECV);
service.setRecv(recv);
break;
}
default:
super.handleMessage(msg);
}
}
}
MyServiceAccessClass.java
private ServiceConnection mCon = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
Messenger messenger = new Messenger(service);
Bundle data = new Bundle();
data.putParcelable(MyTestService.SERVICE_RECV, mRecv);
Message msg = Message.obtain(null, MyTestService.SERVICE_SET_RECV, 0, 0);
msg.setData(data);
messenger.send(msg);
}
#Override
public void onServiceDisconnected(ComponentName name) {}
};
public void bind() {
Intent intent = new Intent(mCtx, MyTestService.class);
mCtx.bindService(intent, mCon, Context.BIND_AUTO_CREATE);
}
Finding this ridiculous problem took me a lot of time. I wish everyone likes this solution and solves a lot of problems for rebinding a service.

OnReceive() method auto triggered

I have created the one receiver inside an Activity for when internet is connected auto calling web service.
Code like
//Create receiver for while network will come auto call webservice
private BroadcastReceiver mConnReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
boolean noConnectivity = intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
if (!noConnectivity) {
bar.setVisibility(View.VISIBLE);
callAuthorizeWebservice();
} else {
bar.setVisibility(View.INVISIBLE);
Toast.makeText(SplashScreenActivity.this, "Check Your Internet connection", Toast.LENGTH_LONG).show();
}
}
};
#Override
protected void onStop() {
super.onStop();
unregisterReceiver(mConnReceiver);
}
#Override
protected void onStart() {
super.onStart();
this.registerReceiver(this.mConnReceiver,
new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
}
When I open that Activity the onReceive() is method called everytime.
How to avoid calling it the first time (when opening that Activity)?
The broadcast is sent when you register for the first time(cf sticky broadcast), a solution is to use isInitialStickyBroadcast in the onReceive callback of your BroadcastReceiver to know if you are actually proceeding a sticky broadcast and act accordingly (BroadcastReceiver : isInitialStickyBroadcast)

Communication between Activity and Service

I am trying to make my own MusicPlayer for android. Where i came to a problem is running some things in background. Main activity manages GUI and up to now all the songs are playing. I wanted to separate GUI and music playing classes. I want to put music managing part in Service and leave other things as they are now.
My problem is that i can't organize communication between Activity and Service as lot of communication is happening between them including moving objects in both directions. I tried many techniques that I searched here on Stack Overflow but every time I had problems. I need Service to be able to send objects to Activity and vice versa. When I add widget i also want it to be able to communicate with Service.
Any tips are appreciated, if you need source code place comment bellow but now in this transition it became chaotic.
Is there any more advanced tutorial on this than calling one method that returns random number from service? :P
EDIT: Possible solution is to use RoboGuice library and move objects with injection
I have implemented communication between Activity and Service using Bind and Callbacks interface.
For sending data to the service I used Binder which retruns the Service instace to the Activity, and then the Activity can access public methods in the Service.
To send data back to the Activity from the Service, I used Callbacks interface like you are using when you want to communicate between Fragment and Activity.
Here is some code samples for each:
The following example shows Activity and Service bidirectional relationship:
The Activity has 2 buttons:
The first button will start and stop the service.
The second button will start a timer which runs in the service.
The service will update the Activity through callback with the timer progress.
My Activity:
//Activity implements the Callbacks interface which defined in the Service
public class MainActivity extends ActionBarActivity implements MyService.Callbacks{
ToggleButton toggleButton;
ToggleButton tbStartTask;
TextView tvServiceState;
TextView tvServiceOutput;
Intent serviceIntent;
MyService myService;
int seconds;
int minutes;
int hours;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serviceIntent = new Intent(MainActivity.this, MyService.class);
setViewsWidgets();
}
private void setViewsWidgets() {
toggleButton = (ToggleButton)findViewById(R.id.toggleButton);
toggleButton.setOnClickListener(btListener);
tbStartTask = (ToggleButton)findViewById(R.id.tbStartServiceTask);
tbStartTask.setOnClickListener(btListener);
tvServiceState = (TextView)findViewById(R.id.tvServiceState);
tvServiceOutput = (TextView)findViewById(R.id.tvServiceOutput);
}
private ServiceConnection mConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className,
IBinder service) {
Toast.makeText(MainActivity.this, "onServiceConnected called", Toast.LENGTH_SHORT).show();
// We've binded to LocalService, cast the IBinder and get LocalService instance
MyService.LocalBinder binder = (MyService.LocalBinder) service;
myService = binder.getServiceInstance(); //Get instance of your service!
myService.registerClient(MainActivity.this); //Activity register in the service as client for callabcks!
tvServiceState.setText("Connected to service...");
tbStartTask.setEnabled(true);
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
Toast.makeText(MainActivity.this, "onServiceDisconnected called", Toast.LENGTH_SHORT).show();
tvServiceState.setText("Service disconnected");
tbStartTask.setEnabled(false);
}
};
View.OnClickListener btListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v == toggleButton){
if(toggleButton.isChecked()){
startService(serviceIntent); //Starting the service
bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE); //Binding to the service!
Toast.makeText(MainActivity.this, "Button checked", Toast.LENGTH_SHORT).show();
}else{
unbindService(mConnection);
stopService(serviceIntent);
Toast.makeText(MainActivity.this, "Button unchecked", Toast.LENGTH_SHORT).show();
tvServiceState.setText("Service disconnected");
tbStartTask.setEnabled(false);
}
}
if(v == tbStartTask){
if(tbStartTask.isChecked()){
myService.startCounter();
}else{
myService.stopCounter();
}
}
}
};
#Override
public void updateClient(long millis) {
seconds = (int) (millis / 1000) % 60 ;
minutes = (int) ((millis / (1000*60)) % 60);
hours = (int) ((millis / (1000*60*60)) % 24);
tvServiceOutput.setText((hours>0 ? String.format("%d:", hours) : "") + ((this.minutes<10 && this.hours > 0)? "0" + String.format("%d:", minutes) : String.format("%d:", minutes)) + (this.seconds<10 ? "0" + this.seconds: this.seconds));
}
}
And here is the service:
public class MyService extends Service {
NotificationManager notificationManager;
NotificationCompat.Builder mBuilder;
Callbacks activity;
private long startTime = 0;
private long millis = 0;
private final IBinder mBinder = new LocalBinder();
Handler handler = new Handler();
Runnable serviceRunnable = new Runnable() {
#Override
public void run() {
millis = System.currentTimeMillis() - startTime;
activity.updateClient(millis); //Update Activity (client) by the implementd callback
handler.postDelayed(this, 1000);
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//Do what you need in onStartCommand when service has been started
return START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
//returns the instance of the service
public class LocalBinder extends Binder{
public MyService getServiceInstance(){
return MyService.this;
}
}
//Here Activity register to the service as Callbacks client
public void registerClient(Activity activity){
this.activity = (Callbacks)activity;
}
public void startCounter(){
startTime = System.currentTimeMillis();
handler.postDelayed(serviceRunnable, 0);
Toast.makeText(getApplicationContext(), "Counter started", Toast.LENGTH_SHORT).show();
}
public void stopCounter(){
handler.removeCallbacks(serviceRunnable);
}
//callbacks interface for communication with service clients!
public interface Callbacks{
public void updateClient(long data);
}
}
Update: July 10 2016
IMO I think using BroadcastReceiver for custom events is better way
as the Messengers mentioned don't handle activity recreation on device
rotation as well as possible memory leaks.
You may create custom BroadCast Receiver for events in the activity, Then you may also use Messengers.
In your Activity
create a MessageHandler class as
public static class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case HIDE:
progressBar.setVisibility(View.GONE);
break;
case SHOW:
progressBar.setVisibility(View.VISIBLE);
break;
}
}
}
Now you can have it's instance as
public static Handler messageHandler = new MessageHandler();
Start your Service with this Handler object as an extra data as
Intent startService = new Intent(context, SERVICE.class)
startService.putExtra("MESSENGER", new Messenger(messageHandler));
context.startService(startService);
In your Service you receive this object from the intent and initialize the Messenger variable in Service as
private Messenger messageHandler;
Bundle extras = intent.getExtras();
messageHandler = (Messenger) extras.get("MESSENGER");
sendMessage(ProgressBarState.SHOW);
And then write a method sendMessage to send messages to activity.
public void sendMessage(ProgressBarState state) {
Message message = Message.obtain();
switch (state) {
case SHOW :
message.arg1 = Home.SHOW;
break;
case HIDE :
message.arg1 = Home.HIDE;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
The sample code above shows and hides a ProgressBar in Activity as messages are received from Service.
Intents are good solution for communication between Activitiy and Service.
A fast solution for receive intents in your service is subclassing IntentService class. It handles asynchronous requests expressed as Intents using a queue and worker thread.
For communication from service to Activity you can broadcast the intent but instead of using normal sendBroadcast() from Context, a more efficent way is to use LocalBroadcastManager from support library.
Example service.
public class MyIntentService extends IntentService {
private static final String ACTION_FOO = "com.myapp.action.FOO";
private static final String EXTRA_PARAM_A = "com.myapp.extra.PARAM_A";
public static final String BROADCAST_ACTION_BAZ = "com.myapp.broadcast_action.FOO";
public static final String EXTRA_PARAM_B = "com.myapp.extra.PARAM_B";
// called by activity to communicate to service
public static void startActionFoo(Context context, String param1) {
Intent intent = new Intent(context, MyIntentService.class);
intent.setAction(ACTION_FOO);
intent.putExtra(EXTRA_PARAM1, param1);
context.startService(intent);
}
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_PARAM_A);
// do something
}
}
}
// called to send data to Activity
public static void broadcastActionBaz(String param) {
Intent intent = new Intent(BROADCAST_ACTION_BAZ);
intent.putExtra(EXTRA_PARAM_B, param);
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.sendBroadcast(intent);
}
}
Example Activity
public class MainActivity extends ActionBarActivity {
// handler for received data from service
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MyIntentService.BROADCAST_ACTION_BAZ)) {
final String param = intent.getStringExtra(EXTRA_PARAM_B);
// do something
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter();
filter.addAction(MyIntentService.BROADCAST_ACTION_BAZ);
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.registerReceiver(mBroadcastReceiver, filter);
}
#Override
protected void onDestroy() {
LocalBroadcastManager bm = LocalBroadcastManager.getInstance(this);
bm.unregisterReceiver(mBroadcastReceiver);
super.onDestroy();
}
// send data to MyService
protected void communicateToService(String parameter) {
MyIntentService.startActionFoo(this, parameter);
}
}
I think there is a problem with the correct answer. I have not enough reputation to comment on it.
Right in the answer:
Activity call bindService() to get pointer to Service is ok. Because service context is maintained when connection is maintained.
wrong in the answer:
service pointer to Activity class to call back is bad way. Activity instance maybe not null during Activity context is being Release => exception here.
solution for the wrong in the answer:
service send intent to Activity. and Activity receiver intent via BroadcastReceiver.
Note:
in this case, Service and Activity in the same Process, you should use LocalBroadcastManager to send intent. It make performance and security better
This is a simple example of communication between activity and service
Activity
MyReceiver myReceiver; //my global var receiver
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layourAwesomexD);
registerReceiver();
}
//When the activity resume, the receiver is going to register...
#Override
protected void onResume() {
super.onResume();
checkStatusService(); // verficarStatusServicio(); <- name change
registerReceiver();
}
//when the activity stop, the receiver is going to unregister...
#Override
protected void onStop() {
unregisterReceiver(myReceiver); //unregister my receiver...
super.onStop();
}
//function to register receiver :3
private void registerReceiver(){
//Register BroadcastReceiver
//to receive event from our service
myReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(MyService.SENDMESAGGE);
registerReceiver(myReceiver, intentFilter);
}
// class of receiver, the magic is here...
private class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
//verify if the extra var exist
System.out.println(arg1.hasExtra("message")); // true or false
//another example...
System.out.println(arg1.getExtras().containsKey("message")); // true or false
//if var exist only print or do some stuff
if (arg1.hasExtra("message")) {
//do what you want to
System.out.println(arg1.getStringExtra("message"));
}
}
}
public void checkStatusService(){
if(MyService.serviceStatus!=null){
if(MyService.serviceStatus == true){
//do something
//textview.text("Service is running");
}else{
//do something
//textview.text("Service is not running");
}
}
}
Service
public class MyService extends Service {
final static String SENDMESAGGE = "passMessage";
public static Boolean serviceStatus = false;
#Override
public void onCreate() {
super.onCreate();
serviceStatus=true;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {return null;}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//you service etc...
passMessageToActivity("hello my friend this an example of send a string...");
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
passMessageToActivity("The service is finished, This is going to be more cooler than the heart of your ex...");
System.out.println("onDestroy");
serviceStatus=false;
}
private void passMessageToActivity(String message){
Intent intent = new Intent();
intent.setAction(SENDMESAGGE);
intent.putExtra("message",message);
sendBroadcast(intent);
}
}
if we don't unregister BroadcastReceiver we will have an error, you need to unregister when the activity go onPause, onStop, onDestroy...
if you don't register BroadcastReceiver when you back to activity, it will not listen anything from the service... the service will send information to BroadcastReceiver but it will not receive anything because it isn't registered.
When you create more than one service, the following services are going to begin in onStartCommand.
You can pass information to service with intent and you get it in onStartCommand
Difference about return in onStartCommand: Difference between START_STICKY and START_REDELIVER_INTENT? and check the official website of google: Services
The best way in this case is to communicate by doing broadcasting from your service for different actions and receiving it in your activity. You can create a custom broadcast and send some codes defining specific events like complete, change, prepare etc...
Most easy and efficient way will be using EventBus from GreenRobot.
Use simple 3 steps:
1 Define events
public static class MessageEvent { /* Additional fields if needed */ }
2 Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode:
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {/* Do something */};
Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
3 Post events:
EventBus.getDefault().post(new MessageEvent());
Very easy yet powerful way is to use EventBus you can add it to your gradle build and enjoy the easy publisher/subscriber pattern .

Misleading documentation on BroadcastReceiver?

Does anyone know what this line supposed to mean in the Android documentation (enter link description here): "
Note: If registering a receiver in your Activity.onResume() implementation, you should unregister it in Activity.onPause(). (You won't receive intents when paused, and this will cut down on unnecessary system overhead).
The first sentence is clear, one should release resources if they aren't needed. But what about the bracketed text? Apparently an app in the background receives the broadcast intents if it is stopped (At least on Android 4.2). It won't get the broadcast when it is destroyed. Code to try:
public class MyActivity extends Activity {
private BroadcastReceiver mBroadcastReceiver;
String a = "1234";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("|BR", "onReceive() - 1" + " intent: " + intent);
a = intent.getStringExtra("ASDF");
}
};
this.registerReceiver(mBroadcastReceiver, new IntentFilter(MyService.RECEIVE));
}
#Override
protected void onResume() {
super.onResume();
Log.d("|BR", "onResume()" + " a: " + a);
}
#Override
protected void onPause() {
super.onPause();
Log.d("|BR", "onPause()");
}
/** Event handler for a button in the main.xml */
public void createService(View view) {
Log.d("|BR", "createService()");
Intent intent = new Intent(MyService.DO);
this.startService(intent);
}
}
public class MyService extends Service {
public static final String DO = MyService.class.getName() + ".DO";
public static final String RECEIVE = MyService.class.getName() + ".RECEIVE";
public Executor executor;
#Override
public void onCreate() {
super.onCreate();
executor = Executors.newSingleThreadExecutor();
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
String action = intent.getAction();
Log.d("|BR", "onStartCommand() - 1");
if(DO.equals(action)) {
executor.execute(new MyRunnable());
Log.d("|BR", "onStartCommand() - 2");
}
return START_NOT_STICKY;
}
#Override
public android.os.IBinder onBind(final Intent intent) {
return null;
}
private class MyRunnable implements Runnable {
#Override
public void run() {
Log.d("|BR", "run() - 1");
try {
Thread.sleep(1000 * 8);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("|BR", "run() - 2");
Intent intent = new Intent(RECEIVE);
intent.putExtra("ASDF", "QWER");
MyService.this.sendBroadcast(intent);
Log.d("|BR", "run() - 3");
}
}
}
Press the button, press home button, wait 8 seconds. Result:
09:05:48.622 D/|BR: onResume() a: 1234
09:05:53.297 D/|BR: createService()
09:05:53.297 D/|BR: onStartCommand() - 1
09:05:53.307 D/|BR: onStartCommand() - 2
09:05:53.307 D/|BR: run() - 1
09:05:54.558 D/|BR: onPause()
09:06:01.306 D/|BR: run() - 2
09:06:01.316 D/|BR: onReceive() - 1 intent: Intent { act=com.example.broadcastReceive.MyService.RECEIVE flg=0x10 (has extras) }
09:06:01.316 D/|BR: run() - 3
09:06:14.139 D/|BR: onResume() a: QWER
It simply means that you won't receive broadcasts if you've unregistered the BroadcastReceiver in your onPause().
Dependening on what you do in the receiver, it might be very CPU/Battery consuming. If it is, you want it to be executed as rarely as possible and thats what this sentence wants to say.
Have you ever ran code, that receives GPS information? It lets your battery live for at max 2 hours. So, every second counts, when this is switched on. onResume and onPause are best places to turn on/off this feature. Using onStart, onStop or even onCreate, onDestroy would mean, that the feature is turned on (and consumes battery) longer then necessary.

Categories

Resources