Пользовательский интерфейс не обновляется, даже если вызывается функция (состояние гонки?) - PullRequest
0 голосов
/ 06 февраля 2020

У меня есть класс BluetoothService, который использует потоки для выполнения своих задач. Я делаю экземпляр этого в моем MainActivity, внутри объекта-компаньона. При этом я также могу использовать тот же экземпляр класса в NotificationListener, что и NotificationListenerService. Из-за того, что NotificationListenerService, он всегда работает в фоновом режиме. Это делает мой BluetoothService экземпляр всегда живым даже после закрытия приложения. Экземпляр BluetoothService сообщает MainActivity о некоторых изменениях состояния с помощью обработчика (поскольку действия происходят внутри потоков). Таким образом, проблема заключается в том, что после подключения к устройству Bluetooth и закрытия приложения устройство остается подключенным, поскольку NotificationListener работает в фоновом режиме. И вот тут появляются странные проблемы. Например, когда я нажимаю на кнопку «отключить», он отключается от устройства и вызывает функцию updateUi (я знаю, что эта функция вызывается, потому что я добавляю журнал, и он показывает). Но интерфейс не обновляется. Что может происходить? Я не добавляю никакого кода, потому что я не знаю, где может быть проблема, я думаю, что это скорее концептуальная ошибка, которую я избегаю. Но не стесняйтесь спрашивать, какие части кода, по вашему мнению, могут помочь решить проблему

Попробуйте самостоятельно:

Создайте новую Android Studio ( последняя версия) проект, мин. API 21, пакет com.example.sample.

Добавьте это в свой AndroidManifest.xml, внутри <application> тег:

<service
    android:name=".NotificationListener"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">

    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>

</service>

MainActivity.kt должно быть:

package com.example.sample

import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.view.View
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

    companion object {
        val bluetoothService: BluetoothService = BluetoothService()
        var handler: Handler? = null
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (handler == null) {
            handler = object : Handler(Looper.getMainLooper()) {
                override fun handleMessage(inputMessage: Message) {
                    updateState()
                }
            }
        }
    }

    override fun onResume() {
        super.onResume()
        updateState()
    }

    private fun updateState() {
        findViewById<TextView>(R.id.test).text = when (bluetoothService.connectionState) {
            BluetoothService.STATE_CONNECTING -> "Connecting"
            BluetoothService.STATE_CONNECTED -> "Connected"
            else -> "Not connected"
        }
    }

    fun connect(view: View) {
        bluetoothService.connect()
    }

    fun disconnect(view: View) {
        bluetoothService.disconnect()
    }

}

Новый класс, называемый BluetoothService, содержащий:

package com.example.sample

class BluetoothService {

    companion object {
        const val STATE_DISCONNECTED = 0
        const val STATE_CONNECTING = 1
        const val STATE_CONNECTED = 2
    }

    var connectionState = STATE_DISCONNECTED

    private fun updateConnectionState(state: Int) {
        connectionState = state
        MainActivity.handler?.obtainMessage(connectionState)?.sendToTarget()
    }

    fun connect() = ConnectThread().start()

    fun disconnect() = updateConnectionState(STATE_DISCONNECTED)

    private inner class ConnectThread : Thread() {

        init {
            sleep(1000)
            updateConnectionState(STATE_CONNECTING)
        }

        override fun run() {
            sleep(1000)
            updateConnectionState(STATE_CONNECTED)
        }

    }
}

И класс NotificationListenerService с:

package com.example.sample

import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification

class NotificationListener : NotificationListenerService() {

    override fun onNotificationPosted(sbn: StatusBarNotification) {
        MainActivity.bluetoothService // using the service here makes it stay always alive
    }

    override fun onNotificationRemoved(sbn: StatusBarNotification) {}

}

И создайте ваш Activity_main. xml вот так:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Not connected"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <Button
        android:id="@+id/test2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Connect"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/test"
        android:onClick="connect"/>

    <Button
        android:id="@+id/test3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Disconnect"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/test2"
        android:onClick="disconnect"/>

</androidx.constraintlayout.widget.ConstraintLayout>

Теперь попробуйте. Соединение заставляет текст показывать соединение и затем соединено. Отключение кнопки делает его go обратно не подключенным. Go к настройкам вашего устройства и предоставьте этому приложению доступ к уведомлениям. Теперь go снова в приложение и нажмите кнопку подключения. Подключение, а затем подключенные шоу. Закройте приложение (перечитайте и проведите пальцем, полностью закрытое, не останавливайте принудительно). Теперь go снова к приложению, и он говорит, что подключен. Нажмите на кнопку отключения и включите, ничего не происходит, текст не обновляется, но функция работает. Вы можете добавить журнал в функцию updateState и посмотреть, как он появляется. Действительно странно

1 Ответ

1 голос
/ 07 февраля 2020

Ну, я на самом деле понял это. Проблема была в if (handler == null) регистрации MainActivity -> onCreate. Похоже, что обработчик был оставлен после последнего выполнения, поэтому все работало, но изменения пользовательского интерфейса не применялись к реальному пользовательскому интерфейсу. Странно, но кажется разумным

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...