I have a service which is running in the background and an Activity that opens up every couple of minutes. When the activity opens the user pushes a button which needs to send a message to the service. The problems is that after the user pushes the button the activity closes. It appears that the activity is closing before the Activity can bind to the service so I am not able to send a message using my Messenger.
Is the best way to handle this to have the service listen to a broadcast receiver and then trigger that broadcast from my activity?
Thanks for any help you can offer!
-Nathan
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.end_activity_button:
endActivity();
break;
}
}
private void endActivity() {
sendMessageToService(ActivityService.MSG_CANCEL_ACTIVITY);
getActivity().finish();
}
/**
* Send data to the service
*
* #param intvaluetosend The data to send
*/
private void sendMessageToService(int intvaluetosend) {
if (mIsBound) {
if (mServiceMessenger != null) {
try {
Message msg = Message.obtain(null, intvaluetosend, 0, 0);
msg.replyTo = mMessenger;
mServiceMessenger.send(msg);
} catch (RemoteException e) {
}
}
}
}
Edit I have implemented Ricardo's solution but I am using localbroadcasts. Everything is now working properly. Thanks for the help!
Is this mServiceMessenger a service of yours to send a message? I have exactly the same scenario as you but I do it differently.
On my Service, I register a BroadcastReceiver like in this post: https://stackoverflow.com/a/4805733/362298
Then, in my activity, whenever I need to send a message, I simply call:
Intent myIntent = new Intent("MY_ACTION_FOR_THE_SERVICE_TO_HANDLE");
//Put some extra here
sendBroadcast(myIntent);
I actually do this right before calling finish() on my activity as well. Is there any reason why you are not using sendBroadcast?
Related
I have an android activity and a corresponding service, where the activity is just a UI and the service calculates what to display. The service is bound to the activity.
First I establish a connection:
in the Activity.java file:
final Messenger _messenger = new Messenger(new IncomingHandler(new WeakReference<>(this)));
Messenger _serviceMessenger = null;
private ServiceConnection _connection = new ServiceConnection()
{
#Override
public void onServiceConnected(ComponentName name, IBinder service)
{
_serviceMessenger = new Messenger(service);
try
{
// sending the initial welcome message
Message m = Message.obtain(null, ForegroundService.BIND_SERVICE);
m.replyTo = _messenger;
_serviceMessenger.send(m);
}
catch(RemoteException ex)
{
_serviceMessenger = null;
}
}
#Override
public void onServiceDisconnected(ComponentName name)
{
_serviceMessenger = null;
}
};
private static class IncomingHandler extends Handler
{
private WeakReference<MyActivity> _parent;
IncomingHandler(WeakReference<MyActivity> parent)
{
super();
_parent = parent;
}
#Override
public void handleMessage(Message msg)
{
Log.i(LOG_TAG, "Activity: Received message");
MyActivity ta = _parent.get();
switch(msg.what)
{
case ForegroundService.LOCATION_UPDATED:
if(msg.obj == null)
{
Log.e(LOG_TAG, "Activity: msg null");
ta.setTexts("", null, null);
}
else
{
Log.i(LOG_TAG, "Activity: msg ok");
LocWithName loc = (LocWithName)msg.obj;
ta.setTexts(loc.getName(), Double.toString(loc.getHossz()), Double.toString(loc.getSzel()));
Chronometer chronometer = ta.findViewById(R.id.chronom);
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
}
break;
case ForegroundService.LOST_GPS:
ta.setTexts("", "unknown", "unknown");
break;
default:
super.handleMessage(msg);
break;
}
}
}
in activity onCreate:
Intent startIntent = new Intent(MyActivity.this, ForegroundService.class);
startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
bindService(startIntent, _connection, Context.BIND_AUTO_CREATE);
startService(startIntent);
and in the service java file:
final Messenger _messenger = new Messenger(new IncomingHandler(new WeakReference<>(this)));
Messenger _activityMessenger = null;
private static class IncomingHandler extends Handler
{
WeakReference<ForegroundService> _parent;
IncomingHandler(WeakReference<ForegroundService> parent)
{
super();
_parent = parent;
}
#Override
public void handleMessage(Message msg) {
// received welcome message, now we know who to reply to
if(msg.what == ForegroundService.BIND_SERVICE)
{
_parent.get()._activityMessenger = msg.replyTo;
Log.d(LOG_TAG, "received reply address for messenger"); // after 1st edit
}
else
{
super.handleMessage(msg);
}
}
}
private void sendMessageToUI(int message, LocWithName newLoc)
{
Log.i(LOG_TAG, "Service: sending message to UI");
if(_activityMessenger != null)
{
Log.d(LOG_TAG, "messenger not null"); // after 1st edit
try
{
_activityMessenger.send(Message.obtain(null, message, newLoc));
Log.i(LOG_TAG, "Service: message sent");
}
catch(RemoteException ex)
{
// activity is dead
_activityMessenger = null;
}
}
}
then I start sending messages via the sendMessageToUI() function periodically from the service, namely every 5 seconds. The service's onStartCommand runs the first UI update immediately, which reschedules itself for every other iteration.
What I know:
the first immediate "UI update" in the service does run as logcat shows me the "sending message to UI" text at the correct time
all other updates run
all other updates deliver their messages successfully (which means that the first was stopped by _serviceMessenger being null, not the RemoteException, because the catch block would stop all later messages)
the welcome message from the activity to the service arrives as it is a necessity for further replies from the service
What I have tried:
in the activity, first bind then start the service (example code is in this state, initially it was the other way around), so _activityMessenger isn't null by the time it has to send first message
send a "burner message" so that is the one that doesn't get delivered instead of actually important messages
search google for similar problems to no avail - where there are problems, it doesn't work at all, not just the first time around
search this site for similar problems, same result as with google
Since there are five seconds between the first and the second message, I suspect it is an issue with the speed of initializing something, but I couldn't get further than that. So what exactly happens here and why doesn't it work?
EDIT 1: at the suggestion of #pskink, I added Log.d()-s. It turns out the activity only sends the "welcome message" with reply address after the first run of the UI updater despite being called earlier than startService.
Also, the code sending the messages, after #pskink asking:
in service class:
final Handler handler = new Handler();
Runnable updateUI = new Runnable()
{
// do work to get the information to display
// in this code I set "int message" to one of the constants handled by the activity's IncomingHandler and "LocWithName newLoc" to a useful value or null
sendMessageToUI(message, newLoc);
handler.removeCallbacks(updateUI);
handler.postDelayed(updateUI, 5000);
}
in service onStartCommand:
handler.post(updateUI);
Your mistake is assuming that the bindService() and startService() calls block until the service has been "bound" or "started", respectively. The reality is that onServiceConnected() won't get called until sometime after onCreate() returns. Likewise, the order you call them in is basically meaningless; the OS doesn't guarantee that the service will handle the binding or the onStartCommand() first or second, in this case.
To fix, delete the call to startService() (as #pskink suggested); the service is started by virtue of the fact that you are binding to it. onStartCommand() will no longer be called. Instead, have the Service kick off the updateUI Runnable when it gets the ForegroundService.BIND_SERVICE message. This allows you to establish the appropriate "happens before" relationships -- namely, that the ServiceConnection binding "happens before" you start trying to use _activityMessenger to send messages.
I have successfully implemented Firebase messaging in my app. Works great in the background and onMessageReceived() gets called when the app is in the foreground (yippee!).
The issue I have is that I need to update the UI dynamically and I am stuck on the best way to achieve this. I don't want to send the user an in-app notification (as the sample code shows), I'm not sure I want to send a broadcast, all I want to do is to access the MainActivity in order to call a method already there, however I have no reference to the MainActivity in the service.
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (remoteMessage.getNotification().getBody() != null) {
Log.d(LOG_TAG, "Message received: " + remoteMessage.getNotification().getBody());
} else {
Log.d(LOG_TAG, "Message received: " + remoteMessage.getData().get("message"));
}
// Call method in MainActivity
// <<< What goes Here?>>>>
}
This seems a simple use case but I can't find anything online to help.
Thanks in advance
Yes, you can update UI and pass value to your activity by using Local Broadcast
In your onMessageReceived() Firebase Service.
broadcaster = LocalBroadcastManager.getInstance(getBaseContext());
Intent intent = new Intent(REQUEST_ACCEPT);
intent.putExtra("Key", value);
intent.putExtra("key", value);
broadcaster.sendBroadcast(intent);
and register local Broadcast in your Activity or fragment method
#Override
public void onStart() {
super.onStart();
LocalBroadcastManager.getInstance(getActivity()).registerReceiver((receiver),
new IntentFilter(PushNotificationService.REQUEST_ACCEPT)
);
}
#Override
public void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(receiver);
}
and Handle Your update event like this, do your update UI work Here, it will call automatically when notification received and onMessageReceived() send a broadcast.
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
try {
String value= intent.getStringExtra("key");
String value= intent.getStringExtra("key");
} catch (Exception e) {
e.printStackTrace();
}
}
};
I believe you should send a Local Broadcast with the data and register a receiver wherever you want that data to be utilised. This is a very good design pattern(Observer) as it decouples your Activity from the Service.
If the activity wants to do something with the data it will, else it won't. They are both separate entities and it would be much easier to maintain this code in the future, as far as I know.
Hope this helped.
I have a BroadCastRecevier that works in an asnyc task and it works when server sent a message then broadcast sends this server messages to activities. When activity get the message I am doing some process using a Handler However sometimes handler is not triggered. I mean broadcast sends three messages but handler works two times.
g.broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
srvrMessage = intent.getStringExtra("message");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
myHandler.sendEmptyMessage(1);
}
};
To avoid this issue I decide to use a Service. I have created a service class and want that handler work inside of it but I could not send myHandler as a parameter while starting the service. How can I make this handler works in the service?
I think these links will help you:-
http://techblogon.com/android-service-example-code-description-complete-tutorial/
http://blog.denevell.org/android-service-handler-tutorial.html
app sent transaction to the server, user closes app, now a message needs to be
sent back to the phone from the server 10+ minutes later. The phone may be asleep, or the user might be checking his email. The question which I have is:
how can the phone be notified that a message has been received from server ?
how to display that message ?
A possible solution would be Google cloud messaging, but I still am not able to answer these 2 questions
1) You have to use SERVICE for that.
2) And to show that message.
Do like this.
The variable and method are members of Service class:
public final static String ACTION = "com.apps.example.MainActivity";
private void messageFromServer()//this method sends broadcast messages
{
Intent intent = new Intent(MOVEMENT_UPDATE);
String messageFromServer=serverMessage //here you will put server message
intent.putExtra("MessageFromServer", messageFromServer);
sendBroadcast(intent);
}
And this are the methods from Main activity:
You have to register receiver in the onResume method:
#Override
public void onResume()
{
IntentFilter intentFilter;
intentFilter= new IntentFilter(YourService.ACTION);
messageFromServer= new MessageFromServer();
registerReceiver(messageFromServer, intentFilter);
startYourService();
super.onResume();
}
private void startYourService()
{
startService(new Intent(this, YourService.class));
}
public class MessageFromServer extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
// this method receives broadcast messages.
// Be sure to modify AndroidManifest.xml file in
// order to enable message receiving
String messageFromServer = intent.getStringExtra("MessageFromServer");
updateGUI();// here you can update the ui
}
}
and put service in you manifest file.
<service android:name="com.apps.service.YourService" ></service>
I'm building a Bluetooth application. I want to periodically scan for nearby Bluetooth devices. This program should start when the device starts and continue discovering devices based on a schedule (every 10 mins for example). I've been looking over the Android example of "BlueTooth Chat" in the API documentation, and I don't kow why it never uses the "Service" class. Should I use Service or Activity?
Furthermore, I understand that Services are supposed to be used for "long running tasks," but I also at some point want to provide some kind of GUI notification to the users via this class that discovers Bluetooth devices.
So, can someone please explain which one to use? What is the advantage?
You should use service if you want your scheduling running.Because android will eventually destroy your activity.
Definitely use a Service. In your MainActivity bind to the Service using bindService providing a ServiceConnection object. In this ServiceConnection object send a Message to the service with a reference to a local Messenger object (as part of a replyTo) that the Service can then use to later on send a Message back to your MainActivity. This will enable you to update your MainActivity GUI based on the results of your bluetooth scan.
In short, in your MainActivity, start and bind to your service with:
Intent i = new Intent(this, NetworkService.class);
startService(i);
bindService(i, networkServiceConnection, Context.BIND_AUTO_CREATE);
Define a messenger to respond to messages from the service like:
Messenger messenger = new Messenger(new IncomingHandler());
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NetworkService.MSG_SOMETHING:
// do something here
break;
default:
super.handleMessage(msg);
}
}
}
And then write your service connection code like:
private ServiceConnection networkServiceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
networkService = new Messenger(service);
try {
Message msg = Message.obtain(null, NetworkService.MSG_REGISTER_CLIENT);
msg.replyTo = messenger;
networkService.send(msg);
log.debug("Connected to service");
} catch (RemoteException e) {
// Here, the service has crashed even before we were able to connect
}
}
Note that the replyTo is the messenger we just created.
In your NetworkService, keep track of connected clients with:
ArrayList<Messenger> clients = new ArrayList<Messenger>();
and create your handler like:
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
log.debug("Adding client: " + msg.replyTo);
clients.add(msg.replyTo);
break;
default:
super.handleMessage(msg);
break;
}
}
}
Then, when you want to send a message back to your MainActivity, just do something like the following:
for (int i = 0; i < clients.size(); i++) {
try {
clients.get(i).send(Message.obtain(null, MSG_SOMETHING));
} catch (RemoteException e) {
// If we get here, the client is dead, and we should remove it from the list
log.debug("Removing client: " + clients.get(i));
clients.remove(i);
}
}