一、权限
<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!-- 精确定位权限,在Android6.0及以上使用,蓝牙扫描需要用到这个的动态权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- 模糊定位权限,在Android6.0及以上使用,蓝牙扫描需要用到这个的动态权限 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
二、获取BluetoothAdapter实例
BLe蓝牙获取BluetoothAdapter实例不同与经典蓝牙一样,首先得通过系统服务getSystemService(BLUETOOTH_SERVICE)获取BluetoothManager的实例,然后再通过BluetoothManager的getAdapter方法获取BluetoothAdapter实例。
代码:
private BluetoothAdapter mBluetoothAdapter;
private BluetoothManager mBluetoothManager;
mBluetoothManager=(BluetoothManager)getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter=mBluetoothManager.getAdapter();
三、判断设备是否支持BLE蓝牙并使能蓝牙打开
mBluetoothManager.getAdapter()返回null值就是设备不支持BLE蓝牙,mBluetoothAdapter.isEnable()方法返回true代表蓝牙已打开,返回false代表关闭。通过Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)可以拉起打开蓝牙的弹窗
if(mBluetoothAdapter!=null){
if(!mBluetoothAdapter.isEnabled()){
Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,1);
}
}
四、自定义BLE蓝牙扫描结果的Adapter
package com.example.bledemo.adapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.bledemo.R;
import java.util.List;
public class BleAdapter extends BaseAdapter {
private List<BluetoothDevice> mDeviceList;
private Context mContext;
private LayoutInflater mInflater;
public BleAdapter(List<BluetoothDevice> deviceList,Context context){
this.mDeviceList=deviceList;
this.mContext=context;
this.mInflater=LayoutInflater.from(this.mContext);
}
@Override
public int getCount() {
return mDeviceList.size();
}
@Override
public Object getItem(int position) {
return mDeviceList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHandler viewHandler;
if(convertView==null){
viewHandler=new ViewHandler();
convertView=mInflater.inflate(R.layout.list_ble_device,null);
viewHandler.textView=convertView.findViewById(R.id.ble_device);
convertView.setTag(viewHandler);
}else{
viewHandler=(ViewHandler)convertView.getTag();
}
viewHandler.textView.setText(mDeviceList.get(position).getName());
return convertView;
}
private class ViewHandler{
TextView textView;
}
}
五、编写蓝牙扫描到设备的回调
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private BluetoothAdapter.LeScanCallback leScanCallback=new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
if(!mDeviceList.contains(device)){
mDeviceList.add(device);
mBleAdapter.notifyDataSetChanged();
}
}
};
六、开启扫描
1、先实现动态权限的获取
String[] permissions={Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION};
int checkSum=0;
for(String permission:permissions){
checkSum+=checkSelfPermission(permission);
}
if(checkSum!=0){
requestPermissions(permissions,1);
}
2、清理数据并开启扫描
mDeviceList.clear();
mBleAdapter.notifyDataSetChanged();
new Thread(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.startLeScan(leScanCallback);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBluetoothAdapter.stopLeScan(leScanCallback);
}
}).start();
七、完整代码
1、AndroidManifest.XML
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bledemo">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BleDemo">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2、activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="扫描"
android:layout_gravity="center"
android:onClick="onScanBluetooth"/>
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/ble_list"/>
</LinearLayout>
3、list_ble_device.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<View
android:layout_width="30dp"
android:layout_height="0dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/ble_device"/>
</LinearLayout>
4、MainActivity.java
package com.example.bledemo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import com.example.bledemo.adapter.BleAdapter;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private BluetoothAdapter mBluetoothAdapter;
private BluetoothManager mBluetoothManager;
private List<BluetoothDevice> mDeviceList=new ArrayList<>();
private ListView mListView;
private BleAdapter mBleAdapter;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBluetoothManager=(BluetoothManager)getSystemService(BLUETOOTH_SERVICE);
mBluetoothAdapter=mBluetoothManager.getAdapter();
if(mBluetoothAdapter!=null){
if(!mBluetoothAdapter.isEnabled()){
Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent,1);
}
}
mListView=findViewById(R.id.ble_list);
mBleAdapter=new BleAdapter(mDeviceList,this);
mListView.setAdapter(mBleAdapter);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable @org.jetbrains.annotations.Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void onScanBluetooth(View view) {
if(mBluetoothAdapter!=null&&mBluetoothAdapter.isEnabled()){
String[] permissions={Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION};
int checkSum=0;
for(String permission:permissions){
checkSum+=checkSelfPermission(permission);
}
if(checkSum!=0){
requestPermissions(permissions,1);
}
mDeviceList.clear();
mBleAdapter.notifyDataSetChanged();
new Thread(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.startLeScan(leScanCallback);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mBluetoothAdapter.stopLeScan(leScanCallback);
}
}).start();
}
}
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
private BluetoothAdapter.LeScanCallback leScanCallback=new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
if(!mDeviceList.contains(device)){
mDeviceList.add(device);
mBleAdapter.notifyDataSetChanged();
}
}
};
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
5、BleAdapter.java
package com.example.bledemo.adapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.example.bledemo.R;
import java.util.List;
public class BleAdapter extends BaseAdapter {
private List<BluetoothDevice> mDeviceList;
private Context mContext;
private LayoutInflater mInflater;
public BleAdapter(List<BluetoothDevice> deviceList,Context context){
this.mDeviceList=deviceList;
this.mContext=context;
this.mInflater=LayoutInflater.from(this.mContext);
}
@Override
public int getCount() {
return mDeviceList.size();
}
@Override
public Object getItem(int position) {
return mDeviceList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHandler viewHandler;
if(convertView==null){
viewHandler=new ViewHandler();
convertView=mInflater.inflate(R.layout.list_ble_device,null);
viewHandler.textView=convertView.findViewById(R.id.ble_device);
convertView.setTag(viewHandler);
}else{
viewHandler=(ViewHandler)convertView.getTag();
}
viewHandler.textView.setText(mDeviceList.get(position).getName());
return convertView;
}
private class ViewHandler{
TextView textView;
}
}