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 + "\n"); 236 } else { 237 //客户端的数据在左边 238 tv_show.setTextColor(Color.BLUE); 239 tv_show.setTextSize(20); 240 tv_show.append(msg.obj + "\n"); 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