How to use network communication using greenrobot eventbus? - android

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 {
}
}

Related

Handling services and sending data across activities

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();
}

Best practice for eventbus with thread safety

My app has activities for the user interaction and a background service which is the only place where the data model is being modified. The background service listens to actions that where made by the user as well as incoming messages from the network. Therefore concurrency issues can arise which I try to prevent by using a handler.
For the event layer I use greenrobots Eventbus.
This is all working well but I wonder if there is a smarter/faster/less code extensive (and therefore less error prone) way to handle this use case?
To be more specific:
Is there a way to ensure serial execution of the onEvent methods
without a handler?
Is there an alternative to having onEvent methods
for each possible event?
Is there a better pattern for what I am
doing here?
This is my approach:
In the oncreate method I do register the service (in case of an activity I do this in onstart)
#Override
public void onCreate() {
super.onCreate();
...
EventBus.getDefault().register(this);
}
And in the onDestroy I do unregister again:
#Override
public void onDestroy() {
super.onDestroy();
....
EventBus.getDefault().unregister(this);
}
Whenever I react to an incoming event I want to ensure serial execution as there can be concurreny issues because there are incoming events from user interactions as well as from other users via networking. So I decided to work with a handler:
private Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
Object receivedEvent = msg.obj;
if(receivedEvent instanceof EditUser)
{
processEditUserBroadcast((EditUser)receivedEvent);
}
else if(receivedEvent instanceof JoinParty)
{
processJoinPartyBroadcast((JoinParty)receivedEvent);
}
else if(receivedEvent instanceof LeaveParty)
{
processLeavePartyBroadcast();
}
else if(receivedEvent instanceof SendMessage)
{
processSendMessageBroadcast((SendMessage)receivedEvent);
}
else if(receivedEvent instanceof ReceivedMessage)
{
processReceivedMessageBroadcast((ReceivedMessage)receivedEvent);
}
else if(receivedEvent instanceof Reset)
{
processResetBroadcast();
}
else if(receivedEvent instanceof ImageDownloadFinished)
{
processImageDownloadFinishedBroadcast((ImageDownloadFinished)receivedEvent);
}
}
};
return handler;
}
For each event of interest I do have an onEvent method which is doing nothing but passing the event to the handler to ensure serial execution via a small "passToHandler" helper function
public void passToHandler(Handler handler, Object object)
{
Message message = handler.obtainMessage();
message.obj = object;
handler.sendMessage(message);
}
public void onEvent(EditUser editUser)
{
passToHandler(handler,editUser);
}
public void onEvent(JoinParty joinParty)
{
passToHandler(handler,joinParty);
}
public void onEvent(LeaveParty leaveParty)
{
passToHandler(handler,leaveParty);
}
public void onEvent(SendMessage sendMessage)
{
passToHandler(handler,sendMessage);
}
public void onEvent(ReceivedMessage receivedMessage)
{
passToHandler(handler,receivedMessage);
}
public void onEvent(Reset reset)
{
passToHandler(handler,reset);
}
public void onEvent(ImageDownloadFinished imageDownloadFinished)
{
passToHandler(handler,imageDownloadFinished);
}
The "process.." methods are where the "data magic" happens and shouldn´t be relevant for my question.
And of course for each possible event I did create a class which is usually quite slim like this:
public class JoinParty {
private String partyCode;
public JoinParty(String partyCode) {
super();
this.partyCode = partyCode;
}
public String getPartyCode() {
return partyCode;
}
}
Thank you for posting this Matthias! I think you bring up a very important point about thread safety with GreenRobot EventBus that can easily be missed by users of it.
I think you are quite possibly heading down the right path, though I'm new to GreenRobot EventBus and Android (but not Java). If I read the GreenRobot EventBus source code correctly, one other possible benefit to this approach is that post of the SendMessage event to your onEvent() method immediately returns (after calling sendMessage on the Handler) allowing the EventBus to continue posting it to any other subscribers without delay of the actual processing by your class. This may or may not be what you desire though.
With the approach that you have given, the other thing you need to ensure is that if you take an approach like this that there are no other public methods to your class that has all of your onEvent() methods and the methods such as processEditUserBroadcast(). Otherwise, while you have ensured that all of the processing of the events received from the EventBus are actually handled on a single thread (in a serial manner), some other class might call a public method of this class on a different thread and then cause you thread safety issues again.
If you know that you do need to support other public methods on this class, doing what you have done here at least gets all of the onEvent() methods handling onto a single thread (that of the Looper for the thread that creates the Looper from what I read in the doc for the Looper class) and that simplifies things at least some. You may also then need to apply some synchronization to the public methods and all of the other methods such as processEditUserBroadcast() so as to guarantee safe access to the data members of the class from multiple threads if you are going to have other public methods on this class. Alternatively, depending on what those data members are and what your needs are, you might be able to get by with simply making some of them volatile, atomic, or using the concurrent collections, etc. It all depends on what the read and write access needs are and also the needed granularity of those accesses.
Does this help at all? For those that are well versed with Android, Loopers, Handlers, GreenRobot EventBus, etc. have I misspoken at all?

How to implement a more flexible AsyncTask?

while it is very convenient to use, from my understanding, AsyncTask has two important limitations:
doInBackground of any instances will share the same worker
thread, i.e. one long running AsyncTasks can block all others.
execute, onPostExecute and other "synchronizing" methods must/will always be executed on the UI-thread, i.e. not on the Thread, which wants to start the task.
I ran into trouble, when I tried to reuse some existing AsyncTasks in a background IntentService that are responsible for the client-server communication of my app. The tasks of the service would fight over time in the worker thread with those of the UI activities. Also they would force the service to fall back onto the UI-thread, although that service should perform its work quietly in the background.
How would I go about removing/circumventing these limitations? I basically want to achieve:
A framework that closely resembles AsyncTask (because I need to migrate a lot of critical code there).
Each instance of such a task should run its doInBackground on its own thread instead of a single worker thread for all instances.
Edit: Thx to VinceFR for pointing out this can be achieved by simply calling executeOnExecutor instead of execute.
The callbacks like onPostExecute should be called on the same thread that started the task by calling execute, which should not need to be the UI-thread.
I figure, I'm not the first person to require something like this. Therefore I wonder: Is there already some third-party library that can be recommended to accomplish this? If not, what would be a way to implement this?
Thanks in advance!
The solution looks like this:
All classes that spawn AsyncTasks that might interfere with each other get their own Executor like this one (make that elaborate as you like using thread pools etc.):
private Executor serviceExecutor = new Executor() {
public void execute(Runnable command) {
new Thread(command).start();
}
};
As pointed out by VinceFR you can run an AsyncTask on a given Executor by calling it like this (where payload are the parameters that you would regularly pass to a task):
task.executeOnExecutor(serviceExecutor, payload);
However, this breaks backwards-compatibility to Gingerbread and earlier. Also, if you want to support Honeycomb, you need to make sure, this call happens on the UI thread. Jelly Bean will take care of this automatically.
Now the trickier part: Keeping the service running on its own thread. As many things in Android this seems harder than it needs to be (or maybe I'm lacking some information here). You can't use an IntentService, because that will shut down automatically the first time an AsyncTask takes over and let's the onHandleIntent callback complete.
You need to setup your own thread and event loop on the service:
public class AsyncService extends Service {
private static final String TAG = AsyncService.class.getSimpleName();
private class LooperThread extends Thread {
public Handler threadHandler = null;
public void run() {
Looper.prepare();
this.threadHandler = new Handler();
Looper.loop();
}
}
private LooperThread serviceThread = null;
private Handler serviceThreadHandler = null;
#Override
// This happens on the UI thread
public void onCreate() {
super.onCreate();
}
#Override
// This happens on the UI thread
public int onStartCommand(Intent intent, int flags, int startId) {
this.serviceThread = new LooperThread();
this.serviceThread.start();
while(this.serviceThread.threadHandler == null) {
Log.d(TAG, "Waiting for service thread to start...");
}
this.serviceThreadHandler = this.serviceThread.threadHandler;
this.serviceThreadHandler.post(new Runnable() {
#Override
public void run() {
doTheFirstThingOnTheServiceThread();
}
});
return Service.START_STICKY;
}
// doTheFirstThingOnTheServiceThread
}
No you need to make sure that each time an AsyncTask returns to the UI thread, you end up in your service thread instead:
// This happens on the serviceThread
private void doTheFirstThingOnTheServiceThread() {
// do some stuff
// here we can reuse a class that performs some work on an AsyncTask
ExistingClassWithAsyncOperation someUsefullObject = new ExistingClassWithAsyncOperation();
// the existing class performs some work on an AsyncTask and reports back via an observer interface
someUsefullObject.setOnOperationCompleteListener(new OnOperationCompleteListener() {
#Override
// This happens on the UI thread (due to an ``AsyncTask`` in someUsefullObject ending)
public void onOperationComplete() {
serviceThreadHandler.post(new Runnable() {
#Override
public void run() {
doTheSecondThingOnTheServiceThread();
}
});
}
}
someUsefulObject.performOperation();
}
// This happens on the serviceThread
private void doTheSecondThingOnTheServiceThread() {
// continue working on the serviceThread
}
So, this works for me. I'd be delighted to see a simpler solution for this. Note that the solution requires the service to know that is will be called back by the ExistingClassWithAsyncOperation on the UI thread. I don't particularly like this dependency, but don't know how to do better right now. However, I don't have to rewrite a lot of existing classes that perform asynchronous operations using AsyncTask.

greenrobot EventBus post event in Android

By using EventBus, I need to post an event(MyEvent) in an Activity and receive the event in another Activity in Android. I tried the greenrobot EventBus performance test project but could not get how to do it.
I tried in ActivitySubscriber
MyEvent event = new MyEvent();
EventBus.getDefault().post(event);
and tried to receive the event in ActivityReceiver as
EventBus.getDefault().register(this);
public void onEvent(MyEvent event){
....
}
but I am unable to receive the event. Can anyone let me know where am I doing wrong?
Since they are two activities, ActivitySubscriber posts the event while ActivityReceiver is still not created, or is in stall mode (onStop()). You need to use sticky events, i.e.
ActivitySubscriber.postSticky(...)
And for ActivityReceiver you have two options:
EventBus.getDefault().register(this) and somewhere after that EventBus.getDefault().getStickyEvent()
EventBus.getDefault().registerSticky() and then using regular EventBus.getDefault().onEvent(...)
Updated:
EventBus 3.0 changes the way to subscribe.
There is no need of method names which end up with specific suffixes but rather annotations.
How to use version 3:
//// in your build.gradle
compile 'de.greenrobot:eventbus:3.0.0-beta1'
// alternatively you can target latest whatever currently
// compile 'de.greenrobot:eventbus:+'
//// from a class which needs to dispatch an event
// posting an event is as before, no changes
// here we dispatch a sticky event
EventBus.getDefault().postSticky(myStickyEvent);
//// from your class which needs to listen
// method name can be any name
// any of subscribe params is optional, i.e. can use just #Subscribe
#Subscribe(threadMode = ThreadMode.MainThread, sticky = true, priority = 1)
public void onEventBusEvent(#Nullable final StickyEvent stickyEvent) {
if (stickyEvent != null) {
...
// optionally you can clean your sticky event in different ways
EventBus.getDefault().removeAllStickyEvents();
// EventBus.getDefault().removeStickyEvent(stickyEvent);
// EventBus.getDefault().removeStickyEvent(StickyEvent.class);
}
}
For more details and comparison of version 3:
http://androiddevblog.com/eventbus-3-droidcon/
http://androiddevblog.com/wordpress/wp-content/uploads/EventBus3.pdf
Some details extracted from the sources:
ThreadMode.PostThread
Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
ThreadMode.MainThread
Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is the main thread, event handler methods will be called directly. Event handlers using this mode must return quickly to avoid blocking the main thread.
ThreadMode.BackgroundThread
Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single background thread, that will deliver all its events sequentially. Event handlers using this mode should try to return quickly to avoid blocking the background thread.
ThreadMode.Async
Event handler methods are called in a separate thread. This is always independent from the posting thread and the main thread. Posting events never wait for event handler methods using this mode. Event handler methods should use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
default values for #Subscribe
threadMode = ThreadMode.PostThread
sticky = false - If true, delivers the most recent sticky event (posted with de.greenrobot.event.EventBus.postSticky(Object) to this subscriber (if event available)
priority = 0 - Subscriber priority to influence the order of event delivery. Within the same delivery thread, higher priority subscribers will receive events before others with a lower priority. The default priority is 0. Note: the priority does NOT affect the order of delivery among subscribers with different thread modes.
Edit 2
There is a dedicated site now for any Greenrobot EventBus questions from the creator of the lib:
http://greenrobot.org/eventbus/
Add
dependencies {
..
compile 'org.greenrobot:eventbus:3.0.0'
..
}
into dependencies section of Modules Build gradle
Create a MessageEvent class like
public final class MessageEvent {
private MessageEvent() {
throw new UnsupportedOperationException("This class is non-instantiable");
}
public static class Message1{
public String str1;
public Message1(String str) {
str1 = str;
}
}
public static class Message2{
public String str2;
public Message2(final String str) {
str2 = str;
}
}
}
// so on
Assume that we have Fragment1 and there is a button that suppose to send messages to MainActivity
public class Fragment1 extends Fragment {
private View frView;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup
container, Bundle savedInstanceState) {
frView = inflater.inflate(R.layout.fragment1,
container, false);
btn = (Button) frView.findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
frView.setBackgroundColor(Color.RED);
EventBus.getDefault().post(new MessageEvent.Message1("1st message"));
EventBus.getDefault().post(new MessageEvent.Message2("2nd message"));
}
});
return frView;
}
End finally MainActivity to listen and do action
public class MainActivity extends AppCompatActivity {
#Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
protected void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Fragment1 Fragment1 = new Fragment1();
getFragmentManager().beginTransaction().replace(
R.id.activity_main, Fragment1,
"Fragment 1").commit();
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage1(MessageEvent.Message1 event) {
Toast.makeText(getApplication(), event.str1,
Toast.LENGTH_LONG).show();
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage2(MessageEvent.Message2 event) {
Toast.makeText(getApplication(), event.str2,
Toast.LENGTH_LONG).show();
}
}
Inside ActivityReceiver class, replace
EventBus.getDefault().register(this);
with
EventBus.getDefault().register(this, MyEvent.class);
It really depends when and where this code exists. Remember that you must register for the events before you can receive them, and registering takes place at runtime, not compile time.
So, you must ensure that you are posting the event after you have registered the second activity. I would simply put some breakpoints on the following lines and ensure that the debugger stops here:
EventBus.getDefault().register(this);
before you get to here:
EventBus.getDefault().post(event);

synchronous messages from service to UI layer

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.

Categories

Resources