我有一个处理蓝牙通信的类,这个类需要将从蓝牙接收的数据转发到我的活动类。我曾经通过广播传递这些数据,但现在我正在尝试通过接口传递数据。我遇到的问题是,在我的活动中收到数据后,我无法在 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 线程通信。