I'm trying to start a service from one activity to make an api call and obtain its result in another activity. I use a BroadcastReceiver to receive the data but how do I make sure that the activity is created and the receiver is attached before sending the broadcast from the service. Is there something wrong with the way that I'm designing this?
Thanks in advance.
Edit: To simply this, I'm starting a 3 second animation when the app is called. I do not want to waste that time and so I'm trying to get data from network and then display it on the activity called after the animation ends. I assumed IntentService would be the way to go but if its not please suggest me how to go about this.
You can use Sticky Events from EventBus library. Basically it will cache your data in memory before broadcasting events. The data can be delivered to subscribers. Thus, you don’t need any special logic to consider already available data.
First you need to declare a class to hold data which you get from network.
public class MyDataEvent {
String token;
// Write more properties here
}
In the service, after getting the data from network, post event which contains data to subscribers.
MyDataEvent data = new MyDataEvent();
data.token = "123456789abcxyz";
EventBus.getDefault().postSticky(data);
In activity which you want to receive the data
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// UI updates must run on MainThread
#Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onMyDataEvent(MyDataEvent data) {
// Process the data here
Log.i("TAG", data.token);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
Related
So , I would like to use the following feature mentioned on GreenRobots website,
EventBus can handle threading for you: events can be posted in threads different from the posting thread. A common use case is dealing with UI changes. In Android, UI changes must be done in the UI (main) thread. On the other hand, networking, or any time consuming task, must not run on the main thread.
What I wish to do is, in my android app i would like to create a event which will handle all my networking tasks(sending and receiving data from the server).
How do i exactly do this?
Should i make a network call in the event POJO and then use OnEvent to do post network call tasks.(I dont think this is correct or is it ?)
Edit : Using an event bus for threading may not be the best option because all your OnEvent call will run synchronously one after the other, which may result in blocking of the bus and also its not meant for that. But the answer below is the way it can be done if at all thats a requirement.
I would suggest using an architecture where an event bus might not be required. An event bus is still useful and I think you can find what you are looking for in their getting started guide.
Some example code:
public class EventBusExample extends Activity {
#Override protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
EventBus.getDefault().post(new BackgroundWorkEvent());
}
#Override protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
#Subscribe(threadMode = ThreadMode.ASYNC)
public void doBackgroundWork(BackgroundWorkEvent event) {
// do background work here
// when finished, post to ui thread
EventBus.getDefault().post(new UiWorkEvent());
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void doUiWork(UiWorkEvent event) {
// on main thread. do ui stuff
}
public static class BackgroundWorkEvent {
}
public static class UiWorkEvent {
}
}
Context:
I'm trying to develop an Android application to gather data from many sources such as: battery, hardware, sensors, cell towers, satellites and wifis.
These data has to be "observed" inside a service because I need it to run in background in order to log the data and send it to my server. When the user opens the app I also need to show that data in the activity.
First I wrote my code using many BehaviorSubject singletons, each representing a data source. This way I could pass the data from service using the onNext method and subscribe to it anywhere. But now I want to get rid of Subject objects since it is recommended the usage of Observable.
Problems:
I have two questions:
Question 1: How to create a RxJava Observable that registers a listener or a BroadcastReceiver at the first subscription and, when all subscribers call the unsubscribe method, it unregisters the listener or receiver?
I have this piece of code to listen to satellites:
private final GpsStatus.Listener gpsListener = new GpsStatus.Listener() {
GpsStatus gpsStatus = null;
#Override
public void onGpsStatusChanged(int event) {
gpsStatus = locationManager.getGpsStatus(gpsStatus);
if (event == GpsStatus.GPS_EVENT_SATELLITE_STATUS) {
if (gpsStatus != null) {
//put satellite data in some list
}
}
}
};
public registerSatellite() {
locationManager.addGpsStatusListener(gpsListener);
}
public void unregisterSatellite() {
locationManager.removeGpsStatusListener(gpsListener);
}
I also have this piece of code to listen to battery information:
private BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//put intent data in some variable
}
};
public registerBattery()
{
context.registerReceiver(batteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
public unregisterBattery()
{
context.unregisterReceiver(batteryReceiver);
}
Question 2: In order to pass data from service to activity, are singletons a good option?
You could use RxJava's PublishSubject. It emits all subsequent observed items to the subscriber and supports multiple subscribers:
PublishSubject<GpsStatus> subject = PublishSubject.create();
// observer1 will receive all onNext events
subject.subscribe(observer1);
subject.onNext(<GpsStatus#1>);
subject.onNext(<GpsStatus#2>);
// observer2 will only receive GpsStatus #3
subject.subscribe(observer2);
subject.onNext(<GpsStatus#3>);
In combination with a singleton, you could create a sort of Bus that allows generators (e.g. your GpsListener, BroadcastReceiver) to publish to activities.
Just don't forget to unsubscribe onDestroy(), otherwise you'll leak activities!
I have a download process that runs in the background, and updates the UI with progress (a ListView adapter). It works fine until I leave the activity and come back. After loading the activity again there is a "new" ListView object that is not the same one that is bound to the BG download process. How can I structure my code so that the background process can always talk to the ListView in my activity?
The specific line that does this is:
adapter.notifyDataSetChanged();
Here is the shell of the Download class:
public class Download
{
}
protected void start()
{
TransferManager tx = new TransferManager(credentials);
this.download = tx.download(s3_bucket, s3_dir + arr_videos.get(position), new_video_file);
download.addProgressListener(new ProgressListener()
{
public void progressChanged(final ProgressEvent pe)
{
handler.post( new Runnable()
{
#Override
public void run()
{
if ( pe.getEventCode() == ProgressEvent.COMPLETED_EVENT_CODE )
{
Download.this.onComplete();
}
else
{
Download.this.onProgressUpdate();
}
}
});
}
});
}
protected void onProgressUpdate()
{
this.download_status = "downloading";
Double progress = this.download.getProgress().getPercentTransfered();
Integer percent = progress.intValue();
//Log.v("runnable", percent + "");
downloaded_data.edit().putInt(position+"", percent).commit();
adapter.notifyDataSetChanged();
}
}
The short answer is simply "no". There's no simple way to find/keep the reference to a ListView in a destroyed/recreated Activity.
One way you can get around this is to use BroadcastReceiver. You can broadcast progress intents, and have the Activity register/deregister from those intents in onCreate() and onPause().
Another (arguably easier) hack you can do it is to persist the state (along the lines of what you're doing with downloaded_data.edit()), and have a thread in your Activity that regularly polls this state and updates the ListView accordingly.
You can save data of listview in file, then in function onCreate callback to take it. Using File may be a solution. Once your Activity is destroyed, all datas are lost
Make tasks detachable, like an Executor service placed in a component that is always there between Activity changes:
Use a Service: clients can connect to it and request what tasks are running etc.
Implement Application class and let it hold references to tasks that are running, exposed via a static field.
(working code extract added below)
My app needs to be notified of all inserts and deletes (and maybe updates, but less important) of contacts. This means when the app is started it will need a list of changes. While it is running it should be notified immediately (is it even possible to make changes to contacts outside the app while it is running?).
Should I be using a ContentObserver? Do I need a Service? Is there a way at app startup to get a list of changes that occurred since the last time the app ran?
Thanks.
ContentObserver does indeed work. However, for contacts, it does much less than I hoped for. You only get a notification that something has changed (in fact, you may get several notifications). You wont know what changed. Better than no notification though, I guess.
When you receive the notificaton, you'll have to run queries to find out if any of the contacts you are interested in have changed. If you need to check all of them, I think you'll be better off using a SyncAdapter.
Here's the code I ended up using. First a ContentObserver subclass; this receives notifications from whatever provider you register with (see next block of code):
class MainContentObserver extends ContentObserver
{
public MainContentObserver (Handler handler)
{
super (handler);
}
#Override
public void onChange (boolean selfChange)
{
Message msg = handler.obtainMessage();
msg.what = CONTACTS_CHANGED; // const int declared elsewhere
msg.obj = null;
handler.sendMessage (msg);
}
}
Here's the sceond block - this is the onCreate from your activity (or it could be in onResume). There are two important parts. One, I implement and instantiate a handler. This will receive "messages" from the observer, which runs in a separate thread, and relay them to my activity. The second piece is the creation of the observer, which happens through the register call.
#Override
public void onCreate (Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// receive notices from our background threads.
handler = new Handler()
{
#Override
public void handleMessage (Message msg)
{
if (msg.what == CONTACTS_CHANGED) // const int declared elsewhere
System.out.println ("handler: contacts changed");
else
throw new IllegalArgumentException ("unrecognized handler message source: " + msg.what);
}
};
// register content observer for contact changes
contactsObserver = new MainContentObserver (handler);
getContentResolver().registerContentObserver (ContactsContract.AUTHORITY_URI, true,
contactsObserver);
... other initialization ...
}
Finally, one more block of code - you need to unregister the observer or (I've read) you'll have a memory leak. (If you regsiter in onResume, be sure to unregister in onPause.)
#Override
public void onDestroy ()
{
super.onDestroy();
getContentResolver().unregisterContentObserver (contactsObserver);
}
I know there is no broadcast for what you want to do. ContentObserver is what you have to go with. Also check:
Native contact change notification
I think ContentObserver is better option, you can refer following ContentOberver
dealing with contacts.
i think you will have to look into the Broadcast Receiver for your question..
is the order of a broadcast intent guaranteed? that is, if i do,
sendBroadcast(intent1);
sendBroadcast(intent2);
are the receivers guaranteed to get intent1 before intent2? i suspect the answer to this is no, but in that case, i'm not quite sure how to solve my problem.
i'm trying to create a "busy" indicator for my app that shows busy when the device is talking on the network, and then goes away when the network communication is done. all network communication happens in an intent service.
my attempt at this was to send a BUSY_START intent when i begin network communication in the service, and a BUSY_STOP when network communication ends. this seems to mostly work, but i'm finding occasionally that i get the stop and start messages out of order.
is there a better way to solve this problem?
i'm thinking of adding an ID to each busy intent, so they can be paired. that way if i receive a start for which i've already received a stop, i can ignore it. or, perhaps more simply, add an integer sequence number into each broadcast. if i ever receive a broadcast for which the sequence of the current intent is less than the sequence of the last received intent, ignore it.
Have you considered using a Handler object to communicate from the background thread in the IntentService? The advantage of a Handler over the BroadcastReciver approach is that the Handler uses a message queue to sequence the Message objects.
(I'm assuming your Service is in the same process as the app's main thread).
At least one viable alternative to intents is to execute messaging through the application class, i.e.,
create a listener interface
Manager a collection of listener objects in the application / provide methods to add / remove listener
Interested entities call the application methods to add / remove themselves as listeners
Add "notify" methods in the application, that call the appropriate listener interface method on each of the registered listeners
Services call the application's notification methods to
For example,
public class MyApplication extends Application {
public interface MyListener {
void onEvent();
}
private Set<MyListener> listeners = new HashSet<Listener>();
public void addListener(MyListener l) {
listeners.add(l);
}
public void removeListener(MyListener l) {
listeners.remove(l);
}
public void sendEvent() {
for (MyListener l: listeners) { l.onEvent(); }
}
}
Now, from your activity (or fragment),
public class MyActivity extends Activity implements MyListener {
...
...
...
#Override
public void onEvent() {
// do something
}
#Override
protected void onResume() {
super.onResume();
((MyApplication)getApplication()).addListener(this);
}
#Override
protected void onPause() {
super.onPause();
((MyApplication)getApplication()).removeListener(this);
}
}
And in your service,
((MyApplication)getApplication()).sendEvent();
This provides synchronous messaging without using intents or static variables.