系统内自己封装的一个应用,在Android11 上连接蓝牙耳机和蓝牙鼠标键盘没啥问题的,
但是移植到Android13 的系统上经常遇到连接蓝牙鼠标键盘是吧的问题。
发现原生Settings 连接未出现这种问题,所有应该是代码处理需要优化。
车载项目蓝牙设置分析研究:
https://blog.csdn.net/liu362732346/article/details/81866575
Android 早期版本 Settings及BluetoothSettings流程分析:
https://blog.csdn.net/weixin_40919230/article/details/80419671
看了下类名称都不一样了,想直接用里面的关键字直接找到蓝牙调用的api好像也不太行!
这里分享一下原生Settings 中蓝牙连接的相关代码分析。
packages\apps\Settings\AndroidManifest.xml
从上面能看到Settings 的主界面和快速入口,activity-alias 是Acitivity的别名属性,
activity-alias 一般用来设置快速入口和图标,一般app都不怎么用。
所以启动原生Settings 可以用命令:
//快速入口
am start -n com.android.settings/.Settings
//主界面
am start -n com.android.settings/.homepage.SettingsHomepageActivity
Settings\src\com\android\settings\Settings.java
public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
里面有上百个空实现的Activity,只复用了 SettingsActivity 的Activity!
这样写的原因是啥???暂时不清楚这个架构!
这个TV平台的Settings 代码,没有NFC,这个"已连接的设备"界面就是蓝牙控制连接和显示已保存蓝牙设备的界面。
packages\apps\Settings\src\com\android\settings\connecteddevice\ConnectedDeviceDashboardFragment.java
ConnectedDeviceDashboardFragment 对应的 PreferenceScreen 布局: connected_devices.xml
对应的xml 文件 R.xml.connected_devices :
上面的布局默认显示蓝牙界面
Settings\src\com\android\settings\connecteddevice\BluetoothDashboardFragment.java
还有相关的:AdvancedConnectedDeviceDashboardFragment
packages\apps\Settings\res\xml\connected_devices_advanced.xml
布局文件:
//字符串"与新设备配对"的布局
Settings\res\xml\bluetooth_screen.xml
//字符串"连接偏好设置"的布局
Settings\res\xml\connected_devices.xml
上面很多相关跳转还是有点混乱的,可以多加log打印查看。
追一下里面的相关逻辑就能看到调用的具体api了。其实关键就是这个类之后的调用。
//点击"与新设备配对"条目后的配对列表界面
Settings\src\com\android\settings\bluetooth\BluetoothPairingDetail.java
对应布局:
Settings\res\xml\bluetooth_pairing_detail.xml
从上面Java代码和xml 文件中是看不出列表数据的。
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
LogUtil.debug("btPreference =" + btPreference);//点击每个条目时,是有打印的!自己加的
disableScanning();
super.onDevicePreferenceClick(btPreference);//所以重点是看父类的点击事件
}
BluetoothPairingDetail 的父类是 DeviceListPreferenceFragment。
Settings\src\com\android\settings\bluetooth\DeviceListPreferenceFragment.java
//扫描到的可以连接的蓝牙集合列表
final HashMap mDevicePreferenceMap = new HashMap<>();
//父类中的点击事件
void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
btPreference.onClicked();//继续追踪 onClicked 方法
}
Settings\src\com\android\settings\bluetooth\BluetoothDevicePreference.java
protected final CachedBluetoothDevice mCachedDevice;
//这里就是原生Settings 中根据蓝牙条目的状态,调用蓝牙的具体api实现。
void onClicked() {
Context context = getContext();
int bondState = mCachedDevice.getBondState();
LogUtil.debug("bondState = " + bondState);
final MetricsFeatureProvider metricsFeatureProvider =
FeatureFactory.getFactory(context).getMetricsFeatureProvider();
if (mCachedDevice.isConnected()) { //条目已连接,弹框提示是否断开连接
metricsFeatureProvider.action(context,
SettingsEnums.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
askDisconnect();
} else if (bondState == BluetoothDevice.BOND_BONDED) { //已保存的情况,直接进行连接操作
metricsFeatureProvider.action(context,
SettingsEnums.ACTION_SETTINGS_BLUETOOTH_CONNECT);
mCachedDevice.connect();
} else if (bondState == BluetoothDevice.BOND_NONE) {
metricsFeatureProvider.action(context,
SettingsEnums.ACTION_SETTINGS_BLUETOOTH_PAIR);
if (!mCachedDevice.hasHumanReadableName()) {
metricsFeatureProvider.action(context,
SettingsEnums.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
}
pair();
}
}
//未连接的设备,配对
private void pair() {
if (!mCachedDevice.startPairing()) { //这里看到只是调用了 startPairing 方法!
Utils.showError(getContext(), mCachedDevice.getName(),
R.string.bluetooth_pairing_error_message);
}
}
packages\apps\Settings\src\com\android\settings\bluetooth\BluetoothDeviceDetailsFragment.java
//点击已连接的蓝牙条目,跳转显示"取消保存"和"断开连接"界面
//点击"断开连接"–》变成"连接",进入可连接状态
@Override
protected List createPreferenceControllers(Context context) {
ArrayList controllers = new ArrayList<>();
if (mCachedDevice != null) {
Lifecycle lifecycle = getSettingsLifecycle();
controllers.add(new BluetoothDetailsHeaderController(context, this, mCachedDevice,
lifecycle, mManager));
//"取消保存","断开连接","连接" 事件都 BluetoothDetailsButtonsController 在里面
controllers.add(new BluetoothDetailsButtonsController(context, this, mCachedDevice,
lifecycle));
controllers.add(new BluetoothDetailsCompanionAppsController(context, this,
mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsSpatialAudioController(context, this, mCachedDevice,
lifecycle));
controllers.add(new BluetoothDetailsProfilesController(context, this, mManager,
mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice,
lifecycle));
controllers.add(new BluetoothDetailsRelatedToolsController(context, this, mCachedDevice,
lifecycle));
controllers.add(new BluetoothDetailsPairOtherController(context, this, mCachedDevice,
lifecycle));
}
return controllers;
}
//点击"取消保存"触发
private void onForgetButtonPressed() {
LogUtil.debug("");
ForgetDeviceDialogFragment fragment =
ForgetDeviceDialogFragment.newInstance(mCachedDevice.getAddress());
fragment.show(mFragment.getFragmentManager(), ForgetDeviceDialogFragment.TAG);
}
//界面创建的时候,"取消保存"按钮
@Override
protected void init(PreferenceScreen screen) {
mActionButtons = ((ActionButtonsPreference) screen.findPreference(
getPreferenceKey()))
.setButton1Text(R.string.forget)
.setButton1Icon(R.drawable.ic_settings_delete)
.setButton1OnClickListener((view) -> onForgetButtonPressed())
.setButton1Enabled(true);
}
//界面resume的时候化,更新蓝牙的状态显示:"断开连接"/"连接"按钮,并添加监听事件
@Override
protected void refresh() {
mActionButtons.setButton2Enabled(!mCachedDevice.isBusy());
boolean previouslyConnected = mIsConnected;
mIsConnected = mCachedDevice.isConnected();
if (mIsConnected) {
if (!mConnectButtonInitialized || !previouslyConnected) {
mActionButtons
.setButton2Text(R.string.bluetooth_device_context_disconnect)
.setButton2Icon(R.drawable.ic_settings_close)
.setButton2OnClickListener(view -> {
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
//断开蓝牙的api
mCachedDevice.disconnect();
});
mConnectButtonInitialized = true;
}
} else {
if (!mConnectButtonInitialized || previouslyConnected) {
mActionButtons
.setButton2Text(R.string.bluetooth_device_context_connect)
.setButton2Icon(R.drawable.ic_add_24dp)
.setButton2OnClickListener(
view -> {
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_SETTINGS_BLUETOOTH_CONNECT);
//连接断开过的蓝牙的api
mCachedDevice.connect();
});
mConnectButtonInitialized = true;
}
}
}
Settings\src\com\android\settings\bluetooth\ForgetDeviceDialogFragment.java
private CachedBluetoothDevice mDevice;
@Override
public Dialog onCreateDialog(Bundle inState) {
DialogInterface.OnClickListener onConfirm = (dialog, which) -> {
//确认忘记蓝牙的api
mDevice.unpair();
Activity activity = getActivity();
if (activity != null) {
activity.finish();
}
};
Context context = getContext();
mDevice = getDevice(context);
AlertDialog dialog = new AlertDialog.Builder(context)
.setPositiveButton(R.string.bluetooth_unpair_dialog_forget_confirm_button,
onConfirm)
.setNegativeButton(android.R.string.cancel, null)
.create();
dialog.setTitle(R.string.bluetooth_unpair_dialog_title);
dialog.setMessage(context.getString(R.string.bluetooth_unpair_dialog_body,
mDevice.getName()));
return dialog;
}
//似乎只执行了这个:
mDevice.unpair();
//获取蓝牙列表
LocalBluetoothManager mLocalManager = LocalBluetoothManager.getInstance(context, mOnInitCallback);;
mLocalManager.getCachedDeviceManager().clearNonBondedDevices();//清除扫描列表
Collection cachedDevices = mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();//获取扫描列表
//蓝牙单个对象 CachedBluetoothDevice
protected final CachedBluetoothDevice mCachedDevice;
//1、未连接的情况,配对+连接
boolean isParing = mCachedDevice.startPairing();
//2、已连接的情况,断开连接,进入保存状态
mCachedDevice.disconnect();
//3、断开连接,进入保存状态的情况,重新连接
mCachedDevice.connect();
//4、连接/保存状态,忘记设备
mCachedDevice.unpair();
LocalBluetoothManager 和 CachedBluetoothDevice 都是系统SettingsLib包中封装的类
LocalBluetoothManager 里面其实是封装了LocalBluetoothAdapter ,最后是调用了原生的 BluetoothAdapter;
CachedBluetoothDevice 其实是对蓝牙基本数据 BluetoothAdapter 的进一步封装。
Android 9 好像是看过TvSettings 的蓝牙连接过程,跟原生Settings 写法好像并不完全相同!
而 LocalBluetoothManager 的api的调用具体是在原生api上做了什么优化,需要大家自己探索研究了!