我在Android中有一个奇怪的错误。我正在构建一个可以连接到BLE设备的应用程序。对于我的测试,我使用了我的三星J3和Android 5.1.1,一切都还可以。
现在我尝试使用Android 6.0的Android设备安装我的应用程序,但我无法扫描BLE设备。
这是我的Gradle文件:
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion '26.0.2'
defaultConfig {
applicationId "com.devicesensor"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support:design:25.2.0'
testCompile 'junit:junit:4.12'
compile 'com.android.support:cardview-v7:25.2.0'
}
这是我的扫描活动类:
public class DeviceScanActivity extends ListActivity{
private LeDeviceListAdapter mLeDeviceListAdapter;
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
private static final int REQUEST_ENABLE_BT = 1;
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
BluetoothDevice bDevice = mLeDeviceListAdapter.getDevice(position);
Intent intent = new Intent();
intent.putExtra("deviceSelected", bDevice);
setResult(RESULT_OK, intent);
finish();
}
});
// getActionBar().setTitle("TITOLO");
mHandler = new Handler();
// Use this check to determine whether BLE is supported on the device. Then you can
// selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
// Initializes a Bluetooth adapter. For API level 18 and above, get a reference to
// BluetoothAdapter through BluetoothManager.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// Checks if Bluetooth is supported on the device.
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
finish();
return;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_bleactivity, menu);
if (!mScanning) {
menu.findItem(R.id.menu_stop).setVisible(false);
menu.findItem(R.id.menu_scan).setVisible(true);
menu.findItem(R.id.menu_refresh).setActionView(null);
} else {
menu.findItem(R.id.menu_stop).setVisible(true);
menu.findItem(R.id.menu_scan).setVisible(false);
menu.findItem(R.id.menu_refresh).setActionView(
R.layout.actionbar_indeterminate_progress);
}
return true;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_scan:
mLeDeviceListAdapter.clear();
scanLeDevice(true);
break;
case R.id.menu_stop:
scanLeDevice(false);
break;
}
return true;
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onResume() {
super.onResume();
// Ensures Bluetooth is enabled on the device. If Bluetooth is not currently enabled,
// fire an intent to display a dialog asking the user to grant permission to enable it.
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
// Initializes list view adapter.
mLeDeviceListAdapter = new LeDeviceListAdapter();
setListAdapter(mLeDeviceListAdapter);
scanLeDevice(true);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// User chose not to enable Bluetooth.
if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
finish();
return;
}
super.onActivityResult(requestCode, resultCode, data);
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onPause() {
super.onPause();
scanLeDevice(false);
mLeDeviceListAdapter.clear();
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
// Adapter for holding devices found through scanning.
private class LeDeviceListAdapter extends BaseAdapter {
private ArrayList<BluetoothDevice> mLeDevices;
private LayoutInflater mInflator;
public LeDeviceListAdapter() {
super();
mLeDevices = new ArrayList<BluetoothDevice>();
mInflator = DeviceScanActivity.this.getLayoutInflater();
}
public void addDevice(BluetoothDevice device) {
if (!mLeDevices.contains(device)) {
mLeDevices.add(device);
}
}
public BluetoothDevice getDevice(int position) {
return mLeDevices.get(position);
}
public void clear() {
mLeDevices.clear();
}
@Override
public int getCount() {
return mLeDevices.size();
}
@Override
public Object getItem(int i) {
return mLeDevices.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
// General ListView optimization code.
if (view == null) {
view = mInflator.inflate(R.layout.listitem_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
view.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) view.getTag();
}
BluetoothDevice device = mLeDevices.get(i);
final String deviceName = device.getName();
if (deviceName != null && deviceName.length() > 0)
viewHolder.deviceName.setText(deviceName);
else
viewHolder.deviceName.setText(R.string.unknown_device);
viewHolder.deviceAddress.setText(device.getAddress());
return view;
}
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
static class ViewHolder {
TextView deviceName;
TextView deviceAddress;
}
如果您的目标是API级别23+并且在使用Android 6.0+的设备上运行,则必须在运行时询问用户权限。要扫描蓝牙设备,您需要ACCESS_COARSE_LOCATION / ACCESS_FINE_LOCATION。
你可以在Android Developers Site上找到如何做到这一点。
您可以使用BluetoothLeScanner
从API级别发现蓝牙LE设备:21。它可以使用ScanFilter
扫描特定类型的蓝牙LE设备。它还可以请求不同类型的回调来传递结果。
使用getBluetoothLeScanner()
获取BluetoothLeScanner
的实例。
注意:此处的大多数扫描方法都需要BLUETOOTH_ADMIN
权限。
您可以查看this示例,以便即时实现它。
此外,请确保在开始扫描之前获得以下权限,显然您必须从Android 6.0启用位置服务。
<manifest ... >
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
...
</manifest>
您可以使用新的SettingsClient
API打开位置服务。
如果您要声明您的应用仅适用于支持BLE的设备,请在应用清单中包含以下内容:
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
BLE Scanner应用程序可用于列出范围内的可用BLE设备以进行调试。