1.蓝牙的基本操作
蓝牙权限
android.permission.BLUETOOTH //允许程序连接到已配对的蓝牙设备,请求连接/接收连接/传输数据需要改权限, 主要用于对配对后进行操作. android.permission.BLUETOOTH_ADMIN //允许程序发现和配对蓝牙设备, 该权限用来管理蓝牙设备, 有了这个权限, 应用才能使用本机的蓝牙设备.
BluetoothAdapter
BluetoothAdapter代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作
//获取蓝牙适配器对象. BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
开关蓝牙(推荐请求用户打开)
//直接打开 mBluetoothAdapter.enable(); //请求用户打开蓝牙 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivity(intent); //关闭蓝牙 mBluetoothAdapter.disable();
蓝牙是否可用
//蓝牙是否可用,返回boolean值 true可用 false不可用 mBluetoothAdapter.isEnabled();
蓝牙可被发现及时间设置
//可被发现 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600); //默认120秒 startActivity(intent);
搜索蓝牙
蓝牙搜索在android6.0以后需要加上一个模糊定位的权限,并动态申请,虽然有些手机不加权限也能搜索到蓝牙设备,
但是大多会出现蓝牙一直等待搜索,蓝牙列表无法弹出。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
//扫描蓝牙的方法,返回boolean值 //开始扫描 mBluetoothAdapter.startDiscovery(); //是否正在扫描 mBluetoothAdapter.isDiscovering(); //停止扫描 mBluetoothAdapter.cancelDiscovery();
startDiscovery()是一个异步方法,在搜索蓝牙设备的过程中,系统可能会发送以下三个广播:
ACTION_DISCOVERY_START(开始搜索)
ACTION_DISCOVERY_FINISHED(搜索结束)
ACTION_FOUND(找到设备)。
蓝牙地址
//获取本地蓝牙地址 返回String mBluetoothAdapter.getAddress(); //检查蓝牙地址 蓝牙地址字母必须大写, 例如 : "00:43:A8:23:10:F0"; mBluetoothAdapter.checkBluetoothAddress(String address);
获取远程蓝牙设备
BluetoothDevice 代表了一个远程的蓝牙设备, 通过这个类可以查询远程设备的物理地址, 名称, 连接状态等信息;
//作用 : 根据蓝牙的物理地址获取远程的蓝牙设备, 如果地址不合法, 就会产生异常; //返回值 : 获取到的BluetoothDevice对象; public BluetoothDevice getRemoteDevice(String address);
蓝牙名字
//设置名字 mBluetoothAdapter.setName(); //得到名字 mBluetoothAdapter.getName();
创建监听
//创建一个监听Rfcommon端口的蓝牙监听, 使用accept()方法监听, 并获取BluetoothSocket对象; // 该系统会根据一个服务名称(name)和唯一的识别码(uuid)来创建一个SDP服务, // 远程蓝牙设备可以根据唯一的UUID来连接这个SDP服务器; public BluetoothServerSocket listenUsingRfcommonWithServiceRecord(String name, UUID uuid); // 参数 : name : SDP服务器名称, UUID, SDP记录下的UUID; // 返回值 : 正在监听蓝牙端口;
UUID
在蓝牙中,每个服务和服务属性都唯一地由全局唯一标识符,Universally Unique Identifier(UUID)来校验。UUID相当于Socket的端口,
而蓝牙地址相当于Socket的IP。两个蓝牙设备进行连接时需要使用同一个UUID, 这是一个服务的唯一标识。
于普通蓝牙适配器和android手机蓝牙模块连接的UUID
//手机连接的UUID //设备连接的UUID由厂商决定。 UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
其他UUID可看http://blog.csdn.net/spmno/article/details/6931941
蓝牙连接
蓝牙客户端 BluetoothSocket,蓝牙服务端 BluetoothServerSocket 匹配成功然后使用connect()方法连接
2.具体代码(扫描蓝牙)没有其他蓝牙设备,使用两部手机替代。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:onClick="startBluetooth" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="开启蓝牙"/> <Button android:onClick="searchBluetooth" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="搜索蓝牙"/> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
两个按钮,一个ListView显示蓝牙设备名字
初始画数据,设置适配器
ListView mListView; BluetoothAdapter mBluetoothAdapter; ArrayAdapter<String> mAdapter; //存储蓝牙设备名字 List<String> deviceNames = new ArrayList<>(); //存储蓝牙设备 List<BluetoothDevice> devices = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); mListView = (ListView) findViewById(R.id.listview); mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, deviceNames); mListView.setAdapter(mAdapter); }
开启蓝牙
//按钮点击事件 开启蓝牙 public void startBluetooth(View view) { //设置蓝牙可被发现 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); //设置可被发现的时间 1200秒 intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,1200); startActivity(intent); }
搜索蓝牙 需要动态权限
//搜索蓝牙 public void searchBluetooth(View view) { //蓝牙开启状态 if (mBluetoothAdapter.isEnabled()) { //动态权限 if (Build.VERSION.SDK_INT > 23) { int check = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION); if (check == PackageManager.PERMISSION_GRANTED) { //开始扫描 startSearch(); } else { requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1); } } else { startSearch(); } startSearch(); } else { //如果蓝牙没开启 startBluetooth(view); } } //开始搜索蓝牙 private void startSearch() { //如果没在搜索 if (!mBluetoothAdapter.isDiscovering()) { //搜索蓝牙设备 mBluetoothAdapter.startDiscovery(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode ==1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startSearch(); } else { Toast.makeText(this, "必须获取地理位置才能扫描蓝牙设备", Toast.LENGTH_SHORT).show(); } }
记得在AndroidManifest文件中在加上模糊地理位置权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
扫描蓝牙时,系统会发送广播,通过广播接收者获得扫描到的蓝牙设备
//广播接收者 private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { //如果发现蓝牙设备 if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) { //BluetoothDevice 代表了一个远程的蓝牙设备, 通过这个类可以查询远程设备的状态信息; BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); //取得蓝牙设备的名字(名字可能为空,空则设置名字为匿名) 以便显示到ListView String name = device.getName() == null ? "匿名" : device.getName(); deviceNames.add(name); mAdapter.notifyDataSetChanged(); devices.add(device); } } };
注册广播接收者
@Override protected void onResume() { super.onResume(); //注册 registerReceiver(receiver, new IntentFilter(BluetoothDevice.ACTION_FOUND)); } @Override protected void onPause() { super.onPause(); //解绑 unregisterReceiver(receiver); }
暂时效果
3.具体代码(蓝牙连接)
两个蓝牙手机链接,需要有服务器客户端
UUID
//手机连接的UUID //设备连接的UUID由厂商决定。 UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
服务端
class BluetoothServer extends Thread { @Override public void run() { super.run(); //服务 BluetoothServerSocket bluetoothServerSocket; try { bluetoothServerSocket = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord( mBluetoothAdapter.getName(),uuid); BluetoothSocket socket = bluetoothServerSocket.accept(); while (socket.isConnected()) { InputStream inputStream = socket.getInputStream(); int len = 0; byte[] buffer = new byte[1024]; while ((len = inputStream.read(buffer)) != -1) { Log.e("TAG",new String(buffer,0,len,"utf-8")); } } //将服务端关闭,不可能1对多,所以,只要获取了客户端,那么服务端就可以关闭了 bluetoothServerSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
然后在开启蓝牙后开启服务,即在searchBluetooh中调用startServer方法
BluetoothServer server; public void startServer() { if (server == null) { server = new BluetoothServer(); server.start(); } }
客户端
MainActivity 实现OnItemClickListener 然后重写方法
mListView.setOnItemClickListener(this);
//客户端 搜索出蓝牙后,ListView Item点击事件 链接服务端 @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //点击列表,去请求服务器 final BluetoothDevice device = devices.get(position); new Thread() { @Override public void run() { super.run(); try { BluetoothSocket socket = device.createInsecureRfcommSocketToServiceRecord(uuid); //链接 socket.connect(); OutputStream os = socket.getOutputStream(); os.write("连接蓝牙服务器成功".getBytes()); os.flush(); os.close(); } catch (IOException e) { e.printStackTrace(); } } }.start(); }
以上代码都写在一个工程中,一步手机既可以做客户端,又可以做服务端。