Сообщения поблизости с использованием IntentService
Изначально я настроил BroadcastReceiver
получать намерения от API Ближайших сообщений.
class BeaconMessageReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Nearby.getMessagesClient(context).handleIntent(intent, object : MessageListener() {
override fun onFound(message: Message) {
val id = IBeaconId.from(message)
Timber.i("Found iBeacon=$id")
sendNotification(context, "Found iBeacon=$id")
}
override fun onLost(message: Message) {
val id = IBeaconId.from(message)
Timber.i("Lost iBeacon=$id")
sendNotification(context, "Lost iBeacon=$id")
}
})
}
private fun sendNotification(context: Context, text: String) {
Timber.d("Send notification.")
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification = NotificationCompat.Builder(context, Notifications.CHANNEL_GENERAL)
.setContentTitle("Beacons")
.setContentText(text)
.setSmallIcon(R.drawable.ic_notification_white)
.build()
manager.notify(NotificationIdGenerator.nextID(), notification)
}
}
Затем зарегистрировал этот приемник в моем MainActivity
после того, как разрешения на местоположение были предоставлены.
class MainActivity : AppCompatActivity() {
// ...
private fun onLocationPermissionsGranted() {
val filter = MessageFilter.Builder()
.includeIBeaconIds(UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FEED"), null, null)
.build()
val options = SubscribeOptions.Builder().setStrategy(Strategy.BLE_ONLY).setFilter(filter).build()
Nearby.getMessagesClient(context).subscribe(getPendingIntent(), options)
}
private fun getPendingIntent(): PendingIntent = PendingIntent.getBroadcast(
this, 0, Intent(context, BeaconMessageReceiver::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
}
Это работало хорошо, когда приложение было открыто, но не работает, когда приложение закрыто. Итак, я нашел этот пример, который демонстрирует, как настроить IntentService
получать сообщения, пока приложение находится в фоновом режиме.
В примере используется Nearby.Messages
класс, который был осужден в пользу MessagesClient
, Поэтому я заменил устаревший код на MessagesClient
реализация.
class MainActivity : AppCompatActivity() {
// ...
private fun onLocationPermissionsGranted() {
val filter = MessageFilter.Builder()
.includeIBeaconIds(UUID.fromString("B9407F30-F5F8-466E-AFF9-25556B57FEED"), null, null)
.build()
val options = SubscribeOptions.Builder().setStrategy(Strategy.BLE_ONLY).setFilter(filter).build()
Nearby.getMessagesClient(context).subscribe(getPendingIntent(), options)
.addOnSuccessListener {
Timber.i("Subscribed successfully.")
startService(Intent(this, BeaconMessageIntentService::class.java))
}.addOnFailureListener {
Timber.e(exception, "Subscription failed.")
}
}
private fun getPendingIntent(): PendingIntent = PendingIntent.getBroadcast(
this, 0, Intent(context, BeaconMessageIntentService::class.java), PendingIntent.FLAG_UPDATE_CURRENT)
}
И это IntentService
(который почти идентичен моему BroadcastReceiver
).
class BeaconMessageIntentService : IntentService("BeaconMessageIntentService") {
override fun onHandleIntent(intent: Intent?) {
intent?.let {
Nearby.getMessagesClient(this)
.handleIntent(it, object : MessageListener() {
override fun onFound(message: Message) {
val id = IBeaconId.from(message)
Timber.i("Found iBeacon=$id")
sendNotification("Found iBeacon=$id")
}
override fun onLost(message: Message) {
val id = IBeaconId.from(message)
Timber.i("Lost iBeacon=$id")
sendNotification("Lost iBeacon=$id")
}
})
}
}
private fun sendNotification(text: String) {
Timber.d("Send notification.")
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification = NotificationCompat.Builder(this, Notifications.CHANNEL_GENERAL)
.setContentTitle("Beacons")
.setContentText(text)
.setSmallIcon(R.drawable.ic_notification_white)
.build()
manager.notify(NotificationIdGenerator.nextID(), notification)
}
}
onHandleIntent
называется, а Intent
не является null
; еще по какой-то причине onFound()
а также onLost()
никогда не называются. Почему это так?
1 ответ
Это не совсем решение, но то, что я нашел, это ( кредит на этот ответ):
Я пробовал несколько конфигураций, включая BroadcastReceiver
и добавление JobIntentService
чтобы запустить код в фоновом режиме, но каждый раз, когда я получил это onExpired
обратный вызов, который вы можете установить на SubscribeOptions
:
options.setCallback(new SubscribeCallback() {
@Override
public void onExpired() {
super.onExpired();
Toast.makeText(context.get(), "No longer Subscribing!", Toast.LENGTH_SHORT).show();
}
}
Когда подписка произошла в фоновом режиме, она была отложена, но все еще вызывалась.
Заметки:
1. Когда я проверил с Strategy.BLE_ONLY
Я не получил onFound
Перезвоните.
2. Из документации Google:
Фоновые подписки потребляют меньше энергии, чем приоритетные, но имеют большую задержку и меньшую надежность
При тестировании я обнаружил, что эта "низкая надежность" является преуменьшением: onFound
редко звонили, и я так и не получил onLost
,
Я знаю, что это запоздалый ответ, но у меня была та же проблема, и при отладке я выяснил, что это проблема, связанная с этой ошибкой: "Попытка выполнить высокопроизводительную операцию из контекста, не связанного с активностью". Это можно решить при звонкеNearby.getMessagesClient(this)
передавая вместо this контекст действия.
В моем случае я добавил класс, расширяющий приложение, которое помогает в возврате этого контекста (приведенное ниже находится в java, но должно быть легко переведено на kotlin)
public class MyApplication extends Application {
private Activity currentActivity = null;
public Activity getCurrentActivity(){
return currentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity){
this.currentActivity = mCurrentActivity;
}
}
И в моем базовом действии, от которого исходят все действия, я устанавливаю текущее действие, вызывая ((MyApplication) this.getApplicationContext()).setCurrentActivity(this);
в конструкторе.
Затем моя служба может вызвать getMessagesClient с правильным контекстом, как показано ниже:
final Activity context = ((MyApplication)getApplicationContext()).getCurrentActivity();
Nearby.getMessagesClient(context).[...]
Не забудьте зарегистрировать свой класс Application в AndroidManifest:
<application
android:name="com.example.xxx.MyApplication"`