Android无线蓝牙总结

一、基础知识:

①蓝牙的四层协议:

      蓝牙协议分为4层,即核心协议层、电缆替代协议层、电话控制协议层和采纳的其它协议层。这4种协议中最重要的是核心协议。蓝牙的核心协议包括基带、链路管理、逻辑链路控制和适应协议四部分。其中链路管理(LMP)负责蓝牙组件间连接的建立。逻辑链路控制与适应协议(L2CAP)位于基带协议层上,属于数据链路层,是一个为高层传输和应用层协议屏蔽基带协议的适配协议。

②蓝牙的操作:

 Android提供蓝牙API来执行这些不同的操作。 
 1. 开关蓝牙 
 2. 扫描其他蓝牙设备 
 3. 获取配对设备列表 
 4. 连接到通过服务发现其他设备

③相关类的概要说明:

关于经典蓝牙(以下简称蓝牙)开发所用到的API都来自于android.bluetooth包中,本部分主要介绍相关类的概要说明:

1、BluetoothAdapter

BluetoothAdapter类的对象代表本地的蓝牙适配器。BluetoothAdapter是所有蓝牙交互操作的入口点,通过使用该类的对象,可以完成以下操作:

  • 发现其他蓝牙设备
  • 查询已配对的设备
  • 通过已知的MAC地址实例化远程蓝牙设备
  • 创建BluetoothServerSocket类(下文2.4)对象监听与其他蓝牙设备的通信。

2、BluetoothDevice

表示远程的蓝牙设备。使用该类对象可进行远程蓝牙设备的连接请求,以及查询该蓝牙设备的信息,例如名称,地址等。

3、BluetoothSocket

表示蓝牙socket的接口(与TCP Socket类似, 关于socket的概念请自行查阅计算机网络的相关内容)。该类的对象作为应用中数据传输的连接点。

4、BluetoothServerSocket

表示服务器socket,用来监听未来的请求(和TCP ServerSocket类似)。为了能使两个蓝牙设备进行连接,一个设备必须使用该类开启服务器socket,当远程的蓝牙设备请求该服务端设备时,如果连接被接受,BluetoothServerSocket将会返回一个已连接的BluetoothSocket类对象。

④蓝牙权限

1. android.permission.BLUETOOTH:

  允许程序连接到已配对的蓝牙设备,请求连接/接收连接/传输数据需要改权限, 主要用于对配对后进行操作;

2. android.permission.BLUETOOTH_ADMIN :

  允许程序发现和配对蓝牙设备, 该权限用来管理蓝牙设备, 有了这个权限, 应用才能使用本机的蓝牙设备, 主要用于对配对前的操作;

⑤BluetoothAdapter

 BluetoothAdapter代表了移动设备的本地的蓝牙适配器, 通过该蓝牙适配器可以对蓝牙进行基本操作 

BluetoothAdapter.getDefaultAdapter()该静态方法可以获取该适配器对象.

⑥蓝牙的BluetoothAdapter .STATE 状态值 , 即开关状态

1.蓝牙关闭 int STATE_OFF //值为10, 蓝牙模块处于关闭状态; 
2.蓝牙打开中 int STATE_TURNING_ON //值为11, 蓝牙模块正在打开; 
3.蓝牙开启 int STATE_ON //值为12, 蓝牙模块处于开启状态; 
4. 蓝牙开启中 int STATE_TURNING_OFF //值为13, 蓝牙模块正在关闭; 
蓝牙开关状态顺序 : STATE_OFF –> STATE_TURNING_ON –> STATE_ON –>STATE_TURNING_OFF –> STATE_OFF;

⑦BluetoothAdapter SCAN_MOD状态值 ,即扫描状态

无功能状态 : int SCAN_MODE_NONE //值为20, 查询扫描和页面扫描都失效, 

该状态下蓝牙模块既不能扫描其它设备, 也不可见;

扫描状态 : int SCAN_MODE_CONNECTABLE //值为21, 查询扫描失效, 页面扫描有效, 

该状态下蓝牙模块可以扫描其它设备, 从可见性来说只对已配对的蓝牙设备可见, 只有配对的设备才能主动连接本设备;

可见状态 : int SCAN_MODE_CONNECTABLE_DISCOVERABLE //值为23, 查询扫描和页面扫描都有效;

⑧打开/关闭蓝牙的两种方法:

1.直接调用函数enable()去打开蓝牙设备 ; 

2.系统API去打开蓝牙设备,该方式会弹出一个对话框样式的Activity供用户选择是否打开蓝牙设备。

//第一种启动蓝牙的方法,不推荐
//bluetoothAdapter.enable();


//第二种启动蓝牙的方法,推荐
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), REQUEST_ENABLE);
//第二种方法要写数据回调方法
/**
 * 数据回调方法
 */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == REQUEST_ENABLE) {
        if (resultCode == RESULT_OK) {
            Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
           // getMyBondedDevices();//获取绑定的蓝牙设备
            adapter.notifyDataSetChanged();//刷新适配器
        } else {
            Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();
        }
    } 
}

⑨关闭蓝牙,直接调用API 函数即disable()即可

public boolean disable () 

返回值:该函数会立即返回。 
1.true 表示关闭操作成功 
2. false 表示蓝牙操作失败

  ①、当前蓝牙已经关闭 ; ②、其他一些异常情况

⑩扫描蓝牙设备

1.public boolean startDiscovery () 
      功能: 扫描蓝牙设备的开启
      注意: 如果蓝牙没有开启,该方法会返回false ,即不会开始扫描过程。

  2.public  boolean cancelDiscovery ()
     功能: 取消扫描过程。
     注意: 如果蓝牙没有开启,该方法会返回false。

 3.public boolean isDiscovering ()
    功能: 是否正在处于扫描过程中。
    注意: 如果蓝牙没有开启,该方法会返回false。

   这里要特别注意,蓝牙扫描的时候,它会发出系统的广播,这是我们就要创建广播接收者来接收数据,数据里面就有蓝牙的设备对象和名称等等,广播也是蓝牙知识的重中之重。 

(十一)蓝牙的广播

Action值说明
ACTION_STATE_CHANGED 蓝牙状态值发生改变
*ACTION_SCAN_MODE_CHANGED 蓝牙扫描状态(SCAN_MODE)发生改变*
ACTION_DISCOVERY_STARTED 蓝牙扫描过程开始
ACTION_DISCOVERY_FINISHED 蓝牙扫描过程结束
ACTION_LOCAL_NAME_CHANGED 蓝牙设备Name发生改变
ACTION_REQUEST_DISCOVERABLE 请求用户选择是否使该蓝牙能被扫描

      如果蓝牙没有开启,用户点击确定后,会首先开启蓝牙,继而设置蓝牙能被扫描。 
  Action值: ACTION_REQUEST_ENABLE // 请求用户选择是否打开蓝牙 

创建广播接收者:

 

 1 /**
 2  * 广播接收者的创建
 3  */
 4 private BroadcastReceiver receiver = new BroadcastReceiver() {
 5     @Override
 6     public void onReceive(Context context, Intent intent) {
 7         //获取设备的发送的广播
 8         //做数据处理
 9      }
10 };

注册广播接收者:

/**
 * 广播的注册,注意这里Action可以添加多个
 */
@Override
protected void onResume() {
    super.onResume();
//添加蓝牙广播的Action,发现蓝牙设备时的Action
    IntentFilter intentFilter = new 
IntentFilter(BluetoothDevice.ACTION_FOUND);
//添加蓝牙广播的Action,蓝牙设备扫描完毕时的Action 
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    registerReceiver(receiver, intentFilter);//注册广播接收者
}

广播的注销:

/**
 * 广播的停止
 */
@Override
protected void onPause() {
    super.onPause();
    unregisterReceiver(receiver);//取消广播
}

  

一般监听发现蓝牙和蓝牙扫描完成的广播就可以了。 通过广播接收数据后,再对数据进行处理。就可以看到我们手机上显示的蓝牙设设备名称和其他信息。

(十二)获取蓝牙的相关信息的方法

1.public String getName ()

功能:获取蓝牙设备Name

2.public String getAddress ()

功能:获取蓝牙设备的硬件地址(MAC地址),例如:00:11:22:AA:BB:CC

3.public boolean setName (String name)

功能:设置蓝牙设备的Name。

4.public SetgetBondedDevices ()

功能:获取与本机蓝牙所有绑定的远程蓝牙信息,以BluetoothDevice类实例(稍后讲到)返回。 
注意:如果蓝牙未开启,该函数会返回一个空集合 。

5.public static boolean checkBluetoothAddress (String address)

 功能: 验证蓝牙设备MAC地址是否有效。所有设备地址的英文字母必须大写,48位,形如:00:43:A8:23:10:F1 。

返回值: true 设备地址有效,false 设备地址无效

6.public BluetoothDevice getRemoteDevice (String address)

功能:以给定的MAC地址去创建一个 BluetoothDevice 类实例(代表远程蓝牙实例)。即使该蓝牙地址不可见,也会产生 一个BluetoothDevice 类实例。 
返回:BluetoothDevice 类实例 。注意,如果该蓝牙设备MAC地址不能被识别,其蓝牙Name为null。 
异常:如果MAC address无效,抛出IllegalArgumentException。

使用上面的方法就可以对蓝牙进行扫描显示。但是要使用蓝牙通信就要使用到Socket编程了。

二.蓝牙Socket通信

(一)UUID

      在蓝牙中,每个服务和服务属性都唯一地由 全局唯一标识符 ,Universally Unique Identifier(UUID)来校验。正如它的名字所暗示的,每一个这样的标识符都要在时空上保证唯一。UUID类可表现为短整形(16或32位)和长整形(128 位)UUID。他提供了分别利用String和16位或32位数值来创建类的构造函数,提供了一个可以比较两个UUID(如果两个都是128位)的方法,还有一个可以转换一个UUID为一个字符串的方法。UUID实例是不可改变的(immutable),只有被UUID标示的服务可以被发现。 
      UUID的格式被分成5段,其中中间3段的字符数相同,都是4个,第1段是8个字符,最后一段是12个字符。所以UUID实际上是8个-4个-4个-4个-12个的字符串。 
      UUID相当于Socket的端口,而蓝牙地址相当于Socket的IP。两个蓝牙设备进行连接时需要使用同一个UUID, 这是一个服务的唯一标识,而且这个UUID的值必须是 
      00001101-0000-1000-8000-00805F9B34FB 

 上面这个UUID,直接复制使用就可以了。

 

  两个手机,其中一个设置为服务器,然后点击连接的手机,就可以进行消息发送和接收了。 
上面只是实现文本通信,文本也只是进行简单处理。

三、项目代码

权限:

1 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
2 <uses-permission android:name="android.permission.BLUETOOTH" />
3 <!--6.0以上才要加的额外权限 -->
4 <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

布局文件:

<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:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="open"
        android:text="开启蓝牙" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="close"
        android:text="关闭蓝牙" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="found"
        android:text="暴露自己的设备名称" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="scan"
        android:text="扫描蓝牙设备" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="createServer"
        android:text="设置为蓝牙服务端" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="listen"
        android:text="监听数据的接收" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <EditText
            android:id="@+id/et_send"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:singleLine="true" />

        <Button
            android:id="@+id/btn_send"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:onClick="send"
            android:text="send" />
    </LinearLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TextView
                android:id="@+id/tv_show"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <TextView
                android:id="@+id/tv_show_service"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="right" />

            <ListView
                android:id="@+id/lv"
                android:layout_width="match_parent"
                android:layout_height="200dp" />
        </LinearLayout>
    </ScrollView>
</LinearLayout>

可读写流的类

 1 package com.example.lifen.bluetoothdemo;
 2 
 3 /**
 4  * Created by LiFen on 2018/1/2.
 5  */
 6 
 7 import android.bluetooth.BluetoothSocket;
 8 import android.util.Log;
 9 
10 import java.io.InputStream;
11 import java.io.OutputStream;
12 
13 /**
14  * 在子线程中进行读写操作
15  */
16 public class RWStream extends Thread {
17 
18     InputStream is;//输入流
19     OutputStream os;//输出流
20 
21     //蓝牙的Socket对象
22     private final BluetoothSocket socket;
23 
24     //通过构造方法传入Socket对象
25     public RWStream(BluetoothSocket socket) {
26         this.socket = socket;
27     }
28 
29     @Override
30     public void run() {
31         super.run();
32         try {
33             is = socket.getInputStream();//获取Socket的输入流
34             os = socket.getOutputStream();//获取Socket的输出流
35 
36             byte[] buf = new byte[1024];
37             int len = 0;
38             Log.e("TAG", "-----------开始读取----(is==null)   " + (is == null));
39             while (socket.isConnected()) {//当Socket是连接状态时,就一直进行数据的读取
40                 while ((len = is.read(buf)) != -1) {
41                     String message = new String(buf, 0, len);
42                     //获取流里面的数据
43                     Log.e("TAG", "----------" + message);
44                     //如果在另一端设置的接口对象,那么就传递数据
45                     if (dateShow != null) {
46                         dateShow.getMessager(message);
47                     }
48                 }
49             }
50         } catch (Exception e) {
51             Log.e("TAG", "-----------线程异常");
52         }
53     }
54 
55     /**
56      * 数据的写入
57      */
58     public void write(String msg) {
59         Log.e("TAG", "--------os!=null   " + (os != null));
60         if (os != null) {
61             try {
62                 //Socket数据的写入
63                 os.write(msg.getBytes());
64                 //刷新输出流数据
65                 os.flush();
66             } catch (Exception e) {
67                 Log.e("TAG", "---写入--------异常" + e.getMessage());
68             }
69         }
70     }
71 
72     /**
73      * 定义接口实现数据回调
74      */
75     interface DataShow {
76         //返回数据,读取到的字符串,传递过去
77         void getMessager(String message);
78 
79     }
80 
81     //定义接口对象
82     DataShow dateShow;
83 
84     //接口的对象的设置方法
85     public void setDataShow(DataShow dateShow) {
86         this.dateShow = dateShow;
87     }
88 }

蓝牙服务器端(Socket服务端)的设计

 1 package com.example.lifen.bluetoothdemo;
 2 
 3 /**
 4  * Created by LiFen on 2018/1/2.
 5  */
 6 
 7 import android.bluetooth.BluetoothAdapter;
 8 import android.bluetooth.BluetoothServerSocket;
 9 import android.bluetooth.BluetoothSocket;
10 import android.util.Log;
11 
12 import java.io.IOException;
13 
14 /**
15  * 蓝牙设置的服务器端
16  */
17 
18 public class BlueServer extends Thread {
19     //可读写数据的对象
20     RWStream rwStream;
21 
22     public RWStream getRwStream() {
23         return rwStream;
24     }
25 
26     //蓝牙设备管理器
27     private final BluetoothAdapter adapter;
28 
29     //通过构造方法传入设置管理器
30     public BlueServer(BluetoothAdapter adapter) {
31         this.adapter = adapter;
32     }
33 
34     //线程内的任务
35     @Override
36     public void run() {
37         super.run();
38         try {
39             //创建蓝牙服务端的Socket,这里第一个参数是服务器的名称,第二个参数是UUID的字符串的值
40             BluetoothServerSocket socket = adapter.listenUsingRfcommWithServiceRecord("server", MainActivity.uuid);
41             Log.e("TAG", "--------------->>开始监听客户端连接");
42             //获取蓝牙客户端对象,这是一个同步方法,用客户端接入才有后面的操作
43             BluetoothSocket client = socket.accept();
44             Log.e("TAG", "--------------->>有客户端接入");
45             //获取可读可写对象
46             rwStream = new RWStream(client);
47             //开始可读可写线程的操作,这里是一直在读取数据的状态
48             rwStream.start();
49         } catch (IOException e) {
50             e.printStackTrace();
51         }
52 
53     }
54 }

主方法类的设计

  1 package com.example.lifen.bluetoothdemo;
  2 
  3 import android.Manifest;
  4 import android.bluetooth.BluetoothAdapter;
  5 import android.bluetooth.BluetoothDevice;
  6 import android.bluetooth.BluetoothSocket;
  7 import android.content.BroadcastReceiver;
  8 import android.content.Context;
  9 import android.content.Intent;
 10 import android.content.IntentFilter;
 11 import android.content.pm.PackageManager;
 12 import android.graphics.Color;
 13 import android.os.Build;
 14 import android.os.Bundle;
 15 import android.os.Handler;
 16 import android.os.Message;
 17 import android.support.annotation.NonNull;
 18 import android.support.v7.app.AppCompatActivity;
 19 import android.text.TextUtils;
 20 import android.util.Log;
 21 import android.view.View;
 22 import android.widget.AdapterView;
 23 import android.widget.ArrayAdapter;
 24 import android.widget.Button;
 25 import android.widget.EditText;
 26 import android.widget.ListView;
 27 import android.widget.TextView;
 28 import android.widget.Toast;
 29 
 30 import java.io.IOException;
 31 import java.util.ArrayList;
 32 import java.util.List;
 33 import java.util.UUID;
 34 
 35 import static android.util.Log.e;
 36 
 37 /**
 38  * 蓝牙的使用示例
 39  */
 40 public class MainActivity extends AppCompatActivity {
 41 
 42     //控制蓝牙设备的对象
 43     BluetoothAdapter bluetoothAdapter;
 44     //布局内的ListView控件
 45     ListView listView;
 46     TextView tv_show;
 47     TextView tv_show_service;
 48     EditText et_send;
 49     Button btn_send;
 50     ArrayAdapter adapter;//适配器对象的定义
 51     //蓝牙设备的对象的集合
 52     ArrayList<BluetoothDevice> devices = new ArrayList<>();
 53     //设备的名称集合
 54     ArrayList<String> deviceNames = new ArrayList<>();
 55     //手机蓝牙的UUID固定值
 56     public static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
 57     private static final int REQUEST_LOCATION = 1000;//手机动态请求权限的请求码
 58     private static final int REQUEST_ENABLE = 1001;//启动蓝牙设备的请求码
 59     private static final int REQUEST_DISCOVER_MYSELF = 1002;//设置自身蓝牙设备可被发现的请求码
 60 
 61     @Override
 62     protected void onCreate(Bundle savedInstanceState) {
 63         super.onCreate(savedInstanceState);
 64         setContentView(R.layout.activity_main);
 65         //实例化
 66         bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 67         listView = (ListView) findViewById(R.id.lv);
 68         tv_show = (TextView) findViewById(R.id.tv_show);
 69         tv_show_service = (TextView) findViewById(R.id.tv_show_service);
 70         et_send = (EditText) findViewById(R.id.et_send);
 71         btn_send = (Button) findViewById(R.id.btn_send);
 72         //创建适配器,使用系统布局
 73         adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, deviceNames);
 74         //给ListView设置适配器
 75         listView.setAdapter(adapter);
 76         //判断是否有了权限
 77         checkPermission();
 78         //给ListView设置点击事件,点击对应的条目就创建对应的客户端,并经行数据的读取和写入
 79         listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
 80             @Override
 81             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 82                 //连接服务器
 83                 connServer(devices.get(position));
 84             }
 85         });
 86 
 87 
 88     }
 89 
 90     /**
 91      * 打开蓝牙设备
 92      */
 93     public void open(View view) {
 94         //不推荐
 95         //bluetoothAdapter.enable();
 96         //推荐
 97         startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), REQUEST_ENABLE);
 98     }
 99 
100     /**
101      * 关闭蓝牙设备
102      */
103     public void close(View view) {
104         bluetoothAdapter.disable();//关闭
105         //擦除页面数据
106         deviceNames.clear();
107         adapter.notifyDataSetChanged();
108     }
109 
110     /**
111      * 扫描蓝牙设备
112      */
113     public void scan(View view) {
114 
115         if (bluetoothAdapter.isEnabled()) {
116             //先清除页面数据!
117             devices.clear();
118             deviceNames.clear();
119 
120             //使用广播的方法去获取设备,这里就要动态创建广播,并进行接听了
121             //定义一个系统规定action的广播,
122             //当系统没扫描到一个蓝牙设备就会发送一条广播
123             // 当系统做完扫描后,系统会发送广播,你只需在广播接收者做好处理
124             bluetoothAdapter.startDiscovery();
125         } else {
126             Toast.makeText(this, "请先开启蓝牙", Toast.LENGTH_SHORT).show();
127         }
128     }
129 
130     /**
131      * 让自身蓝牙设备可被发现
132      */
133     public void found(View view) {
134         getMyBondedDevices();
135         if (bluetoothAdapter.isDiscovering()) {//如果蓝牙设置正在扫描
136             Toast.makeText(this, "正在扫描,别急", Toast.LENGTH_SHORT).show();
137         } else {
138             //这里可以设置显示自己蓝牙设备的时间,默认是300秒,也可以自定义单位是秒
139             Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600);
140             startActivityForResult(intent, REQUEST_DISCOVER_MYSELF);
141         }
142     }
143 
144     /**
145      * 开启蓝牙服务的服务端
146      */
147     boolean isServer = false;//默认是普通客户端
148     BlueServer server;//创建蓝牙服务器的对象,在服务器做相关操作
149 
150     public void createServer(View view) {
151         server = new BlueServer(bluetoothAdapter);
152         server.start();
153         isServer = true;//设置为服务端
154     }
155 
156     /**
157      * 发送数据
158      */
159     public void send(View view) {
160         String et = et_send.getText().toString();//获取输入框的数据
161         //给另一端写入数据
162         write(et);
163     }
164 
165     /**
166      * 数据的传递
167      */
168     public void write(String msg) {
169         if (isServer) {//服务器的写数据
170             btn_send.setText("服务器");
171             handlerSendMessager(1, msg);
172             e("TAG", "----------(server != null && server.getRwStream() != null)  " + (server != null && server.getRwStream() != null));
173             if (server != null && server.getRwStream() != null) {
174                 server.getRwStream().write(msg);
175             }
176         } else {//客户端的写数据
177             btn_send.setText("客户端");
178             handlerSendMessager(2, msg);
179             if (client != null) {
180                 client.write(msg);
181             }
182         }
183     }
184 
185     /**
186      * 监听数据的接收
187      */
188     public void listen(View view) {
189 
190         //服务器的监听数据
191         if (server != null) {
192             server.getRwStream().setDataShow(new RWStream.DataShow() {
193                 @Override
194                 public void getMessager(final String message) {
195                     //要在主线程中改变UI
196                     Log.e("TAG", "-------listen---Service" + message);
197                     handlerSendMessager(2, message);
198                 }
199             });
200             //客户端的监听数据
201         } else if (client != null) {
202             client.setDataShow(new RWStream.DataShow() {
203                 @Override
204                 public void getMessager(final String message) {
205                     //要在主线程中改变UI
206                     e("TAG", "-------listen---client" + message);
207                     handlerSendMessager(1, message);
208                 }
209             });
210         }
211     }
212 
213     /**
214      * Handler包装类
215      */
216     private void handlerSendMessager(int what, String messge) {
217         Message msg = Message.obtain();
218         msg.what = what;
219         msg.obj = messge;
220         handler.sendMessage(msg);
221     }
222 
223     /**
224      * 创建Handler对象用于线程间通信
225      */
226     Handler handler = new Handler() {
227         @Override
228         public void handleMessage(Message msg) {
229             super.handleMessage(msg);
230             //显示数据在文本中
231             if (msg.what == 1) {
232                 //服务器的数据在右边
233                 tv_show_service.setTextColor(Color.RED);
234                 tv_show_service.setTextSize(20);
235                 tv_show_service.append(msg.obj + "
");
236             } else {
237                 //客户端的数据在左边
238                 tv_show.setTextColor(Color.BLUE);
239                 tv_show.setTextSize(20);
240                 tv_show.append(msg.obj + "
");
241             }
242 
243         }
244     };
245 
246 
247     /**
248      * 客户端连接服务器
249      */
250     RWStream client;
251 
252     private void connServer(BluetoothDevice device) {
253         try {
254             //创建蓝牙客户端的Socket对象,这里是类BluetoothSocket,服务端是BluetoothServerSocket
255             BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid);
256             socket.connect();//连接Socket
257             //创建可读写的客户对象,传入Socket对象
258             client = new RWStream(socket);
259             //开始客户端的线程
260             client.start();
261         } catch (IOException e) {
262             e.printStackTrace();
263         }
264 
265     }
266 
267     /**
268      * 判断是否有蓝牙的权限,如果手机系统是6.0以上的就要动态创建权限
269      */
270     private void checkPermission() {
271         if (Build.VERSION.SDK_INT >= 23) {
272             //23
273             int check = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
274             if (check != PackageManager.PERMISSION_GRANTED) {
275                 //请求权限
276                 requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_LOCATION);
277             }
278         }
279     }
280 
281     /**
282      * 动态请求权限后,返回页面时的回调方法
283      */
284     @Override
285     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
286         super.onRequestPermissionsResult(requestCode, permissions, grantResults);
287         if (requestCode == REQUEST_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
288             Toast.makeText(this, "权限已获取", Toast.LENGTH_SHORT).show();
289         } else {
290             Toast.makeText(this, "权限未获取", Toast.LENGTH_SHORT).show();
291             finish();//关闭页面
292         }
293     }
294 
295     /**
296      * 数据回调方法
297      */
298     @Override
299     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
300         super.onActivityResult(requestCode, resultCode, data);
301         if (requestCode == REQUEST_ENABLE) {
302             if (resultCode == RESULT_OK) {
303                 Toast.makeText(this, "蓝牙已开启", Toast.LENGTH_SHORT).show();
304                 getMyBondedDevices();//获取绑定的蓝牙设备
305                 adapter.notifyDataSetChanged();//刷新适配器
306             } else {
307                 Toast.makeText(this, "蓝牙未开启", Toast.LENGTH_SHORT).show();
308             }
309         } else {
310 
311         }
312     }
313 
314     /**
315      * 获取已经绑定的蓝牙设置
316      */
317     private void getMyBondedDevices() {
318         //获取所有已经绑定了的设备
319         deviceNames.clear();//清除设备名称的集合
320         devices.clear();//清除蓝牙设备对象的集合
321         if (bluetoothAdapter.getBondedDevices() != null) {//如果蓝牙适配器对象不为空时
322             //获取里面的数据的对象
323             List<BluetoothDevice> liset = new ArrayList<>(bluetoothAdapter.getBondedDevices());
324             devices.addAll(liset);
325             //拿到适配器对象的名称数据
326             for (BluetoothDevice device : liset) {
327                 deviceNames.add(device.getName());
328             }
329         }
330     }
331 
332     /**
333      * 广播的注册
334      */
335     @Override
336     protected void onResume() {
337         super.onResume();
338         IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
339         //添加蓝牙广播的Action
340         intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
341         registerReceiver(receiver, intentFilter);//注册广播接收者
342     }
343 
344     /**
345      * 广播的停止
346      */
347     @Override
348     protected void onPause() {
349         super.onPause();
350         unregisterReceiver(receiver);//取消广播
351     }
352 
353     /**
354      * 广播接收者的创建
355      */
356     private BroadcastReceiver receiver = new BroadcastReceiver() {
357         @Override
358         public void onReceive(Context context, Intent intent) {
359             //获取设备的发送的广播
360             if (intent.getAction().equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
361                 Toast.makeText(context, "蓝牙扫描完毕", Toast.LENGTH_SHORT).show();
362             } else {
363                 //获取蓝牙设置对象
364                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
365                 //把对象和名称放到对应的集合中
366                 devices.add(device);
367                 deviceNames.add(TextUtils.isEmpty(device.getName()) ? "未命名" : device.getName());
368                 adapter.notifyDataSetChanged();
369             }
370         }
371     };
372 
373 
374 }

本项目下载:http://download.csdn.net/download/qq_36726507/10185756

原文地址:https://www.cnblogs.com/jxust-jiege666/p/8182863.html