蓝牙设备扫描需要定位权限的原因在于蓝牙定位的存在,蓝牙定位属于精确定位,因此需要开启精确定位权限,而精确定位跟GPS
又是直接相关的,因此才会导致GPS
权限需要获取。
项目中需要用到Android Ble蓝牙4.0开发技术,于是开启了蓝牙填坑之旅,说实话,蓝牙开发坑真多,跳出一个又进入下一个,每次遇到 问题,就觉得不可能解决了,还好在自己的摸索中,都一一的化解了,以此来记录安卓蓝牙开发的心得。
接手的蓝牙开发项目,原来的同事已经写好,不用再去写,开始也就大概看了看Android蓝牙开发相关资料,对比项目中的蓝牙开发代码,发现发现搜索、连接蓝牙有一样的处理也有不一样的处理,想着项目不一样,代码处理肯定不一样,于是就没去深究,毕竟项目运行起来使用正常,然而,没过几天,客户提出蓝牙搜索太慢,然后就去调试,先看代码是搜索蓝牙8秒钟才显示搜索到的蓝牙设备列表,查看日志,其实不到8秒就搜索到外围设备,只是搜索8秒后停止扫描才显示蓝牙设备,那就把显示成3、4秒呗。
正这样想,同事拿来一部荣耀8,装的APP怎么就是搜不到蓝牙设备,然后调试其他手机,都能搜索到,于是就纳闷了,荣耀8怎么就是搜索不到蓝牙设备呢,其他手机都能,再看一遍代码,也没有专门针对荣耀8处理的地方,再想,荣耀8是系统7.0的手机,是不是跟权限没开有关呢,对比其他华为手机,申请的蓝牙权限也都开着,荣耀8就是搜索不到,是不是荣耀8手机有问题,同事告知下载一个第三方蓝牙调试工具查查看,令人疑惑的是荣耀8用第三方蓝牙调试工具立刻就能扫描显示出来,试了几次都是立刻扫描出来,当时心想,我靠,什么鬼,这么迅速就能把蓝牙设备扫描显示出来,领导也说,别人的调试工具都能做这么快,你也得做这么快,想着目前的代码都是几秒后才能搜索到,目前的荣耀8还搜索不出来,客户那边还一个劲儿的给老板吐槽太慢,于是压力剧增,真正开始了蓝牙开发不归之旅。
但是再考虑索慢时,领导说,得把荣耀8搜索不到的问题先解决,因为荣耀8是项目实施人员给客户安装硬件调试数据要用的,那就先把搜索慢暂时放一放,想着一边搜索慢没还没着落,荣耀8手机就是搜索不到,想想就头大,查找荣耀8为什么搜索不到蓝牙设备的资料,也没发现有效的办法,这时,用荣耀手机的人说,另一个公司的项目app(简称B吧,荣耀8搜索不到蓝牙设备的项目简称A吧)就能正常搜索到蓝牙设备,一打开调试还真是每次都能搜索到,看了一下两个项目的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
private BluetoothAdapter btAdapt; private List<BluetoothDevice> listDevices = new ArrayList<BluetoothDevice>(); private List<TieBean> listDeviceName = new ArrayList<TieBean>(); private boolean bIsConnected; private BluetoothDevice myBluetooth; private public BluetoothGatt mBluetoothGatt; private TextView tv_seacher_ble; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏 setContentView(R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//锁屏 tv_seacher_ble = (LinearLayout) findViewById(R.id.tv_seacher_ble);//linear Layout BluetoothAdapter btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能 IntentFilter intent = new IntentFilter(); intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果 intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); mActivity.registerReceiver(searchDevicesBroadcast, intent); tv_seacher_ble.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { btAdapt.startDiscovery(); } }); } //搜索到的蓝牙设备在这里 private BroadcastReceiver searchDevicesBroadcast = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (TextUtils.isEmpty(device.getName())) { return; } ToastUtil.showToast(mActivity, "蓝牙设备 " + device.getName()); String str = device.getName() + "|" + device.getAddress(); if (listDeviceName.indexOf(str) == -1) {// 防止重复添加 listDevices.add(device); // 获取设备名称和mac地址 listDeviceName.add(new TieBean(str)); } } }; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { //断开设备 if (msg.what == 1) { mBluetoothGatt.disconnect(); //连接设备 } else if (msg.what == 2) { ToastUtil.showToast(mActivity, "开始搜索蓝牙设备"); if (btAdapt.getState() == BluetoothAdapter.STATE_OFF) {// 如果蓝牙还没开启 ToastUtil.showToast(mActivity, "请先打开蓝牙"); return; } if (btAdapt.isDiscovering()) btAdapt.cancelDiscovery(); listDevices.clear(); listDeviceName.clear(); btAdapt.startDiscovery(); //定时器操作 new Handler().postDelayed(new Runnable() { @Override public void run() { //ToastUtil.showToast(mActivity,"搜索蓝牙完毕"); btAdapt.cancelDiscovery(); closeProgressDialog(); //其中BuildBean,TieAdapter是在app的build.gradle中dependencies {compile 'com.dou361.dialogui:jjdxm-dialogui:1.0.3'}; TieAdapter adapter = new TieAdapter(mContext, listDeviceName, true); BuildBean buildBean = DialogUIUtils.showMdBottomSheet(mActivity, true, "", listDeviceName, 0, new DialogUIItemListener() { @Override public void onItemClick(CharSequence text, int position) { myBluetooth = listDevices.get(position); //根据蓝牙地址获取远程蓝牙设备 final BluetoothDevice device = btAdapt.getRemoteDevice(myBluetooth.getAddress()); if (device == null) { return; } mBluetoothGatt = device.connectGatt(mActivity, false, mGattCallback); } }); buildBean.mAdapter = adapter; buildBean.show(); } }, 8000); //延时8s执行 } else if (msg.what == 3) { tv_seacher_ble.setText("断开蓝牙"); } else if (msg.what == 4) { tv_seacher_ble.setText("连接蓝牙"); mBluetoothGatt.close(); } super.handleMessage(msg); } }; |
两个项目中的蓝牙适配器的获取,去发现,动态注册广播、广播接收蓝牙搜索结果、蓝牙连接都一样,至于手机是否支持 ble 4.0,蓝牙是否开启的逻辑,连接上对数据的处理、断开mBluetoothGatt.disconnect(),关闭mBluetoothGatt.close() 的逻辑网上有很多,我这里就不在详说;
经过2个项目对比,发现就蓝牙搜索多久停止不一样,荣耀8能搜索到蓝牙设备的时间项目代码设置是10秒,而搜索不到的项目是设置8秒,带着半信半疑把项目A的蓝牙搜索时间由8秒改成10秒,运行,奇迹还真是出现了,荣耀8可以正常搜索到蓝牙设备了,反复测了好多次都能正常搜索到蓝牙设备,我是不信邪的人,还改成8秒试试,果然还是搜索不到。
于是和领导说,你看荣耀8必须10秒才能搜索 到蓝牙设备,搜索到的蓝牙设备列表不晚点展示不行啊,领导自然 是不同意,拿出第三方蓝牙提哦啊是工具说,那第三方就能很快搜索到,你想想其他法吧,想着有的手机快,有的手机慢,提出意见,能不能搜索到一个设备就展示到列表,不必等10秒后再展示,这样体验好些,反正领导说不行,既然领导说不行,那就回去研究吧。
查了很多蓝牙搜索慢怎么办?看了半天,当然这期间调试过各种其他方法,都没用,功夫不负有心人,其中一个博客说:btAdapt.startDiscovery();搜索返回的结果慢, 现在都用这个 btAdapt.startLeScan(getmLeScanCallback); 其他也没多说什么,喜出望外,这不是正是我要的吗,于是改蓝牙搜索,上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
package com.hand.hand16.activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Vibrator; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.TextView; import com.dou361.dialogui.DialogUIUtils; import com.dou361.dialogui.adapter.TieAdapter; import com.dou361.dialogui.bean.BuildBean; import com.dou361.dialogui.bean.TieBean; import com.dou361.dialogui.listener.DialogUIItemListener; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener { public boolean bIsConnected; public BluetoothGatt mBluetoothGatt;//中央使用和处理数据 public BluetoothGattCharacteristic mNotifyCharacteristic; List<BluetoothDevice> listDevices = new ArrayList<BluetoothDevice>(); List<TieBean> listDeviceName = new ArrayList<TieBean>(); private TextView tv_seacher_ble; private TieAdapter tieAdapter; private Vibrator vibrator; private BluetoothDevice mBluetoothDevice; private BluetoothAdapter btAdapt; private BluetoothDevice myBluetooth; private boolean mScanning; //蓝牙服务 private String strDevice = ""; BluetoothAdapter.LeScanCallback getmLeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice bluetoothDevice, int i, byte[] bytes) { runOnUiThread(new Runnable() { @Override public void run() { boolean isSame = true; if (bluetoothDevice != null && bluetoothDevice.getName() != null) { LogUtil.e("蓝牙设备==" + bluetoothDevice.getName() + "|" + bluetoothDevice.getAddress()); strDevice = bluetoothDevice.getName() + "|" + bluetoothDevice.getAddress(); //去重 for (BluetoothDevice bd : listDevices) { if (bd.getAddress().equalsIgnoreCase(bluetoothDevice.getAddress())) { isSame = false; } } if (isSame) { listDevices.add(bluetoothDevice); listDeviceName.add(new TieBean(strDevice)); // tieAdapter.notifyDataSetChanged(); } } } }); } }; Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { //断开设备 mBluetoothGatt.disconnect(); } else if (msg.what == 2) { //连接设备 if (btAdapt == null || !btAdapt.isEnabled() || btAdapt.getState() == BluetoothAdapter.STATE_OFF) {// 如果蓝牙还没开启 Toast.makeText(com.mobibrw.lego.MainActivity.this, "请先打开蓝牙", Toast.LENGTH_SHORT).show(); Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, 0); return; } if (mScanning) { btAdapt.stopLeScan(getmLeScanCallback); } listDevices.clear(); listDeviceName.clear(); mScanning = true; btAdapt.startLeScan(getmLeScanCallback); //开始搜索 //定时器操作 new Handler().postDelayed(new Runnable() { @Override public void run() { mScanning = false; btAdapt.stopLeScan(getmLeScanCallback); //其中BuildBean,TieAdapter是在build.gradle中dependencies {compile 'com.dou361.dialogui:jjdxm-dialogui:1.0.3'}; tieAdapter = new TieAdapter(mContext, listDeviceName, true); BuildBean buildBean = DialogUIUtils.showMdBottomSheet(mActivity, true, "", listDeviceName, 0, new DialogUIItemListener() { @Override public void onItemClick(CharSequence text, int position) { mBluetoothDevice = btAdapt.getRemoteDevice(listDevices.get(position).getAddress()); if (mBluetoothDevice == null) { return; } mBluetoothGatt = mBluetoothDevice.connectGatt(mActivity, false, mGattCallback); } }); if (listDevices.size() > 0) { buildBean.mAdapter = tieAdapter; buildBean.show(); } else { Toast.makeText(com.mobibrw.lego.MainActivity.this, "未搜索到蓝牙设备", Toast.LENGTH_SHORT).show(); } } }, 2000); //延时2s执行 } else if (msg.what == 3) { tv_seacher_ble.setText("断开蓝牙"); bIsConnected = true; Toast.makeText(com.mobibrw.lego.MainActivity.this, "蓝牙连接成功", Toast.LENGTH_SHORT).show(); } else if (msg.what == 4) { tv_seacher_ble.setText("连接蓝牙"); bIsConnected = false; mBluetoothGatt.close(); mBluetoothGatt = null; } } };); private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, final int status, final int newState) { LogUtil.e("StateChange,status=" + status + ",newState==" + newState + ",Services=" + mBluetoothGatt.discoverServices()); if (status == BluetoothGatt.GATT_SUCCESS) { if (newState == BluetoothProfile.STATE_CONNECTED) {//2 LogUtil.e("去连接GATT server"); } else if (newState == BluetoothProfile.STATE_CONNECTING) { LogUtil.e("连接中"); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { LogUtil.e("已断开断开蓝牙Disconnected"); Message message = new Message(); message.what = 4; handler.sendMessage(message); } else if (newState == BluetoothProfile.STATE_DISCONNECTING) { LogUtil.e("断开中"); } } else if (status == 133) { //防止出现133,搜索到列表连接不上 LogUtil.e("出现133"); Message message = new Message(); message.what = 5; handler.sendMessage(message); } else if (status == 8) { //自动断开 LogUtil.e("自动断开了"); Message message = new Message(); message.what = 6; handler.sendMessage(message); } else if (status == 40) { //距离太远 Message message = new Message(); message.what = 7; handler.sendMessage(message); } else { //其他连接不到设备的情况 } Message message = new Message(); message.what = 8; handler.sendMessage(message); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { LogUtil.e("ServicesDiscovered状态status==" + status + ",Services==" + gatt.getServices()); if (status == BluetoothGatt.GATT_SUCCESS) { Message message = new Message(); message.what = 3; handler.sendMessage(message); //激活要开启的服务 displayGattServices(gatt.getServices()); LogUtil.e("Discovered获取GATT_SUCCESS"); } else { LogUtil.e("onServicesDiscovered received: " + status); bIsConnected = false; } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); supportRequestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题栏 setContentView(com.mobibrw.lego.R.layout.activity_main); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//锁屏 tv_seacher_ble = (TextView) findViewById(com.mobibrw.lego.R.id.tv_main_id); final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); btAdapt = bluetoothManager.getAdapter(); // btAdapt = BluetoothAdapter.getDefaultAdapter();// 初始化本机蓝牙功能,除了搜索方法欢了,获取蓝牙适配器的方法也换了 //添加了震动 vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); tv_seacher_ble.setOnClickListener(new View.OnClickListener() { { vibrator.vibrate(200);//震动指定时间 //蓝牙搜索 Message message = new Message(); message.what = 2; handler.sendMessage(message); } else @Override public void onClick(View v) { final AlertDialog.Builder myDialog = new AlertDialog.Builder(com.mobibrw.lego.MainActivity.this); //nomalDialog.setIcon(R.drawable.icon_dialog); myDialog.setTitle("提示"); myDialog.setMessage("是否断开蓝牙设备?"); myDialog.setPositiveButton("是", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { Message message = new Message(); message.what = 1; handler.sendMessage(message); } }); myDialog.setNegativeButton("否", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { } }); myDialog.show(); } } } //打开服务 private void displayGattServices(List<BluetoothGattService> gattServices) { if (gattServices == null) return; String uuid = null; for (BluetoothGattService gattService : gattServices) { uuid = gattService.getUuid().toString(); List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics(); for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) { uuid = gattCharacteristic.getUuid().toString(); if (uuid.equals(SPP_UUID)) { final BluetoothGattCharacteristic characteristic = gattCharacteristic; final int charaProp = characteristic.getProperties(); //这里根据charaProp 去做读、写、通知,真正的连接成功是在这里做判断的 // Message message = new Message(); //message.what = 3; // handler.sendMessage(message) } } } } @Override public void onDestroy() { super.onDestroy(); if (mScanning) { btAdapt.stopLeScan(getmLeScanCallback); } if (NotNull.isNotNull(mBluetoothGatt)) { mBluetoothGatt.disconnect(); mBluetoothGatt.close(); } android.os.Process.killProcess(android.os.Process.myPid()); } } |
经过这样修改之后,奇迹又出现了,蓝牙搜索到确实快了很多,一秒左右甚至不到一秒就把蓝牙设备搜索出来了, 真是令人喜出望外,想起华为荣耀8那么奇葩,不知这个方法在华为荣耀8上测试如何,然而,2秒,4秒,8秒,10秒,20秒都在华为荣耀8搜索不出来,心又凉了半截,暗想这个方法看来还不适用,又白整了,于是又查各种资料,有的说Android系统6.0以上的手机需要定位权限,低头一想,还真是,一直测试的手机是Android系统6.0以下的,华为荣耀8是7.0以上的,抱着试试的态度,申请权限,还真是努力出奇迹,定位权限允许后,华为荣耀8确实也在不到一秒将蓝牙设备搜索出来,测试了其他6.0以上的手机,都正常很快搜出来。
测试没有其他问题,打包发布,然而客户不给定位权限,问公司怎么一点搜索不出来,告知需要允许定位,才能搜索到,客户觉得一个蓝牙搜索你跟我要定位,很是不理解,老板也认为这样的体验也不好,客户不给权限,就无法使用,非常不友好。告知领导6.0以上必须给定位权限,否则搜不到, 这个没法啊,领导说你看第三方的蓝牙调试工具,在6.0以上的手机,不用打开定位,而且搜索的更快,第三方都能做到,你想办法吧, 我当然不苟同,下载了几个第三方蓝牙调试工具,有的确实要定位权限不允许定位就无法搜索到,有的确实禁止定位权限也能快速搜索到蓝牙设备,这么啪啪打脸,只能埋头去想办法了,同时,为了保证程序兼容性,公司又买了几部6.0以上的安卓手机,让我更惊心的是,魅族6和华为畅享7不仅要允许定位权限,GPS定位还要打开,GPS不打开,也是搜索不到蓝牙设备,想起以前开发的项目,由于安卓碎片化,不同的手机厂商对定位,GPS管理不一样,有的打开定位权限GPS权限也给了,有的打开GPS定位权限也给了,有的是他们两个独立,处理起来很麻烦,而蓝牙有的要GPS,有的不要GPS,6.0一下还不需要任何定位权限,想着这处理起来太麻烦,还是安心去找不要定位权限的方法吧,其实当时心里是没底的,虽然第三方已经实现这种不需要定位权限就能搜索到,心想,第三方也不知用的是什么黑科技,于是就查找Android系统6.0一上的手机不需要定位权限就能把蓝牙设备搜索出来等相关资料,其中最有帮助的查到 btAdapt.startLeScan(getmLeScanCallback) 这个方法已过时,Google已经在api里将其划掉,不推荐使用,推荐使用:
1 2 |
BluetoothLeScanner scanner = btAdapt.getBluetoothLeScanner(); scanner.startScan(scanCallback); |
喜出过望,难道是用的方法过时了,所以需要定位权限,上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); final BluetoothDevice bluetoothDevice = result.getDevice(); runOnUiThread(new Runnable() { @Override public void run() { boolean isSame = true; if (bluetoothDevice != null && bluetoothDevice.getName() != null) { LogUtil.e("蓝牙设备==" + bluetoothDevice.getName() + bluetoothDevice.getAddress()); strDevice =bluetoothDevice.getName() + "|" + bluetoothDevice.getAddress(); for (BluetoothDevice bd : listDevices) { if (bd.getAddress().equalsIgnoreCase(bluetoothDevice.getAddress())) { isSame = false; } } if (isSame) { listDevices.add(bluetoothDevice); listDeviceName.add(new TieBean(strDevice)); if (bleDeviceAdapter != null) { bleDeviceAdapter.notifyDataSetChanged(); } } } } }); } @Override public void onBatchScanResults(final List<ScanResult> results) { super.onBatchScanResults(results); runOnUiThread(new Runnable() { @Override public void run() { LogUtil.d("扫描onBatchScanResults==" + results.size()); } }); } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); LogUtil.e("扫描失败onScanFailed, errorCode==" + errorCode); } }; |
停止扫描的方法是:
1 |
scanner.stopScan(scanCallback); |
然而,虽然使用了新方法,速度也是一样快,但是还是需要定位权限,魅族6和华为畅享7还得打开GPS,看来这个也不是正解,当然项目中的扫描外围蓝牙设备的方法也都换成了scanner.startScan(scanCallback);
第一天毫无结果,灰头土脸,昏天暗地,焦头烂额地回家。
第二天继续查找相关资料,在一个偶然的机会,看到一篇博客说,把app中的build.gradle中的:
1 2 3 4 5 6 7 8 |
defaultConfig { applicationId "com.demo.ble" minSdkVersion 18 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } |
中的 targetSdkVersion 25改成 targetSdkVersion 22,就不需要定位权限也可以搜索到蓝牙设备了,看到网上很少有人这样说的,搜了那么多资料,就一篇这样说的,带着半信半疑将targetSdkVersion改成 22, 编译,也没报任何错,运行,还真是又出奇迹,测试了手上的所有手机,都不需要定位,不要GPS就可以搜索到蓝牙设备。
至于为什么会这样,可以查找 Android compileSdkVersion、buildToolsVersion,targetSdkVersion之间的区别,网上有很多解释,保证能解答你的疑惑,下面附上一些简单的解释
targetSDKVersion
简单来说就代表着你的App能够适配的系统版本,意味着你的App在这个版本的手机上做了充分的 前向 兼容性处理和实际测试。其实我们写代码时都是经常干这么一件事,就是 if(Build.VERSION.SDK_INT >= 23) { ... } ,这就是兼容性处理最典型的一个例子。如果你的target设置得越高,其实调用系统提供的API时,所得到的处理也是不一样的,甚至有些新的API是只有新的系统才有的Android6.0普通权限normal permission 和 危险权限dangerous permission 。
Normal Permission:写在xml文件里,那么App安装时就会默认获得这些权限,即使是在Android6.0系统的手机上,用户也无法在安装后动态取消这些normal权限,这和以前的权限系统是一样的,不变。
Dangerous Permission:还是得写在xml文件里,但是App安装时具体如果执行授权分以下几种情况:
1、targetSDKVersion < 23 & API(手机系统) < 6.0 :安装时默认获得权限,且用户无法在安装App之后取消权限。
2、targetSDKVersion < 23 & API(手机系统) >= 6.0 :安装时默认获得权限,但是用户可以在安装App完成后动态取消授权( 取消时手机会弹出提醒,告诉用户这个是为旧版手机打造的应用,让用户谨慎操作 )。
3、targetSDKVersion >= 23 & API(手机系统) < 6.0 :安装时默认获得权限,且用户无法在安装App之后取消权限。
4、targetSDKVersion >= 23 & API(手机系统) >= 6.0 :安装时不会获得权限,可以在运行时向用户申请权限。用户授权以后仍然可以在设置界面中取消授权,用户主动在设置界面取消后,在app运行过程中可能会出现crash。
其他:
1.虽然解决了大问题,但是目前项目中发现,连续扫描-断开-扫描四五次,就无法再搜索到外围蓝牙设备,尤其是在每次搜索1-2秒内扫描断开,4、5次无论如何也是搜索不到,需要重新进入界面才能扫描到,每次扫描花费在4、5秒以上的,出现4、5次扫不到的几率就很低了,当然,4、5秒是老板和客户无法忍受的。
2.蓝牙连接成功自动断开;