Android 前台服务配置问题导致蓝牙对讲功能异常

更新日期: 2023-02-20 阅读次数: 1003 字数: 892 分类: Android

基于 STM32 WB 蓝牙模块的开源 SDK 实现蓝牙对讲功能时,总是无法建立连接。 而使用官方 APP ST BLE Sensor 或者我自己不用 SDK 实现的 App 都可以正常建立连接。

最终发现是前台服务,及 Android 12 的兼容性问题引起的。做了下修改就可以正常运行了。

连接异常

我的状态异常:

Lost connection with the node BVL-WB1

而官方 APP 是在正常连接中显示:

连接到

对应的翻译文件:

<string name="progressDialogConnTitle">Connecting..</string>
<string name="progressDialogConnMsg">Connecting to: %s</string>
<string name="progressDialogConnMsgLostNodeError">Lost connection with the node %s</string>

显示代码在 ConnectionStatusView/ConnectionStatusView.java

@Override
public void showLostNodeError(String nodeName) {
	final String error = String.format(getContext().getString(R.string.progressDialogConnMsgLostNodeError), nodeName);
	mGuiThread.post(() -> showError(error));
}

ConnectionStatusView/ConnectionStatusController.java

private Node.NodeStateListener mNodeStateListener = new Node.NodeStateListener() {
	@Override
	public void onStateChange(@NonNull Node node, @NonNull Node.State newState, @NonNull Node.State prevState) {
		switch (newState){
			case Connecting:
				mView.showConnecting(node.getName());
				break;
			case Connected:
				mView.showConnected();
				break;
			case Disconnecting:
				break;
			case Lost:
				mView.showLostNodeError(node.getName());
				break;
			case Unreachable:
				mView.showUnreachableNodeError(node.getName());
				break;
			case Dead:
				mView.showDeadNodeError(node.getName());
				break;
			case Init:
			case Idle:
				break;
		}
	}

为何状态会变成 Lost 呢

日志中显示:

BVL-WB1 Idle->Lost

蓝牙模块有哪些状态

com/st/BlueSTSDK/Node.java

/** State of the node */
public enum State {
	/** dummy initial state */
	Init,

	/** the node is waiting for a connection, it is sending advertise message*/
	Idle,

	/** we open a connection with the node */
	Connecting,

	/** we are connected with the node, this status can be fired 2 times if we do a secure
	 * connection using bt pairing */
	Connected,

	/** we are closing the node connection */
	Disconnecting,

	/** we saw the advertise message for some time but now we do not receive anymore*/
	Lost,

	/** we were connected with the node but now it is disappear without
	 disconnecting */
	Unreachable,

	/** dummy final state */
	Dead
}//State

建立连接的过程

DemosActivity.java onResume

if(!mNode.isConnected()){
	mNode.addNodeStateListener(mBuildDemoAdapterOnConnection);
	NodeConnectionService.connect(this,mNode, mConnectionOption);

NodeConnectionService.java

/**
 * start the service asking to connect with the node
 * @param c context used for start the service
 * @param n node to connect
 * @param option connection options, if no parameters present the default one will be used
 */
static public void connect(Context c, Node n,@Nullable ConnectionOption option ){
	Intent i = new Intent(c,NodeConnectionService.class);
	i.setAction(CONNECT_ACTION);
	i.putExtra(NODE_TAG_ARG,n.getTag());
	if(option==null)
		option = ConnectionOption.builder().build();
	i.putExtra(CONNECTION_PARAM_ARG,option);
	ContextCompat.startForegroundService(c,i);
}

ContextCompat.startForegroundService(c,i);

调用之后,Service 对应的入口函数在哪里?

Inside the service, usually in onStartCommand(), you can request that your service run in the foreground. To do so, call startForeground(). This method takes two parameters: a positive integer that uniquely identifies the notification in the status bar and the Notification object itself.

在 onStartCommand 中打印日志,并没有显示。说明没有走这里。

加个 onCreate 试试,也没有显示日志。

感觉没有建立连接

但是我用自己写的 BLE 工具就可以正常的建立连接。

前台服务的 Manifest 配置

白天没定位到问题,下班的班车上搜了一下关于前台服务 (Foreground Service) 的资料。在微信公众号上找到不少有用的文章。

对照着,确实发现了几个问题:

Manifest 文件增加配置:

<!-- needed for the NodeConnectionService: Android 9 (API level 28) or higher -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>


<!-- application -->
<service
	android:name=".NodeConnectionService"
	android:enabled="true"
	android:exported="false" />

修改之后,就可以看到建立连接的状态显示了。

唯一难以理解的是,为何之前没有加配置任何提示都没有。

FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent

java.lang.RuntimeException: Unable to start service com.sunzhongwei.bluevoice.NodeConnectionService@f2b942d with Intent { act=com.sunzhongwei.bluevoice.NodeConnectionService.CONNECT cmp=com.sunzhongwei.bluevoice/.NodeConnectionService (has extras) }: java.lang.IllegalArgumentException: com.sunzhongwei.bluevoice: Targeting S+ (version 31 and above) requires that one of FLAG_IMMUTABLE or FLAG_MUTABLE be specified when creating a PendingIntent.

修复:

     private PendingIntent getDisconnectPendingIntent(Node n){
         Intent stopServiceIntent = buildDisconnectIntent(this,n);
         return PendingIntent.getService(this, STOP_SERVICE, stopServiceIntent,
-                PendingIntent.FLAG_ONE_SHOT);
+                //PendingIntent.FLAG_ONE_SHOT);
+                PendingIntent.FLAG_IMMUTABLE
+        );
     }

这里的 pending intent 用于点击通知条上的断开连接按钮时触发。

最后

至此,功能终于调通了,顺便学习了 Service 及 Foreground Service 的使用。

看来边动手写代码,边查,更适合我。

参考

  • https://medium.com/androiddevelopers/all-about-pendingintents-748c8eb8619

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式