Сообщения поблизости с использованием 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"`
Другие вопросы по тегам