当前位置:   article > 正文

Android Bluetooth 蓝牙使用详情_startdiscovery

startdiscovery

通常情况下,我们对蓝牙的操作主要有:开启和关闭蓝牙、搜索周边设备、能被周边设备所发现、获取配对设备、蓝牙设备间的数据传输

1、打开蓝牙(当然首先要确保你的手机是有蓝牙设备的)

  蓝牙设备主要分为两部分,一部分为本地设备,另一部分为远程设备。

  • BluetoothAdapter——本地设备,对蓝牙操作首先就需要有一个BluetoothAdapter实例。常用的几个方法如下:
    •   cancelDiscovery()——取消本地蓝牙设备的搜索操作,如果本地设备正在进行搜索,那么调用该方法后将停止搜索操作。
    •   Disable()——关闭蓝牙设备。
    •   Enable()——打开蓝牙设备。相信大家都有过打开蓝牙的经历,一般情况下都会弹出一个窗口,说正在请求打开蓝牙设备,你是不是允许云云。
    •   getAddress()——获取蓝牙设备的MAC地址。
    •   GetDefaultAdapter()——获取本地的蓝牙设备
    •   getName()——获取本地蓝牙的名称
    •   getRemoteDevice(String address)——根据远程设备的MAC地址来获取远程设备
    •   startDiscovery()——蓝牙设备开始搜索周边设备
  • BuletoothDevice——远程设备。

    它所包含的方法和BluetoothAdapter一样,不再累述。

  1. // 获取本地的蓝牙适配器实例
  2. BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
  3. if(adapter!=null)
  4. {
  5. if(!adapter.isEnabled())
  6. {
  7. //通过这个方法来请求打开我们的蓝牙设备
  8. Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  9. startActivity(intent);
  10. }
  11. }
  12. else
  13. {
  14. System.out.println("本地设备驱动异常!");
  15. }

2、搜索周边设备

  对于Android查找发现蓝牙设备使用BluetoothAdapter类的startDiscovery()方法就可以执行一个异步方式获取周边的蓝牙设备,因为是一个异步的方法所以我们不需要考虑线程被阻塞问题,整个过程大约需要12秒时间,这时我们可以注册一个 BroadcastReceiver 对象来接收查找到的蓝牙设备信息,我们通过Filter来过滤ACTION_FOUND这个 Intent动作以获取每个远程设备的详细信息,通过Intent字段EXTRA_DEVICE 和 EXTRA_CLASS可以获得包含了每个BluetoothDevice 对象和对象的该设备类型 BluetoothClass。

  实现一个自己的BroadCastReceiver类,并注册这个类。

  1. private class BluetoothReciever extends BroadcastReceiver {
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. // TODO Auto-generated method stub
  5. String action = intent.getAction();
  6. if (BluetoothDevice.ACTION_FOUND.equals(action)) {
  7. BluetoothDevice device = intent
  8. .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  9. System.out.println(device.getAddress());
  10. }
  11. }
  12. }
  1. IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  2. bluetoothReceive = new BluetoothReciever();
  3. registerReceiver(bluetoothReceive, intentFilter);

因为在注册一个Receiver后,程序并不知道该何时去回收它,所以需要我们自己重写Activity类的onDestroy()方法。

  1. @Override
  2. protected void onDestroy() {
  3. // TODO Auto-generated method stub
  4. unregisterReceiver(bluetoothReceive);
  5. super.onDestroy();
  6. }

3、被周边设备所发现  

  如果需要用户确认操作,不需要获取底层蓝牙服务实例,可以通过一个Intent来传递ACTION_REQUEST_DISCOVERABLE参数, 这里通过startActivity来请求开启。

  1. Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
  2. //50这个参数代表的是蓝牙设备能在多少秒内被发现
  3. discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 50);
  4. startActivity(discoverableIntent);

4、配对

  配对操作呢,一般都是发现设备后,由我们人工来进行选择后系统自动去配对。我们可以通过下面的方法来获得配对的设备:

  1. //通过getBondedDevices方法来获取已经与本设备配对的设备
  2. Set<BluetoothDevice> device= adapter.getBondedDevices();
  3. if(device.size()>0)
  4. {
  5. for(Iterator iterator=device.iterator();iterator.hasNext();)
  6. {
  7. BluetoothDevice bluetoothDevice=(BluetoothDevice)iterator.next();
  8. System.out.println(bluetoothDevice.getAddress());
  9. }
  10. }

 5、数据交换  

  在看过前面的启动、发现/搜索、配对这些操作后,下面来说说数据传输的问题。

  在Android系统中,蓝牙设备间的数据传输问题和我们在PC上的网络编程颇为类似,有一端作为Server端监听Client端的连接请求,在二者建立了连接后,就可以使用普通的数据传输方式进行数据交换操作了。在这个过程中,我需要使用到BluetoothServerSocket和BluetoothSocket两个类来建立Server端和Client端,还需要使用到一些关于流(Stream)的知识。

  • BluetoothServerSocket——服务端(监听端、监听器、接受请求的一端)
    • Accept()——阻塞宿主线程,直至收到客户端请求。返回BluetoothSocket对象。由于这个
    • Accept(int timeout)——阻塞宿主线程,直至收到客户端请求或等待时间超过timeout。返回BluetoothSocket对象。
    • Close()——关闭BluetoothServerSocket监听器。

  可以看到,Accept方法是一个阻塞方法,所以在进行开发的时候,一般都需要用到多线程的知识。JAVA的多线程知识,可以在JAVA的JDK帮助文档中查看,就单纯的应用来说还是比较简单的。

  • BluetoothSocket——客户端(请求端)
    • Close()——关闭BluetoothSocket请求端。
    • Connect()——主动向服务端(监听端)发起连接请求。

  在了解了这两个类后,可以着手来建立我们自己的Server端和Client端了。

  如果一个设备需要和两个或多个设备连接时,就需要作为一个server来传输,服务器端套接字在接受(accepted) 一个客户发来的BluetoothSocket连接请求时作出相应的响应。服务器socket将监听进入的连接请求,一旦连接被接受,将产生一个BluetoothSocket。

  • 创建一个Server

  使用BluetoothAdapter类的listenUsingRfcommWithServiceRecord方法来新建一个ServerSocket。在listenUsingRfcommWithServiceRecord中有一个参数叫做UUID,UUID(Universally Unique Identifier)是一个128位的字符串ID,被用于唯一标识我们的蓝牙服务。你可以使用web上的任何一款UUID产生器为你的程序获取一个UUID,然后使用fromString(String)初始化一个UUID。

  使用ServerSocket实例的accept方法进行监听,当监听到带有我们初始化的UUID参数的连接请求后作出响应,连接成功后返回一个BluetoothSocket对象。连接完成后,调用close方法关闭该Socket监听。

  1. // Bluetooth的ServerSocket包装类
  2. class BluetoothServer {
  3. public BluetoothServer() throws IOException {
  4. }
  5. // 要建立一个ServerSocket对象,需要使用adapter.listenUsingRfcommWithServiceRecord方法
  6. // UUID可以在网上去申请
  7. private BluetoothServerSocket serverSocket = adapter.listenUsingRfcommWithServiceRecord("myServerSocket",
  8. UUID.fromString("84D1319C-FBAF-644C-901A-8F091F25AF04"));
  9. BluetoothSocket socket = serverSocket.accept();
  10. void m() throws IOException {
  11. if (socket != null) {
  12. InputStream inputStream = socket.getInputStream();
  13. int read = -1;
  14. final byte[] bytes = new byte[1024];
  15. for (; (read = inputStream.read(bytes)) > -1;) {
  16. final int count = read;
  17. Thread _start = new Thread(new Runnable() {
  18. @Override
  19. public void run() {
  20. // TODO Auto-generated method stub
  21. StringBuilder sb = new StringBuilder();
  22. for (int i = 0; i < count; i++) {
  23. if (i > 0) {
  24. sb.append(' ');
  25. }
  26. String _s = Integer.toHexString(bytes[i] & 0xFF);
  27. if (_s.length() < 2) {
  28. sb.append('0');
  29. }
  30. sb.append(_s);
  31. }
  32. System.out.println(sb.toString());
  33. }
  34. });
  35. _start.start();
  36. }
  37. }
  38. }
  39. }
  • 创建一个Client

  创建一个Client端,首先需要我们使用BluetoothDevice的实例的createRfcommSocketToServiceRecord方法来创建一个BluetoothSocket实例。在创建的时候,需要给createRfcommSocketToServiceRecord方法传入我们服务端的UUID值。然后使用BluetoothSocket实例的Connect方法对Server端进行连接请求,当连接成功后,Client端和Server端的传输通道就被打开。最后在连接完成后使用该实例的close方法来关闭这个连接。

  1. class BluetoothClient {
  2. BluetoothDevice device = null;
  3. //通过构造函数来传入一个BluetoothDevice实例
  4. public BluetoothClient(BluetoothDevice device) {
  5. this.device = device;
  6. }
  7. BluetoothSocket socket = null;
  8. void connetServer() throws IOException {
  9. Thread _clientThread = new Thread(new Runnable() {
  10. public void run() {
  11. try {
  12. //通过BluetoothDevice实例的createRfcommSocketToServiceRecord方法可以返回一个带有UUID的BluetoothSocket实例
  13. socket = device.createRfcommSocketToServiceRecord(UUID.fromString("84D1319C-FBAF-644C-901A-8F091F25AF04"));
  14. } catch (IOException e1) {
  15. // TODO Auto-generated catch block
  16. e1.printStackTrace();
  17. }
  18. try {
  19. socket.connect();
  20. } catch (IOException e1) {
  21. // TODO Auto-generated catch block
  22. e1.printStackTrace();
  23. }
  24. if (socket != null) {
  25. try {
  26. socket.close();
  27. } catch (Exception e) {
  28. // TODO: handle exception
  29. }
  30. }
  31. }
  32. });
  33. _clientThread.start();
  34. }
  35. }

 getInputStream()——获得一个可读的流,该流在连接不成功的情况下依旧可以获得,但是对其操作的话就会报IOException的异常。需要从外部获取的数据都从该流中获取。

  getOutputStrem()——获得一个可写的流,该流在连接不成功的情况下依旧可以获得,但是对其操作的话就会报IOException的异常。需要往外部传输的数据都可以写到该流中传输出去。

  数据传输的大致流程如下:

    • 首先,分别通过getInputStream()和getOutputStream()获得管理数据传输的InputStream和OutputStream。
    • 然后,开辟一个线程专门用于数据的读或写。这是非常重要的,因为read(byte[])和write(byte[])方法都是阻塞调用。read(byte[])从输入流(InputStream)中读取数据。write(byte[])将数据写入到OutputStream流中去,这个方法一般不会阻塞,但当远程设备的中间缓冲区已满而对方没有及时地调用read(byte[])时将会一直阻塞。所以,新开辟的线程中的主循环将一直用于从InputStream中读取数据。

  还要补充一点,由于蓝牙设备是系统设备,所以需要有相应的权限支持。在AndroidManifest.xml文件中添加上权限。

  1. <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
  2. <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>

源码相关

常用类说明

BluetoothAdapter

首先调用静态方法getDefaultAdapter()获取蓝牙适配器bluetoothadapter, 如果返回为空,则表明此设备不支持蓝牙。

代表本地蓝牙适配器。BluetoothAdapter 让你进行基础的蓝牙操作,比如初始化搜索设备,对已配对设备进行检索,根据一直MAC地址 实例化一个 BluetoothDevice,建立一个监听其他设备连接请求的 BluetoothServerSocket,启动对蓝牙LE设备的搜索。

这是使用蓝牙的起点。获得了本地适配器后,可以调用getBondedDevices()获得代表配对设备的 BluetoothDevice 对象 startDiscovery()启动搜索蓝牙设备。或者建立一个BluetoothServerSocket , 调用listenUsingRfcommWithServiceRecord(String, UUID)来监听接入请求 startLeScan(LeScanCallback) 搜索Bluetooth LE 设备

相应的源码 BluetoothAdapter.java (frameworks/base/core/java/android/bluetooth)

  1. /**
  2. * Get a handle to the default local Bluetooth adapter.
  3. * <p>Currently Android only supports one Bluetooth adapter, but the API
  4. * could be extended to support more. This will always return the default
  5. * adapter.
  6. * @return the default local adapter, or null if Bluetooth is not supported
  7. * on this hardware platform
  8. */
  9. public static synchronized BluetoothAdapter getDefaultAdapter() {
  10. if (sAdapter == null) {
  11. IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
  12. if (b != null) {
  13. IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
  14. sAdapter = new BluetoothAdapter(managerService);
  15. } else {
  16. Log.e(TAG, "Bluetooth binder is null");
  17. }
  18. }
  19. return sAdapter;
  20. }

BluetoothSocket

一个连接上或连接中的socket 使用BluetoothServerSocket来创建一个监听的服务socket。 对于服务端,当BluetoothServerSocket接收了一个连接,会返回一个新的BluetoothSocket来管理这个连接。 对于客户端,使用一个单独的BluetoothSocket来初始化一个发送连接并管理这个连接。 蓝牙socket最普通的模式是RFCOMM,这是Android API支持的模式。 RFCOMM面向连接,使用流传输。也称为Serial Port Profile (SPP)

建立一个到已知蓝牙设备的BluetoothSocket,使用BluetoothDevice.createRfcommSocketToServiceRecord() 然后调用connect()来连接这个远程设备。调用这个方法会阻塞程序直到建立连接或者连接失败。 一旦socket连接上,不论初始化为客户端或者服务端,调用getInputStream()和getOutputStream()打开IO流来分别接收 输入流和输出流对象。流对象自动连接到socket

BluetoothSocket是线程安全的。特别的是,close()会立刻关闭进行中的操作并关闭socket。

需要 BLUETOOTH 相关权限

BluetoothServerSocket

一个监听的蓝牙socket 在服务端,使用BluetoothServerSocket来建立一个监听的服务socket。

使用BluetoothAdapter.listenUsingRfcommWithServiceRecord()来建立一个监听接入连接的BluetoothServerSocket 然后调用accept()监听连接请求。这个调用会阻塞,直到建立连接,并返回一个管理连接的BluetoothSocket 获得了 BluetoothSocket,并且不再需要连接,可以调用close()来关闭掉 BluetoothServerSocket 关闭 BluetoothServerSocket 并不会关闭返回的 BluetoothSocket

BluetoothServerSocket 是线程安全的,特别的是,close()会立刻关闭进行中的操作并关闭服务 socket。

BluetoothDevice

代表一个远程蓝牙设备。BluetoothDevice 能让你与其他设备建立连接,或者查询设备信息,比如名称,地址,类别和连接状态等。 这是蓝牙硬件地址的简单包装类。找个类的对象都说不可变的。这个类的操作会在远程蓝牙硬件地址上体现。

通过一个已知的MAC地址(可用BluetoothDevice来发现),或是通过BluetoothAdapter.getBondedDevices()返回的已连接设备 调用BluetoothAdapter.getRemoteDevice(String)来获得一个 BluetoothDevice。 然后就可以打开 BluetoothSocket 来与远程设备建立连接,调用createRfcommSocketToServiceRecord(UUID)

API 说明

  • 搜索其他蓝牙设备

  • 检索匹配到的蓝牙设备

  • 建立RFCOMM频道

  • 通过发现服务来连接其他设备

  • 管理多个连接

Setting Up Bluetooth 设置蓝牙

使用蓝牙通信前,确定设备支持蓝牙,并将蓝牙打开

1.获取 BluetoothAdapter 使用静态方法BluetoothAdapter.getDefaultAdapter()获取机器的蓝牙适配器;若返回null,则表示机器不支持蓝牙

  1. BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  2. if (mBluetoothAdapter == null) {
  3. // Device does not support Bluetooth
  4. }

2.激活蓝牙 检查蓝牙是否已经打开;若没打开,可以使用下面的方法打开蓝牙

  1. if (!mBluetoothAdapter.isEnabled()) {
  2. Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
  3. startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
  4. }

会弹出一个对话框,请求用户打开蓝牙。如果成功打开,activity 的 onActivityResult() 会收到 RESULT_OK , 如果未打开, 则收到 RESULT_CANCELED

可以让应用监听 ACTION_STATE_CHANGED ,当蓝牙状态改变时会发出这个广播。这个广播包括蓝牙原先状态和现在状态,分别装在 EXTRA_PREVIOUS_STATE 和 EXTRA_STATE 里。 状态可能是 STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, 和 STATE_OFF

Finding Devices 寻找设备

使用 BluetoothAdapter,可以寻找远程蓝牙设备或检索匹配设备

搜索设备是搜索本地区域已开启的蓝牙设备程序。蓝牙设备有可见模式和不可见模式。如果一个设备是可发现的,它会相应搜索请求并 返回一些信息,比如设备名,类别,独立的MAC地址。利用这些信息,设备可以初始化一个到被发现设备的连接。

第一次与其他设备连接建立,会自动弹出一个配对请求给用户。成功配对后,设备的基本信息会被保存下来,并且可以被蓝牙API调用。 使用已知的远程设备的MAC地址,可以在不搜索设备的情况下建立连接。

配对和连接是不同的。配对表示两个设备知道对方的存在,有相互认证的key,能够与对方建立加密的连接。 连接意味着设备目前共享一个RFCOMM频道,并能相互发送数据。目前android蓝牙API要求设备先配对,再进行连接。

注意:Android设备并不是默认蓝牙可见的。用户可以在系统设置中让设备蓝牙可被搜索到。

Querying paired devices 检索已配对的设备

搜索设备前,可以调用getBondedDevices()检索一下已配对的设备。这会返回配对设备的BluetoothDevices集合。 例如,你可以检索配对设备并把每个设备信息存入 ArrayAdapter :

  1. Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
  2. // If there are paired devices
  3. if (pairedDevices.size() > 0) {
  4. // Loop through paired devices
  5. for (BluetoothDevice device : pairedDevices) {
  6. // Add the name and address to an array adapter to show in a ListView
  7. mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
  8. }
  9. }

只需要MAC地址,就能够用BluetoothDevice对象建立连接。

Discovering devices 搜索设备

调用startDiscovery()搜索设备。这个异步进程会立刻返回是否启动成功的boolean值。搜索进程通常是inquiry scan进行12秒, 接下来是每个发现设备的 page scan 。

你的应用必须注册一个广播接收器来监听 ACTION_FOUND ,接收发现的每个设备的信息。每发现一个设备,系统会发送 ACTION_FOUND 这个 Intent 带有 EXTRA_DEVICE 和 EXTRA_CLASS,里面分别包含 BluetoothDevice 和一个 BluetoothClass 例如,注册一个广播接收器来监听被发现设备:

  1. // Create a BroadcastReceiver for ACTION_FOUND
  2. private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  3. public void onReceive(Context context, Intent intent) {
  4. String action = intent.getAction();
  5. // When discovery finds a device
  6. if (BluetoothDevice.ACTION_FOUND.equals(action)) {
  7. // Get the BluetoothDevice object from the Intent
  8. BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  9. // Add the name and address to an array adapter to show in a ListView
  10. mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
  11. }
  12. }
  13. };
  14. // Register the BroadcastReceiver
  15. IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
  16. registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

警告:搜索设备对于蓝牙适配器来说是一个耗费资源的进程。发现目标设备后,一定要在连接前调用cancelDiscovery()停止搜索。 不要在与设备连接时启动搜索。搜索设备会减小连接的带宽。

Enabling discoverability 激活设备蓝牙可见

如果想让本地设备对其他设备蓝牙可见,调用startActivityForResult(Intent, int),传入ACTION_REQUEST_DISCOVERABLE 这会请求激活系统设置。默认激活120秒。可以用EXTRA_DISCOVERABLE_DURATION来请求别的时间。应用可设最长时间是3600秒。 0表示设备永远可见。在0~3600外的数字会被设置为120秒。比如,将时间设置为300秒:

Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);

discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

startActivity(discoverableIntent);

会显示一个对话框来请求用户。如果用户点击“Yes”,设备会变得蓝牙可见。activity会收到onActivityResult()回调,并传回 设置蓝牙可见时间的数值。如果用户点击“No”或出现错误,返回代码会是 RESULT_CANCELED 。

注意:如果设备未开启蓝牙,将设备蓝牙设为可见会自动打开蓝牙

设备会在允许时间内保持沉默。可以注册广播接收器来监听 ACTION_SCAN_MODE_CHANGED 。这个Intent会带着 EXTRA_SCAN_MODE 和 EXTRA_PREVIOUS_SCAN_MODE,分别是新的和旧的状态。可能的状态值会是: SCAN_MODE_CONNECTABLE_DISCOVERABLE, SCAN_MODE_CONNECTABLE, 或 SCAN_MODE_NONE

如果要与远程设备连接,可以不激活设备可见。当应用想要建立服务socket获取接入时,必须打开设备可见。因为远程设备必须要发现 本地设备来建立连接。

Connecting Devices 连接设备

服务端设备和客户端设备以不同的方式获取 BluetoothSocket。连接建立时,服务端获取一个 BluetoothSocket。 客户端打开一个RFCOMM时会得到 BluetoothSocket 。

Connecting as a server 在连接中作为服务端

当你要连接2个设备,其中一个必须作为服务器并持有一个打开的 BluetoothServerSocket 。服务socket的目的是获取接入请求并 给已连上设备一个 BluetoothSocket 。当从 BluetoothServerSocket 获取到 BluetoothSocket,BluetoothServerSocket 可以关闭掉,除非你想接入更多连接。

  1. 调用listenUsingRfcommWithServiceRecord(String, UUID)来获得一个 BluetoothServerSocket

  2. 调用accept() 来监听接入请求

  3. 如果不需要接入更多连接,调用close()

accept()不应该在UI线程中调用,因为它是阻塞式的。通常在应用中启动新的线程来操作BluetoothServerSocket 或 BluetoothSocket 在另一个线程调用BluetoothServerSocket (或 BluetoothSocket)的close()能跳出阻塞,并立刻返回

例子:

  1. private class AcceptThread extends Thread {
  2. private final BluetoothServerSocket mmServerSocket;
  3. public AcceptThread() {
  4. // Use a temporary object that is later assigned to mmServerSocket,
  5. // because mmServerSocket is final
  6. BluetoothServerSocket tmp = null;
  7. try {
  8. // MY_UUID is the app's UUID string, also used by the client code
  9. tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
  10. } catch (IOException e) { }
  11. mmServerSocket = tmp;
  12. }
  13. public void run() {
  14. BluetoothSocket socket = null;
  15. // Keep listening until exception occurs or a socket is returned
  16. while (true) {
  17. try {
  18. socket = mmServerSocket.accept();
  19. } catch (IOException e) {
  20. break;
  21. }
  22. // If a connection was accepted
  23. if (socket != null) {
  24. // Do work to manage the connection (in a separate thread)
  25. manageConnectedSocket(socket);
  26. mmServerSocket.close();
  27. break;
  28. }
  29. }
  30. }
  31. /** Will cancel the listening socket, and cause the thread to finish */
  32. public void cancel() {
  33. try {
  34. mmServerSocket.close();
  35. } catch (IOException e) { }
  36. }
  37. }

上面这个例子只需要一个接入连接。连接建立并获得 BluetoothSocket 后,应用将这个 BluetoothSocket 发给单独的线程来处理 并关闭 BluetoothServerSocket 结束循环。

当accept()返回 BluetoothSocket,这个socket已经是连接上的了。因此不必调用connect()(客户端也一样)

manageConnectedSocket()是一个虚构的方法,用来初始化传输数据的线程,在连接管理中来讨论它

监听接入连接结束后通常要立刻关闭 BluetoothServerSocket 。这个例子中,获取 BluetoothSocket 后立刻调用了close()。 可以在线程中写一个公共方法来关闭私有的 BluetoothSocket 。当需要停止监听服务socket时可以使用这个方法。

Connecting as a client 在连接中作为客户端

与远程设备(服务端)建立连接,先获取一个代表远程设备的 BluetoothDevice 对象。必须使用 BluetoothDevice 来获取 BluetoothSocket 并初始化连接。

基本流程:

  1. 调用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)获取BluetoothSocket

  2. 调用connect()来初始化连接

注意:在调用connect()时必须确保设备不在搜索进行中。在搜索设备时,连接尝试会变慢并且很容易失败。

  1. private class ConnectThread extends Thread {
  2. private final BluetoothSocket mmSocket;
  3. private final BluetoothDevice mmDevice;
  4. public ConnectThread(BluetoothDevice device) {
  5. // Use a temporary object that is later assigned to mmSocket,
  6. // because mmSocket is final
  7. BluetoothSocket tmp = null;
  8. mmDevice = device;
  9. // Get a BluetoothSocket to connect with the given BluetoothDevice
  10. try {
  11. // MY_UUID is the app's UUID string, also used by the server code
  12. tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
  13. } catch (IOException e) { }
  14. mmSocket = tmp;
  15. }
  16. public void run() {
  17. // Cancel discovery because it will slow down the connection
  18. mBluetoothAdapter.cancelDiscovery();
  19. try {
  20. // Connect the device through the socket. This will block
  21. // until it succeeds or throws an exception
  22. mmSocket.connect();
  23. } catch (IOException connectException) {
  24. // Unable to connect; close the socket and get out
  25. try {
  26. mmSocket.close();
  27. } catch (IOException closeException) { }
  28. return;
  29. }
  30. // Do work to manage the connection (in a separate thread)
  31. manageConnectedSocket(mmSocket);
  32. }
  33. /** Will cancel an in-progress connection, and close the socket */
  34. public void cancel() {
  35. try {
  36. mmSocket.close();
  37. } catch (IOException e) { }
  38. }
  39. }

建立连接前,调用cancelDiscovery()。

manageConnectedSocket()是一个虚构的方法,在连接管理中讨论它。 使用完BluetoothSocket,一定要调用close()来结束

Managing a Connection

成功连接2个或更多的设备后,每个设备有一个 BluetoothSocket 。设备直接可以共享数据。使用BluetoothSocket传输任意数据

获取处理传输的 InputStream 和 OutputStream,分别调用 getInputStream() 和 getOutputStream() 调用read(byte[]) 和 write(byte[]) 来读写数据

线程的主循环中应该用于专门从InputStream中读数据。线程中要有专门的public方法来写数据到OutputStream

  1. private class ConnectedThread extends Thread {
  2. private final BluetoothSocket mmSocket;
  3. private final InputStream mmInStream;
  4. private final OutputStream mmOutStream;
  5. public ConnectedThread(BluetoothSocket socket) {
  6. mmSocket = socket;
  7. InputStream tmpIn = null;
  8. OutputStream tmpOut = null;
  9. // Get the input and output streams, using temp objects because
  10. // member streams are final
  11. try {
  12. tmpIn = socket.getInputStream();
  13. tmpOut = socket.getOutputStream();
  14. } catch (IOException e) { }
  15. mmInStream = tmpIn;
  16. mmOutStream = tmpOut;
  17. }
  18. public void run() {
  19. byte[] buffer = new byte[1024]; // buffer store for the stream
  20. int bytes; // bytes returned from read()
  21. // Keep listening to the InputStream until an exception occurs
  22. while (true) {
  23. try {
  24. // Read from the InputStream
  25. bytes = mmInStream.read(buffer);
  26. // Send the obtained bytes to the UI activity
  27. mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
  28. .sendToTarget();
  29. } catch (IOException e) {
  30. break;
  31. }
  32. }
  33. }
  34. /* Call this from the main activity to send data to the remote device */
  35. public void write(byte[] bytes) {
  36. try {
  37. mmOutStream.write(bytes);
  38. } catch (IOException e) { }
  39. }
  40. /* Call this from the main activity to shutdown the connection */
  41. public void cancel() {
  42. try {
  43. mmSocket.close();
  44. } catch (IOException e) { }
  45. }
  46. }

构造方法需要数据流,一旦执行,线程会等InputStream中传来的数据。当read(byte[])返回数据流,通过handler将数据送往 主activity。然后等待数据流中更多的字节

向外发送数据调用write()

线程的cancel()方法很重要。完成了蓝牙连接的所有操作后,应当cancel掉

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/646889
推荐阅读
相关标签
  

闽ICP备14008679号