Можно ли ссылаться на контекст в службе, но не используя контекст приложения?
Я пытаюсь добиться возможности показывать закусочную из службы, когда что-то происходит. Это должно быть выполнено в НЕСКОЛЬКИХ действиях. Для этого мне нужен контекст, а затем ссылка на него, чтобы получить представление, используя эту строку:
View rootView = ((Activity)context).getWindow().getDecorView().findViewById(android.R.id.content)
Я делаю это в своем сервисе:
class MyService : Service() {
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
//TODO do something useful
val view: View = (this.applicationContext as Activity).window.decorView.findViewById(android.R.id.content)
Timber.d("some jobe done")
return Service.START_NOT_STICKY
}
override fun onBind(intent: Intent): IBinder? {
//TODO for communication return IBinder implementation
return null
}
}
Однако я получаю сообщение об ошибке при обращении к контексту приложения и приведению его к Activty:
Caused by: java.lang.ClassCastException: com.myapp.Application cannot be cast to android.app.Activity
Можно ли с этим справиться должным образом (в отношении моего варианта использования)?
I am trying to achive ability to show snackbar from service when something occurs.
Это плохая идея. Например, он не касается сценария, в котором у вас нет текущих действий.
To do this i need context and then refer from it to get the view using this line:
Нет, вам понадобится объект Activity
, как вы видите по вашему слепку.
Is it possible to handle this properly (refering my use case)?
Не так, как вы подходите к проблеме.
Ваша ситуация такова: служба обнаруживает какое-то событие, и служба хочет сообщить пользователю об этом событии. Есть две возможности:
У вас есть видимое действие, которое затем может сообщить пользователю о событии с помощью некоторых подходящих средств (например, закусочной).
У вас нет активности, которая является видимой, и в этом случае служба может вызвать уведомление или, возможно, просто отбросить событие (если нет необходимости сообщать пользователю об этом, если пользователь не находится в пользовательском интерфейсе приложения )
Типичный подход к этой проблеме - использовать ту или иную форму шины событий (LocalBroadcastManager
, EventBus greenrobot или любое количество реализаций шины событий на основе RxJava):
Сервис размещает мероприятие в автобусе.
Каждое действие подписывается на шину для событий, пока оно видно, и отменяет подписку, когда оно больше не отображается (например, через onStart()
/ onStop()
)
Если действие получает событие, оно показывает пользователю снэк-бар (или что-то еще).
Если служба определяет, что событие не зафиксировано, она использует некоторый запасной подход.
Этот подход предлагает лучшее разделение проблем, поскольку сервис не должен заботиться о деталях того, как ваш пользовательский интерфейс позволяет пользователю узнать о событии. Он также аккуратно обрабатывает случай, когда у вас нет видимой активности для закусочной.
Это пример приложения демонстрирует этот подход с помощью EventBus greenrobot, и
это пример приложения демонстрирует этот подход с LocalBroadcastManager
.
Существуют и другие решения, которые не используют настоящую шину событий, например, служба обновления репозитория, которая обновляет компоненты пользовательского интерфейса с помощью реактивных средств (например, RxJava, ведущего к LiveData
). Однако служба не должна пытаться напрямую манипулировать пользовательским интерфейсом действия, отчасти потому, что действия может не быть.
@Konrad: «у меня их много во всем приложении» - используйте наследование, чтобы минимизировать дублирование кода.
Да, но видение установки подписки на все мои действия довольно плохое для меня, у меня их много во всем моем приложении. Если бы у меня была эта возможность в моем Сервисе, я бы не заботился о реализации подписки во всех из них и просто показывал Snackbar, когда что-то происходит там, где есть активность (например, с Toast, он работает, поскольку для работы ему нужен только контекст)