通过接口将数据从类传递到活动并修改 UI



我有一个处理蓝牙通信的类,这个类需要将从蓝牙接收的数据转发到我的活动类。我曾经通过广播传递这些数据,但现在我正在尝试通过接口传递数据。我遇到的问题是,在我的活动中收到数据后,我无法在 UI 中进行更改,并且出现一条错误消息,指出"只有创建视图层次结构的原始线程才能触摸其视图"。

//My Activity class implements an interface and receiveDataFromBluetoothConnection is a method in that interface
//My bluetooth service sends data to my activity via this method.
public void receiveDataFromBluetoothConnection(String data) {
    processIncomingBtMessage(data);
}
private void processIncomingBtMessage(String incomingMessage) {
    //...
    //...
    //...
    if (message == BtMessageIn.BT_MESSAGE_IN_SYSTEM_OFF) {
        Log.d(TAG, "remoteControlBubblePillar: bReceiver: Setting Button to On");
        arduinoPowerStatus = false;
        LightsButtonsBackgroundUnpressed();
        btnOnOff.setBackgroundResource(R.drawable.button_shape_round_corners_gradient_green);
        btnOnOff.setText(R.string.On);
    }
    //...
    //...
    //...
}

这是我收到的完整错误消息

 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7913)
    at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1373)
    at android.view.ViewGroup.invalidateChild(ViewGroup.java:5449)
    at android.view.View.invalidateInternal(View.java:14825)
    at android.view.View.invalidate(View.java:14761)
    at android.view.View.invalidateDrawable(View.java:19051)
    at android.widget.TextView.invalidateDrawable(TextView.java:6353)
    at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:436)
    at android.graphics.drawable.Drawable.setVisible(Drawable.java:820)
    at android.view.View.setBackgroundDrawable(View.java:19522)
    at android.support.v7.widget.AppCompatButton.setBackgroundDrawable(AppCompatButton.java:86)
    at android.view.View.setBackground(View.java:19498)
    at android.view.View.setBackgroundResource(View.java:19481)
    at android.support.v7.widget.AppCompatButton.setBackgroundResource(AppCompatButton.java:78)
    at com.bubblewall.saik.bubblewall.remoteControlBubblePillar.processIncomingBtMessage(remoteControlBubblePillar.java:354)
    at com.bubblewall.saik.bubblewall.remoteControlBubblePillar.receiveDataFromBluetoothConnection(remoteControlBubblePillar.java:275)
    at com.bubblewall.saik.bubblewall.BluetoothConnection$1.onCharacteristicChanged(BluetoothConnection.java:97)
    at android.bluetooth.BluetoothGatt$1.onNotify(BluetoothGatt.java:400)
    at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:177)
    at android.os.Binder.execTransact(Binder.java:573)

任何想法如何解决此问题?还是我应该切换回广播?

问题是您的蓝牙类不像活动那样在主/UI 线程上运行。为了避免竞争条件,Android 会让应用程序崩溃,让您知道自己做错了什么。

幸运的是,有一种简单的方法可以使用runOnUiThread(Runnable)来解决此问题:

public void receiveDataFromBluetoothConnection(String data) {
    runOnUiThread(() -> processIncomingBtMessage(data));
}

但我有一个更好的建议。与其让活动处理此问题,不如设计您的其他类,以便确保当它通知您的活动时,它会在正确的线程上执行此操作。

new Handler(Looper.getMainLooper()).post(() -> listener.receiveDataFromBluetoothConnection(data))
最好

这样做,因为如果其他活动使用蓝牙类,他们不必担心确保在正确的线程上执行代码,蓝牙类已经处理了这个问题。

有关正在发生的事情以及处理它的正确方法的更深入说明,请阅读 developer.android.com 上的与 UI 线程通信

最新更新