Service can't control activity UI? - android

I have problem when I control Activity UI from service. It doesn't work.
class MainActivity
public void showNotice() {
Log.d("log", "can't connect to server");
tvText.setText(notice);
tvTex.setVisibility(View.VISIBLE);
pbDialog.hide();
}
I call showNotice method in my service:
((MainActivity) mContext).showNotice();
But it only show log "can't connect to server".
tvText doesn't change anything, not change text, not visible. pbDialog does'n hide?
Can i help me resolve it? Many thanks.

A service runs in its own background thread, the UI can be modified only from the UI Thread which is pretty much with the main activity.
You can try doing this, you can broadcast an event from the service when you want to hide the dialog box. Have a broadcast listener registered for this which would handle the UI modification. You should probably be using a LocalBroadcastManager and give your broadcast a unique name.
In your mainActivity, use
registerReceiver(<receiver instance>, <broadcast name>)
in the onStart or onCreate method.
This will setup your mainActivity to listen to the broadcasts, next you need to define your broadcast receiver which is the in the above register call.
BroadcastReceiver receiver;
receiver = new BroadcastReceiver() {
public void onReceive(Context c, Intent i) {
//Modify the UI, in this case hide the dialog box.
}
}

Related

Toast message from background only displays while in main activity; not other activities

This problem is a little different from most 'display toast' issues. I am not asking to display Toast messages in other activities but to see Toast messages sent by a background service while in another activity besides the main activity. I DO see the Toast messages in the main activity!
I have an application that has a background service. When certain events happen in this background service a Toast message is displayed. The background service receives data from external BT and BLE devices and sends messages out over wifi. The toast messages show certain important events in those processes. The MainActivity and the background service use the application context from getApplicationContext() for the display of this toast message.
However, these messages do not display if I move to another Activity. For example, to configure some parameters. I am not trying to display toast messages from other activities; that I can do. But how can I get the toast messages from the background service to also display while I am in another Activity besides the MainActivity? I think I will need to do something like 'run the activity on the application context' though I have no idea how to do it (or even if it is possible).
Use BroadcastReceiver for this purpose. In each of your Activity you need to declare a BroadcastReceiver like this.
private class ShowToastBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("Message");
Toast.makeText(OtherActivity.this, message, Toast.LENGTH_LONG).show();
}
}
You need to register the BroadcastReceiver each time in your onResume and unregister it back in onPause function of your activities.
// This is in your Activity
private ShowToastBroadcastReceiver showToastBroadcastReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
showToastBroadcastReceiver = new ShowToastBroadcastReceiver();
}
#Override
public void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this).registerReceiver(showToastBroadcastReceiver, new IntentFilter("SHOW_TOAST"));
}
#Override
public void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(showToastBroadcastReceiver);
}
Now you need to send the broadcast from your Service to get the toast to be shown in your Activity.
// This is in your Service
Intent intent = new Intent("SHOW_TOAST");
intent.putExtra("Message", "This toast needs to be shown");
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
You should be able to display a Toast from any context, including a Service. The Toast Notifications developer guide has sample code that shows how:
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
In other words, you don't need an Activity at all. You can display the Toast directly from the Service. If you're worried about doing it from a separate thread, create a Handler in the main thread of the Service and post a Runnable or message to that handler to display the Toast.
One other possibility is that you are using a bound service and it is going away when you switch away from the main activity. Make sure the service itself is still running.
Oh, and also make sure that the 'Show notifications' setting for your app hasn't been unchecked.
Thanks to Reaz I found a stupid error I had made. I was unregistering the BroadcastReceiver handling the Toasts at onStop() instead of onDestroy(). Moving the unregistration to onDestroy() and registration and filters to onCreate() solved the problem.

Closing an Activity in Broadcast Receiver

I have a broadcast receiver to check the internet connectivity, in which i have validated if no internet connection I am displaying an error screen through intent. Now i need to close the error screen when the network is reconnected and display the previous screen from which the network was gone.
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
isConnected = intent.getBooleanExtra(ConnectionDetector.CONNECTED_KEY, false);
if(isConnected){
System.out.println("A1");
finish();
}
if (intent.getAction().equals(CONNECTION_BROADCAST_ACTION)) {
System.out.println("A2");
No_Network.this.finish();
}
}
};
This is my code inside the error screen activity. How to close the activity here? finish() is not working.
How to close the activity here? finish() is not working
It is "not working" because you are not using it correctly. The correct approach is to notify the activity and tell it to finish() itself. You can do that by sending intent with "magic" code of yours directly to your activity (if it is in singleTop launch mode then startActivity() + onNewIntent() would suffice). Alternatively use event bus for communication.

How to send message from BroadcastReceiver to activity or fragment

I have a receiver, it does call details saving task like storing incoming call, outgoing call etc.. all these details goes to sqlite DB. If my activity is not running, then its fine.
Sometime, when my activity is running, i get some incoming call. the receiver runs & stores data to DB. UI wont get refreshed because it never knows about change in DB.
Here i need to manually tell from receiver that, if activity is running refresh screen. How to implement this process in android.
I'm slightly confused in this part
You can use a LocalBroadcastManager to send a local broadcast to your Activity (more efficient and more secure than using a global broadcast):
Intent intent = new Intent(action);
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(context);
mgr.sendBroadcast(intent);
http://developer.android.com/reference/android/support/v4/content/LocalBroadcastManager.html
Your Activity would have to register a BroadcastReceiver in onStart and unregister it in onStop:
private BroadcastReceiver mBroadcastReceiver;
mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// do your thing
}
};
LocalBroadcastManager mgr = LocalBroadcastManager.getInstance(this);
mgr.registerReceiver(mBroadcastReceiver, new IntentFilter(action));
in onStop:
mgr.unregisterReceiver(mBroadcastReceiver)
Now that's the official Android way to do it. I most certainly prefer to use an event/message bus like Otto or EventBus (https://github.com/greenrobot/EventBus). You can use those to broadcast messages/events across different components in your app. The advantage is you don't need access to a Context (like you do when using Broadcasts), it's faster and it forces the developer to object oriented programming (since the events are always objects). Once you start using an event bus you'll never look back to local broadcasts and you'll replace many of the sometimes messy observer / listener patterns used across your app.
You can create a BroadcastReceiver inside an activity. Register it in onResume() and unregister it in onPause(). Whenever your other receiver receives a broadcast, send a broadcast to this receiver too. If the activity is running(i.e. on front), the broadcast will be received. Do whatever you want in its onReceive().
Example:
BroadcastReceiver br = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//Do stuff
}
};
Also override methods:
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(br);
}
#Override
protected void onResume() {
super.onResume();
registerReceiver(br, new IntentFilter("intent_filter"));//Use any string for IntentFilter you like
}
You can update fragments from activiy by creating methods inside fragment and access them from Fragment object inside activity.

How to terminate Android app from within service?

On receiving a certain event in my Android service, I want to terminate the app from within the service. I know I can call finish in an activity to end it.
Also I understand that service will call stopSelf() on itself to end itself. But I need to terminate the entire app including any particular activity of the app that was visible at that time.
Any ideas?
Try this:
Process.killProcess(Process.myPid());
You can create an BaseActivity with BroadcastReceiver to close all Activities by registering it in onCreate() of BaseActivity and extending all your Activities with BaseActivity.
public static final String EXIT_APP_ACTION = "EXIT_APP_ACTION";
BroadcastReceiver ExitAppBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
Register it in onCreate() for the BaseActivity in your Application using,
registerReceiver(ExitAppBroadcastReceiver, new IntentFilter(EXIT_APP_ACTION));
Then you can fire this BroadcastReceiver from your Service using the Context to close all Activites including Service by calling stofSelf(),
stopSelf()
context.sendBroadcast(new Intent(BaseActivity.EXIT_APP_ACTION));

send broadcast with combination of localbroadcastmanager sendorderedbroadcast

I'm wanting to implement what CommonsWare describes on this blog post: http://commonsware.com/blog/2010/08/11/activity-notification-ordered-broadcast.html. The post makes sense, and I was able to browse the example source here: https://github.com/commonsguy/cw-advandroid/tree/master/Broadcast.
What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.
In case what I'm asking isn't clear, what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible, but I also want to display notifications if the user closes my app and the service is still running. Is there a way to combine both of those capabilities without sending a broadcast twice inside of the service?
(What I don't want to have to do) like:
LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast);
sendOrderedBroadcast(broadcast);
What I'm curious about is if calling LocalBroadcastManager.getInstance(UnzipService.this).sendBroadcast(broadcast); inside of a service will still be picked up by a broadcast receiver of the type you define in your manifest.
No. LocalBroadcastManager only works with receivers registered with the LocalBroadcastManager singleton itself. Moreover, LocalBroadcastManager does not support ordered broadcasts, last I checked.
what I'm trying to do is use the LocalBroadcastManager because the broadcasts from my service don't necessarily need to be seen system wide and I'd rather keep them private if possible
So long as you are not using an <intent-filter> on your BroadcastReceiver in the manifest, and therefore are using an explicit Intent as the broadcast itself, your broadcast will only be seen by yourself and the bit of the OS that manages broadcasts. Other apps will not be able to spy upon it.
If you only have 2 objects that might handle your broadcast (in your case an Activity and a notifications controller), you can achieve the behavior of a ordered broadcast using only the LocalBroadcastManager.
The general idea is:
Set up your Service so that it broadcasts an Intent to your Activity with a particular action when you want to display your result
In your Activity create a BroadcastReceiver that handles your Service result Intent, and register it on the LocalBroadcastManager with an IntentFilter using the action from step 1
In your Service, when the results are available, try to send the result Intent using LocalBroadcastManager.getInstance(Context).sendBroadcast(Intent) this method returns a boolean that indicates if the broadcast has been sent to at least one receiver. If this boolean is false, it means that your Activity didn't handle your broadcast and you should show a notification instead.
In your service:
public UnzipService extends IntentService {
public static final String ACTION_SHOWRESULT = UnzipService.class.getCanonicalName() + ".ACTION_SHOWRESULT";
#Override
protected void onHandleIntent(Intent intent) {
Thread.sleep(500); // Do the hard work
// Then try to notify the Activity about the results
Intent activityIntent = new Intent(this, YourActivity.class);
activityIntent.setAction(ACTION_SHOWRESULT);
activityIntent.putExtra(SOME_KEY, SOME_RESULTVALUE); // Put the result into extras
boolean broadcastEnqueued = LocalBroadcastManager.getInstance(this).sendBroadcast(activityIntent);
if (!broadcastEnqueued) { // Fallback to notification!
PendingIntent pendingIntent = PendingIntent.getActivity(this, (int) System.currentTimeMillis(), activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(SOME_ID, new NotificationCompat.Builder(this)
.setContentIntent(pendingIntent)
.setTicker("results available")
.setContentText("results")
.build());
}
}
}
In your Activity:
public YourActivity extends Activity {
private BroadcastReceiver resultReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
processResult(intent); // Results Intent received through local broadcast
}
}
private IntentFilter resultFilter = new IntentFilter(UnzipService.ACTION_SHOWRESULT);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate();
Intent intent = getIntent();
if (UnzipService.ACTION_SHOWRESULT.equals(intent.getAction())) {
// The Activity has been launched with a tap on the notification
processResult(intent); // Results Intent contained in the notification PendingIntent
}
}
#Override
protected void onResume() {
super.onResume();
LocalBroadcastManager.getInstance(this)
.registerReceiver(resultReceiver, resultFilter);
}
#Override
protected void onPause() {
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(resultReceiver);
super.onPause();
}
private void processResult(Intent intent) {
// Show the results from Intent extras
}
}
This should be a complete working example.
I hope this helps who is trying to implement ordered broadcasts with LocalBroadcastManager from support library!
I understand you want to achieve the following:
"I have an event that occurs in the background. I want to update my activity, if the activity is on the screen. Otherwise, I want to raise a Notification." (#TheCommonsBlog)
You can achieve this behaviour by implementing a ResultReceiver.
Examples Restful API service and
http://itekblog.com/background-processing-with-intentservice-class/
What you basically do is instance a ResultReceiver in your Activity and pass it to the Service like a Parcelable parameter through an intent. Then, each time your service whats to update the UI, the service verifies the ResultReceiver object for NULL. If not NULL, you update the Ui via the onReceiveResult interface. Else, you raise a notification. When your activity dismisses, make sure you set the ResultReceiver on the Service to NULL.
Hope it helps.
PS: IMO, broadcasts are too much work and hard to control.
Use LocalBroadcastManager and broadcasts become easy to use.
I am not in favor of updating an Activity if an event occurs in the background. The user might already be doing something else in the Activity. Seems to me that a Notification is sufficient; it's always visible and remains until the user dismisses it. Gmail and Gcal work like this; Gmail doesn't update the current screen if a new mail comes in. If you want to know how to handle the task flow for handling a notification when the user is already in the app, see the Notifications API guide and also the [Notifying The User2 training class.

Categories

Resources