More efficient way of updating UI from Service than intents? - android

I currently have a Service in Android that is a sample VOIP client so it listens out for SIP messages and if it recieves one it starts up an Activity screen with UI components.
Then the following SIP messages determine what the Activity is to display on the screen.
For example if its an incoming call it will display Answer or Reject or an outgoing call it will show a dialling screen.
At the minute I use Intents to let the Activity know what state it should display.
An example is as follows:
Intent i = new Intent();
i.setAction(SIPEngine.SIP_TRYING_INTENT);
i.putExtra("com.net.INCOMING", true);
sendBroadcast(i);
Intent x = new Intent();
x.setAction(CallManager.SIP_INCOMING_CALL_INTENT);
sendBroadcast(x);
Log.d("INTENT SENT", "INTENT SENT INCOMING CALL AFTER PROCESSINVITE");
So the activity will have a broadcast reciever registered for these intents and will switch its state according to the last intent it received.
Sample code as follows:
SipCallListener = new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(SIPEngine.SIP_RINGING_INTENT.equals(action)){
Log.d("cda ", "Got RINGING action SIPENGINE");
ringingSetup();
}
if(CallManager.SIP_INCOMING_CALL_INTENT.equals(action)){
Log.d("cda ", "Got PHONE RINGING action");
incomingCallSetup();
}
}
};
IntentFilter filter = new IntentFilter(CallManager.SIP_INCOMING_CALL_INTENT);
filter.addAction(CallManager.SIP_RINGING_CALL_INTENT);
registerReceiver(SipCallListener, filter);
This works however it seems like it is not very efficient, the Intents will get broadcast system wide and Intents having to fire for different states seems like it could become inefficient the more I have to include as well as adding complexity.
So I was wondering if there is a different more efficient and cleaner way to do this?
Is there a way to keep Intents broadcasting only inside an application?
Would callbacks be a better idea? If so why and in what way should they be implemented?

UPDATE 2015:
This question/answer still gets a little bit of activity, but it is over 5 yrs old and things have changed quite a bit. 5 years ago, the answer below was how I would have handled it. Later I wrote a very lightweight dependency injection solution that I was using for a while (which I mentioned in the comments). Nowadays, I would answer this question using Dagger and RxAndroid. Dagger to inject a "mediator" class into both the Service and all Activities that need to be notified, the Service would push the status update to the mediator class, and the mediator class would expose an observable for the activities to consume the status update (in place of the OP's broadcast receiver).
Original answer
I usually subclass Application and let my in-app communication go through this class (or have a mediator owned by the Application do the work...regardless, the Application being the entry point for the service to communicate with). I have a bound service that needs to update the UI as well (much simpler than yours, but the same idea) and it basically tells the app its new state and the app can then pass this information along in one way or another to the currently active activity. You can also maintain a pointer to the currently active activity (if there is more than one), and make decisions whether or not to simply update the current activity, broadcast the intent to launch a different activity, ignore the message, etc. I would also subclass Activity and have your new activity base class tell the Application that it is currently the active one in onResume and that it is being paused in onPause (for cases where your service is running in the background and the activities are all paused).
EDIT:
In response to the comment, here's more specifics.
Your application currently consists of Activity-derived and Service-derived classes for the most part. Inherently, you get functionality from an instance of the android.app.Application class. This is declared in your manifest (by default) with the following line:
<application android:icon="#drawable/icon" android:label="#string/app_name">
The application element in your manifest doesn't use the android:name attribute, so it just creates an instance of the default android.app.Application class to represent your global application context.
In my apps, I create a subclass of Application (ApplicationEx, for example) and I tell my app through the manifest that this is the class to instantiate as MY global application context. For example:
<application
android:name="com.mycompany.myapp.app.ApplicationEx"
android:icon="#drawable/app_icon"
android:label="#string/app_name">
I can now add methods to ApplicationEx for activities and services to use to communicate. There is always a single instance of your global application context, so this is your starting point if anything needs to be global for your app.
A second piece of this is that instead of deriving my services and activities from Service and Activity, I create a subclass of each with a getAppContext method that casts the return value of getApplicationContext (which exists already in both of these classes because they derive from Context) to my ApplicationEx class.
So........
All that being said, you add a CurrentActivity property to your ApplicationEx class of type Activity (or ActivityBase if you subclass it as I do). In ActivityBase's onResume method, you pass yourself to ApplicationEx for it to set CurrentActivity to that activity. Now, you can expose methods on ApplicationEx to pass information directly to the current activity instead of relying on the Intent mechanisms.
That's about as clear as I can make it

You can send broadcast intents just to your own application and not system wide with LocalBroadcastManager:
Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with sendBroadcast(Intent):
You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data.
It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit.
It is more efficient than sending a global broadcast through the system.
However, I'd still recommend going with the Service approach and locally binding and talking through Handler when necessary to update UI components for efficiency.

Related

Send broadcast intent from service to Application Class

Is it possible to send an intent from a service to an Application class? Not Activity?
I wouldn't know what activity would be running at a particular time, so I am adding a boolean flag in the activity class that detects the activity and sends the appropriate data based on the broadcast received.
If your Service is active, then your Application class is active as well.
Otherwise you wouldn't be able to use getApplicationContext().
Although I'm skeptic about a service that runs forever there is a very clean way to make the Service communicate with a certain Activity, should the last one be currently active.
Such clean way is called LocalBroadcastManager.
The Activity meant to receive the data should register a BroadcastReceiver in onResume() and unregister it in onPause().
You instantiate your BroadcastReceiver in your Activity's onCreate()
this.localBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Do what you have to do here if you receive data from the Service.
}
}
You create a Filter so your Activity only listens to a certain type of signals.
private IntentFilter notifIntentFilter new IntentFilter("com.you.yourapp.MY_SIGNAL");
in onResume()
LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(this.localBroadcastReceiver, notifIntentFilter);
in onPause()
LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(this.localBroadcastReceiver);
Now whenever you want to send data to your Activity, your Service can call:
final Intent intent = new Intent();
intent.setAction("com.you.yourapp.MY_SIGNAL");
// put your data in intent
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
If your Activity is awake, it will respond to the signal. Otherwise, if it's in the background, or it is not instantiated it won't.
You can apply this pattern to as many Activities as you wish.
Still, I have never used this inside the Application class. But you can try to register your receiver there. It might work, since if the Application class is destroyed, the BroadcastReceiver is destroyed too and thus probably unregistered as well.
The point is, if your Application gets destroyed, your Service will be killed as well. Unless you launched it in another process. But then it will have it's own instance of Application; and this is a complex thing you probably do not want to get into details now...
Important: since the Application class is not tied to any UI component, you can do whatever you need directly inside your service. If you need to manipulate the UI, then the pattern described above will work for you.
Please read about new Android's background limitations.
Edit:
Oh yeah right, if you need your Service to call a function declared in your Application class, you can just do
((MyApplication) getApplication()).myFunctionToHandleData(Intent intent);
I didn't really understand your question though, but either of the methods described above should work for you.

Communication between Class and Main Activity Android

I am new in Android so I hope you can excuse my ignorance
I made an activity to control some bluetooth devices with my telephone, now that everything is working I would like to generate a new class from this activity, a class to take care of all bluetooth communication.
I have some questions:
First: In my activty I employed one broadcast receiver to listen to some actions of the Bluetooth Adapter like STATE_ON, BOND_BONDED... Using this actions I update my views, I call some methods and so on.
So, it is possible to keep listening to this broadcast receiver inside my class and then send the changes to my main activity to update the views and so on?
Second: I really need to send information from my bluetooth class to my main activity, information that I read from my devices, information from the broadcast receiver... so, which is the best way to pass information between a class and the main activity?
Well, thanks a lot for your help :)
The onReceive() method of your BroadcastReceiver is called from the main thread:
"This method is always called within the main thread of its process" (http://developer.android.com/reference/android/content/BroadcastReceiver.html#onReceive(android.content.Context, android.content.Intent)).
That means you can update your ui from the onReceive() method. All you need to do is use a local class like so:
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// update the ui
}
};
Register this receiver programmatically (instead of defining it in the manifest) and you're good to go:
Context.registerReceiver(android.content.BroadcastReceiver, android.content.IntentFilter)

How BroadcastReceiver is different from Intent

Hello I see contradicting definitions. Android experts, can you explain this to me please?
1) If BroadCastReceiver is a component in android that responds to intents, then I can as well register an filter for activity in androidManifest xml file and have it do my job based on intent like battery low, no network, orientation change etc. these are intents I might be interested to react in my code.
2) Why register whole another filter for BroadcastReceiver in androoidManifest.xml and perform action at onReceive() inside the BCRCVR class?
3.Can we really perform intent driven operations in an activity? yes right?
I guess the title should be "How BroadcastReceiver is different from Activity".
In my opinion, Broadcast receiver is independent unit, because sometimes you don't want the system to create a new Activity object just to handle arrived intent. Moreover, you don't your activity to be shown. Broadcast receivers are independent and can be used outside any Activity. Activity is more about user interface and broadcast is about handling events.

Android - startActivityForResult outside an Activity?

I have a wrapper class (BluetoothDiscoverer) which is instantiated within a Service. This class obtains a BluetoothAdapter and checks whether Bluetooth is enabled before scanning for neighbouring devices.
Now if Bluetooth is not enabled I want to be able to do the following within this class (BluetoothDiscoverer):
Intent enableBluetoothIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetoothIntent, BLUETOOTH_ENABLER);
Now I have read this: use startActivityForResult from non-activity
but I don't want to pass my Main Activity into this object since I want to deal with the result (whether the user accepts to enable bluetooth or not)
within the BluetoothDiscoverer class.
Now If I make BluetoothDiscoverer a subclass of Activity
I seem to be getting a NullPointerException when the startActivityForResult is about to be called.
I think this is because I need to add an onCreate()/onDestroy() method,
but this defeats the purpose of what I am doing as I need to be able to call methods on the BluetoothDiscoverer object within the service that instantiates this class.
I also need to register a broadcast receiver for retrieving neighbouring devices when a scan is initiated. If the BluetoothDiscoverer class is not an Activity, how do I register this receiver?
Is there a work around for this?
Thank you
Andreas
startActivityForResult() is only available from real on-screen activities. Please redesign your application so that the user interface is driven from activities, then have your service scan for devices.
I also need to register a broadcast receiver for retrieving neighbouring devices when a scan is initiated. If the BluetoothDiscoverer class is not an Activity, how do I register this receiver?
You get rid of BluetoothDiscoverer and move its logic into the Service, which is a Context and therefore can register receivers.
In your Service, create an Activity whose purpose is to request Bluetooth discoverability. Pass a Messenger along with the Intent for this Activity and have the Activity start the discoverability activity:
Intent enableBluetoothIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetoothIntent, BLUETOOTH_ENABLER);
Then, inside onActivityResult() send a message via the Messenger back to your service.

How to determine if one of my activities is in the foreground [duplicate]

This question already has answers here:
Checking if an Android application is running in the background
(35 answers)
Closed 4 years ago.
I have implemented a BroadcastReceiver which is triggered by the AlarmManager. The AlarmManager is initialized on BOOT_COMPLETED. So i have to declare the receiver in the manifest.
My problem is that i want the BroadcastReceiver only to do something when none of my own activities are in the foreground (aka the user is not interacting with my application). I pull information from a remote server and don't want to notify the user if he is currently in my application anyways.
So far i have not managed to find a way to determine if my application is in the foreground. Is there a way to do such thing? The ActivityManager tells me if my application is running but not whether it is in the foreground.
The problem is pretty much the same as described here: Inform Activity from a BroadcastReceiver ONLY if it is in the foreground
SOLUTION:
After evaluating several solutions i want to quickly outline what i think is the best method to deal with activities in the background/foreground.
The preferred way is to register a broadcast receiver in the onResume method of your activity and to deregister it on the activities on onPause. Any service or other background element will than need to send a broadcast intent with a specific action that your activity will intercept.
If your activity is in the foreground it will have its intent receiver registered and is able to directly deal with the intent send from your service. If it is not in the foreground it will not receive the intent but the service that invokved the broadcast will know that nobody intercepted its broadcast intent and will be able to deal with that itself. Eg it could than launch the desired activity, show a notification etc.
The following answer: "Is application running in background", summarizes solutions available for background/foreground checking.
Note:
Previously this answer suggested to use ActivityManager.getRunningAppProcesses(), however that method appeared to be not completely reliable and its usage is discouraged. Check the link above for the details.
Your activity can track its own state as to whether it is in the foreground (set boolean to true in onStart(), to false in onStop()). Alas, that boolean is not provided to you by Activity automatically.
ActivityManager#getRunningAppProcesses() returns a List of RunningAppProcessInfo. Each RunningAppProcessInfo has a field called importance. importance equal to RunningAppProcessInfo.IMPORTANCE_FOREGROUND seems to show which activity is actively being observed by the user. There is also RunningAppProcessInfo.IMPORTANCE_VISIBLE which is lower but might be worth checking out.
check out my solution for determining if an activity is in the foreground: http://www.mannaz.at/codebase/android-activity-foreground-surveillance/
It should be easy to revert the logic from "in the foreground" to "not in the foreground".
I have implemented a BroadcastReceiver which is triggered by the AlarmManager. The AlarmManager is initialized on BOOT_COMPLETED. So i have to declare the receiver in the manifest.
My problem is that i want the BroadcastReceiver only to do something when none of my own activities are in the foreground (aka the user is not interacting with my application). I pull information from a remote server and don't want to notify the user if he is currently in my application anyways.
So far i have not managed to find a way to determine if my application is in the foreground. Is there a way to do such thing? The ActivityManager tells me if my application is running but not whether it is in the foreground.
There doesn't seem to be a direct way to determine if one of your activities is the current running foreground activity. However, you can get the desired effect by using an ordered broadcast and two broadcast receivers. One broadcast receiver needs to be registered in OnResume() and unregistered in OnPause(). The 2nd broadcast receiver will be declared in your manifest as you've already done. Set the android:priority for your receivers such that if the dynamically registered receiver is registered, it will receive the intent first, then you can eat the intent so that the broadcast receiver you registered in your manifest is never notified.
You can test if the window has focus - but as stated in dev docs this is not the same as if activity is in foreground.
I'd use ActivityLifecycleCallbacks to get much cleaner solution.
It can be insecure, read this before you decided to use example below in production. It works in 'home development' for my device and OS version.
public class App extends Application implements Application.ActivityLifecycleCallbacks {
private boolean inForeground;
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityResumed(Activity activity) {
inForeground = activity instanceof YourActivity;
}
public boolean isInForeground() {
return inForeground;
}
Register App in AndroidManifest:
<application
android:name=".App" />
And the final piece of the puzzle:
public class YourReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
App app = (App) context.getApplicationContext();
if(app.isInForeground()){
// do some stuff
}
}
}

Categories

Resources