Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to post a message from a background service to a UI fragment?

I have an issue with EventBus from Greenrobot.

I was trying to post an event from a background service form my sync adapter and catch it in a fragment to update the UI.

The problem is that when I try to post the event from sync adapter I have get the following in the debug log:

No subscribers registered for event class olexiimuraviov.ua.simplerssreader.event.UpdateUIEvent
No subscribers registered for event class org.greenrobot.eventbus.NoSubscriberEvent

I register fragment in onResume and unregister it in onPause

@Override
public void onResume() {
    super.onResume();
    EventBus.getDefault().register(this);
    if (mDebug) Log.d(LOG_TAG, EventBus.getDefault().isRegistered(this) + "");
}

@Override
public void onPause() {
    super.onPause();
    EventBus.getDefault().unregister(this);
}

Log statement in onResume shows that fragment is successfully registered.

Here is onEvent method:

@Subscribe
public void onEvent(UpdateUIEvent event) {
    if (mSwipeRefreshLayout.isRefreshing())
        mSwipeRefreshLayout.setRefreshing(false);
}

I was trying to call onEvent method with Background threadmode but it didn't help.

After this I was trying to use handler to post event but eventbus still can't find any registered subscribers for event.

 new Handler(Looper.getMainLooper()).post(new Runnable() {
     @Override
     public void run() {
         EventBus.getDefault().post(new UpdateUIEvent());
     }
 });

Here is my onPerform method of sync adapter:

    @Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
    // Fetch data from server
    } finally {
       // close everything
       new Handler(Looper.getMainLooper()).post(new Runnable() {
           @Override
           public void run() {
               Log.d(LOG_TAG, "Update event");
               EventBus.getDefault().post(new UpdateUIEvent());
           }
       });
    }
}

How can I send event from a sync adapter to a fragment using Greenrobot's EventBus?

like image 487
Olexii Muraviov Avatar asked Feb 07 '23 03:02

Olexii Muraviov


1 Answers

tl;dr

You never have to worry about which thread you post an event from, but you do have to worry about which process you post it from. Your sync adapter is running in a different process to your fragment - the EventBus only works within the process in which it was created.

Long Version

You never need to spin up different threads or even worry about which thread you are on anywhere except where you subscribe for the event.

check http://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/ for more details but the syntax is very self explanatory:

@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void thisMethodWillBeRunInABackgroundThread(DoLoginEvent event)
{
    //Run some code that makes an HTTP request
}

And to run something on the UI:

@Subscribe(threadMode = ThreadMode.MAIN)
public void thisMethodWillBeRunOnTheUIThread(LoginSuccessfulEvent event)
{
    //Run some code that touches the UI
}

When you call post() to fire off both the DoLoginEvent and the LoginSuccessfulEvent it absolutely doesn't matter which thread you are in.

Now to the real problem that you have: events don't seem to be captured on the subscribing objects.

I suspect that this is because your sync adapter runs in a different Process. The EventBus only operates within the Process it is created.

A sync adapter, like a BroadcastReceiver or a Service runs in a separate process to allow you to sync data with your server without any interaction with your App itself. The idea is that you sync with your server even while the app is closed, save whatever to a database, then when the app starts up, it checks that database (fast) instead of having to sync with the server (slow). But if your app is alive when the sync adapter runs, then you want to save to the database but also tell the app that there is some new information right?

So in order to allow your sync adapter to communicate with your application I would advise you to setup a BroadcastReceiver in your fragment/activity and then broadcast an intent from your sync adapter.

This mechanism is a very similar concept to the EventBus, you can even send sticky intents! Your fragment listens for these intents, but only while it's alive. If your sync adapter sends a broadcast while your application is closed, then nothing happens. But when the fragment starts up and registers for those intents, it will get the last sticky one.

public class LogFragment extends Fragment {

    private LogReceiver mReceiver;

    public class LogReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //you can call the EventBus from in here
        }
    }


    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReceiver = new LogReceiver();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        getActivity().registerReceiver(mReceiver, new IntentFilter("com.whatever.whatever"));
        return view;
    }

    @Override
    public void onDestroyView() {
        getActivity().unregisterReceiver(mReceiver);
        super.onDestroy();
    }
}

And then from your Sync Adapter

Intent sendIntent = new Intent("com.whatever.whatever");
sendIntent.putExtra("SYNC_ADAPTER_EXTRA", "Your sync adapter says hi");
context.sendBroadcast(sendIntent);
like image 108
shredder Avatar answered Feb 09 '23 17:02

shredder