Android怎么检测蓝牙的连接状态?如果一段断开,我这边怎么检测得到?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android怎么检测蓝牙的连接状态?如果一段断开,我这边怎么检测得到?相关的知识,希望对你有一定的参考价值。
按照蓝牙规范,一旦超时连接断开,会返回上层Disconnect complete Event with reason code: supervision timeout.可以根据该原因码,检测连接状态,具体方法为:1、BluetoothAdapter 顾名思义,蓝牙适配器,直到建立bluetoothSocket连接之前,都要不断操作它BluetoothAdapter里的方法很多,常用的有以下几个:cancelDiscovery() 根据字面意思,是取消发现,也就是说当正在搜索设备的时候调用这个方法将不再继续搜索disable()关闭蓝牙enable()打开蓝牙,这个方法打开蓝牙不会弹出提示,更多的时候需要问下用户是否打开,一下这两行代码同样是打开蓝牙,不过会提示用户:Intemtenabler=newIntent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler,reCode);//同startActivity(enabler);
getAddress()获取本地蓝牙地址getDefaultAdapter()获取默认BluetoothAdapter,实际上,也只有这一种方法获取BluetoothAdaptergetName()获取本地蓝牙名称。
2、getRemoteDevice(String address)根据蓝牙地址获取远程蓝牙设备getState()获取本地蓝牙适配器当前状态(感觉可能调试的时候更需要)isDiscovering()判断当前是否正在查找设备,是返回true***isEnabled()判断蓝牙是否打开,已打开返回true,否则,返回false***listenUsingRfcommWithServiceRecord(String name,UUID uuid)根据名称,UUID创建并返回BluetoothServerSocket,这是创建BluetoothSocket服务器端的第一步startDiscovery()开始搜索,这是搜索的第一步2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket
这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter。 参考技术A 如果是代码方面的话,断开时系统会发出一个特定的broadcast
监听这个广播就可以知道BlueTooth断开的事件 参考技术B 打开蓝牙,打开可检测性下面自动扫描的
当蓝牙连接状态发生变化时,防止onDestroy
目标
- 如果已连接的蓝牙设备断开连接,并且活动已在运行,请关闭活动
问题
- 当蓝牙设备连接状态通过BluetoothAdapterProperties:CONNECTION_STATE_CHANGE更改时,似乎创建了一个新的活动或当前的活动重新启动。
代码中没有任何内容可以监听和/或应该对蓝牙连接状态的变化作出反应。
问题表现在BroadcastReceivers的使用上,而BroadcastReceivers又使用意图启动Activity。出于某种原因,即使蓝牙连接的唯一变化是BluetoothAdapterProperties,活动也会在其生命周期中运行,产生新的窗口:CONNECTION_STATE_CHANGE
我已经在Android N的Nexus 6P上进行了测试。我还不知道这个实现对于任何其他设备意味着什么样的影响。但我至少需要在一台设备上使用它。
UPDATE
我做了一些实验,发现如果我没有在AndroidManifest中注册BroadcastReceiver,那么调用onDestroy的问题就会消失。但是,我希望能够对蓝牙连接设备作出反应,以便我可以启动我的活动,然后处理输入。如果每次新设备连接/断开连接时活动都被销毁,这根本不起作用。如果BroadcastReceiver已经在运行并且可以控制该行为,那么让它成为活动的原因是什么?
更新2
我还可以得出结论,使用这种方法https://stackoverflow.com/a/6529365/975641禁用静态声明的BroadcastReceiver并不会改进。一旦Manifest-BroadcastReceiver从Android捕获ACL_CONNECTED意图并启动我的自定义活动,当连接状态发生变化时(通常在ACL_DISCONNECTED之前),它将无情地在其上调用onDestroy。如果我在Manifest中声明了ACL_DISCONNECTED,则无关紧要。只要我的接收器侦听ACL_CONNECTED意图并基于此启动我的Activity,就会在连接状态发生变化时调用onDestroy。太令人沮丧了。
表现
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".BtActivity"
android:launchMode="singleTop" />
<receiver android:name=".BtConnectionBroadcastReceiver" android:priority="100000">
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.VOLUME_CHANGED_ACTION" />
</intent-filter>
</receiver>
</application>
BtConnectionBroadcastReceiver
public class BtConnectionBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "BT";
public static final String BROADCAST_ACTION_CONNECTED = "CONNECTED";
public static final String BROADCAST_ACTION_DISCONNECTED = "DISCONNECTED";
SharedPreferences mSharedPreferences;
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// When discovery finds a device
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
// Get the BluetoothDevice object from the Intent
Log.d(TAG, "DEVICE CONNECTED");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d("DEVICE NAME", device.getName());
Log.d("DEVICE ADDRESS", device.getAddress());
Intent i = new Intent(context, BtActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(i);
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
Log.d(TAG, "DEVICE DISCONNECTED");
intent = new Intent();
intent.setAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
context.sendBroadcast(intent);
}
}
BtActivity
public class BtActivity extends AppCompatActivity {
private static final String TAG = "BT";
Window mWindow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bt);
Log.d(TAG, "onCreate");
IntentFilter filter = new IntentFilter(BtConnectionBroadcastReceiver.INTENT_FILTER);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_CONNECTED);
filter.addAction(BtConnectionBroadcastReceiver.BROADCAST_ACTION_DISCONNECTED);
//registerReceiver(mReceiver, filter);
mWindow = getWindow();
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
//params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_OFF;
params.screenBrightness = 0.2f;
mWindow.setAttributes(params);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
mWindow.getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_IMMERSIVE);
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "BROADCAST RECEIVED IN ACTIVITY");
String mac;
if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_CONNECTED)) {
Log.d(TAG, "CONNECT BROADCAST RECEIVED");
mac = intent.getStringExtra("mac");
checkConnectedDevice(mac, true); // This adds a device to an internal list
Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
}
if(intent.getAction().equals(BtConnectionBroadcastReceiver.BROADCAST_DEVICE_DISCONNECTED)) {
Log.d(TAG, "DISCONNECT BROADCAST RECEIVED");
mac = intent.getStringExtra("mac");
checkConnectedDevice(mac, false); // This removes a device from an internal list
Log.d(TAG, "Activity nr of devices:" +mNrOfDevices);
if(mNrOfDevices < 1) {
Log.d(TAG, "No more connected devices");
finish();
}
}
abortBroadcast();
}
};
}
当我运行此代码时,我得到以下链:
- 启动MainActivity(不包括在内,它只包含一个具有默认主布局的活动,以便注册应用程序接收器)
- 打开蓝牙设备(这已经配对了,所以android知道它)
- 等到它连接并得到这个: 设备已连接 的onCreate 的onResume
- 关闭蓝牙设备然后我得到这个: 设备已断开连接 的onDestroy 的onCreate 的onResume
我无法理解为什么活动在此时重新开始被破坏。活动已在运行,BroadcastReceiver仅向已经运行的活动发送广播。我无法弄清楚为什么Activity会自行杀死然后重新启动。这使我处于Activity仍在运行的状态,但它不是启动的原始Activity。
然而,我确实在logcats中看到了一些似乎与此有关的东西,而且正是在这个排序中;
06-02 15:45:09.156 26431 26431 D BT:设备已断开连接
06-02 15:45:09.213 19547 19547 D BluetoothAdapterService:handleMessage() - MESSAGE_PROFILE_CONNECTION_STATE_CHANGED
06-02 15:45:09.213 26431 26431 D BTI:onDestroy
06-02 15:45:09.214 19547 19547 D BluetoothAdapterProperties:CONNECTION_STATE_CHANGE:FF:FF:20:00:C1:47:2 - > 0
06-02 15:45:09.216 3502 3805 D CachedBluetoothDevice:onProfileStateChanged:profile HID newProfileState 0
06-02 15:45:09.237 414 414 W SurfaceFlinger:无法登录到二进制事件日志:溢出。
06-02 15:45:09.239 26431 26431 D BT:onCreate
06-02 15:45:09.243 26431 26431 D BT:对不起
读过这个https://developer.android.com/guide/components/broadcasts.html#effects_on_process_state后,我可以安全地得出结论,为什么onDestroy被调用的原因是因为接收器影响它运行的进程,实际上意味着当接收器运行其onReceive方法时,它将自行销毁并使用它。
我当然希望它的工作方式不同,但我相信这是有效的,需要采取另一种方法。
在AndroidManifest.xml中为Activity添加以下内容,它对我有用。
android:configChanges="keyboard|keyboardHidden"
以上是关于Android怎么检测蓝牙的连接状态?如果一段断开,我这边怎么检测得到?的主要内容,如果未能解决你的问题,请参考以下文章
荣耀手表ES与运动健康App蓝牙连接自动断开或无法自动重连?
如何检测 Zidoo Digital Android Box 的 HDMI 连接或断开状态?