Как опубликовать сообщение из фоновой службы во фрагмент пользовательского интерфейса?

У меня проблема с EventBus от Greenrobot.

Я пытался опубликовать событие из фоновой службы из моего адаптера синхронизации и поймать его во фрагменте, чтобы обновить пользовательский интерфейс.

Проблема в том, что когда я пытаюсь опубликовать событие из адаптера синхронизации, я получаю следующее в журнале отладки:

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

Я регистрирую фрагмент в onResume и отменяю регистрацию в 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);
}

Оператор журнала в onResume показывает, что фрагмент успешно зарегистрирован.

Вот метод onEvent:

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

Я пытался вызвать метод onEvent с фоновым режимом потока, но это не помогло.

После этого я пытался использовать обработчик для публикации события, но eventbus по-прежнему не может найти зарегистрированных подписчиков для события.

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

Вот мой метод onPerform адаптера синхронизации:

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

Как я могу отправить событие из адаптера синхронизации во фрагмент, используя EventBus Greenrobot?


person Olexii Muraviov    schedule 28.04.2016    source источник
comment
Попробуйте изменить обработчик событий с onEvent на onEventMainThread. Вы проверили, прошел ли ваш фрагмент также onPause?   -  person Francesc    schedule 28.04.2016
comment
Я попытался изменить имя метода, а также попытался добавить поток MAIN в аннотацию, но это не помогло. Да, я проверил это. @Francesc   -  person Olexii Muraviov    schedule 28.04.2016
comment
Итак, ваш фрагмент приостановлен? Очевидно, что он не сможет ничего получить, если он прошел через onPause.   -  person Francesc    schedule 28.04.2016
comment
Нет, фрагмент работает во время вызова почтового метода @Francesc   -  person Olexii Muraviov    schedule 29.04.2016


Ответы (1)


тл;др

Вам никогда не придется беспокоиться о том, из какой потоки вы публикуете событие, но вам нужно беспокоиться о том, из какого процесса вы публикуете его. Ваш адаптер синхронизации работает в другом процессе, отличном от вашего фрагмента — EventBus работает только в том процессе, в котором он был создан.

Длинная версия

Вам никогда не нужно запускать разные темы или даже беспокоиться о том, в какой теме вы находитесь, за исключением того места, где вы подписываетесь на событие.

проверьте http://greenrobot.org/eventbus/documentation/delivery-threads-threadmode/ для более подробной информации, но синтаксис говорит сам за себя:

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

И запустить что-то в пользовательском интерфейсе:

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

Когда вы вызываете post() для запуска как DoLoginEvent, так и LoginSuccessfulEvent, абсолютно не имеет значения, в каком потоке вы находитесь.

Теперь к реальной проблеме, которая у вас есть: события, похоже, не фиксируются на объектах подписки.

Я подозреваю, что это связано с тем, что ваш адаптер синхронизации работает в другом процессе. EventBus работает только внутри Процесса, в котором она создана.

Адаптер синхронизации, такой как BroadcastReceiver или Service, запускается в отдельном процессе, что позволяет вам синхронизировать данные с вашим сервером без какого-либо взаимодействия с самим вашим приложением. Идея состоит в том, что вы синхронизируетесь со своим сервером, даже когда приложение закрыто, сохраняете все в базу данных, а затем, когда приложение запускается, оно проверяет эту базу данных (быстро), вместо того, чтобы синхронизироваться с сервером (медленно). Но если ваше приложение активно, когда работает адаптер синхронизации, то вы хотите сохранить в базе данных, но также сообщить приложению, что есть какая-то новая информация, верно?

Поэтому, чтобы ваш адаптер синхронизации мог взаимодействовать с вашим приложением, я бы посоветовал вам настроить BroadcastReceiver в вашем фрагменте/действии, а затем передать намерение от вашего адаптера синхронизации.

Этот механизм очень похож на концепцию EventBus, вы даже можете отправлять android">липкие намерения! Ваш фрагмент прислушивается к этим намерениям, но только пока он жив. Если ваш адаптер синхронизации отправляет широковещательную рассылку, когда ваше приложение закрыто, ничего не происходит. Но когда фрагмент запустится и зарегистрируется для этих намерений, он получит последний липкий.

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

А затем с вашего адаптера синхронизации

Intent sendIntent = new Intent("com.whatever.whatever");
sendIntent.putExtra("SYNC_ADAPTER_EXTRA", "Your sync adapter says hi");
context.sendBroadcast(sendIntent);
person shredder    schedule 29.04.2016