您的位置:首页 > 移动开发 > Android开发

android中的无线通信蓝牙

2017-07-25 14:40 246 查看

一.蓝牙基础知识

      蓝牙(Bluetooth)是一种短距离的无线通信技术标准。这个名子来源于10世纪丹麦国王Harald Blatand,英文名子是Harold Bluetooth。

(一)蓝牙的四层协议

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

(二)蓝牙的操作

Android提供蓝牙API来执行这些不同的操作。

1. 开关蓝牙

2. 扫描其他蓝牙设备

3. 获取配对设备列表

4. 连接到通过服务发现其他设备

(三)蓝牙权限

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();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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

public boolean disable ()

返回值:该函数会立即返回。

1.true 表示关闭操作成功

2. false 表示蓝牙操作失败 , ①、当前蓝牙已经关闭 ; ②、其他一些异常情况

(九)扫描蓝牙设备

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

2.public  boolean cancelDiscovery ()
功能: 取消扫描过程
18e75
。
注意: 如果蓝牙没有开启,该方法会返回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请求用户选择是否使该蓝牙能被扫描
      PS:如果蓝牙没有开启,用户点击确定后,会首先开启蓝牙,继而设置蓝牙能被扫描。

Action值: ACTION_REQUEST_ENABLE // 请求用户选择是否打开蓝牙

示例:

创建广播接收者:

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


1
2
3
4
5
6
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);//注册广播接收者
}
1
2
3
4
5
6
7
8
9
10
11
12
13


1
2
3
4
5
6
7
8
9
10
11
12
13
广播的注销

/**
* 广播的停止
*/
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(receiver);//取消广播
}
1
2
3
4
5
6
7
8


1
2
3
4
5
6
7
8
      一般监听发现蓝牙和蓝牙扫描完成的广播就可以了。

      通过广播接收数据后,再对数据进行处理。就可以看到我们手机上显示的蓝牙设设备名称和其他信息。

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

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通信

      关于Socket编程,之前有一篇Java的简介和使用示例:

http://blog.csdn.net/wenzhi20102321/article/details/52620323

(一)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,直接复制使用就可以了。

下面是蓝牙Socket使用示例:

三.蓝牙Socket通信示例

      程序实现两台手机的蓝牙发送消息,并接受消息。这里的蓝牙是不需要网络支持的。

程序运行后,扫描蓝牙后的界面:



程序进行通信后的界面,要两个手机哦!



      两个手机,其中一个设置为服务器,然后点击连接的手机,就可以进行消息发送和接收了。

上面只是实现文本通信,文本也只是进行简单处理。

程序设计代码:

(一)添加权限

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


1
2
3
4

(二)布局文件的设计

<?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="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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

(三)可读写流的类

package fuxi.bluetooth20;

import android.bluetooth.BluetoothSocket;
import android.util.Log;

import java.io.InputStream;
import java.io.OutputStream;

/**
* 在子线程中进行读写操作
*/
public class RWStream extends Thread {

InputStream is;//输入流
OutputStream os;//输出流

//蓝牙的Socket对象
private final BluetoothSocket socket;

//通过构造方法传入Socket对象
public RWStream(BluetoothSocket socket) {
this.socket = socket;
}

@Override
public void run() {
super.run();
try {
is = socket.getInputStream();//获取Socket的输入流
os = socket.getOutputStream();//获取Socket的输出流

byte[] buf = new byte[1024];
int len = 0;
Log.e("TAG", "-----------开始读取----(is==null)   " + (is == null));
while (socket.isConnected()) {//当Socket是连接状态时,就一直进行数据的读取
while ((len = is.read(buf)) != -1) {
String message = new String(buf, 0, len);
//获取流里面的数据
Log.e("TAG", "----------" + message);
//如果在另一端设置的接口对象,那么就传递数据
if (dateShow != null) {
dateShow.getMessager(message);
}
}
}
} catch (Exception e) {
Log.e("TAG", "-----------线程异常");
}
}

/**
* 数据的写入
*/
public void write(String msg) {
Log.e("TAG", "--------os!=null   " + (os != null));
if (os != null) {
try {
//Socket数据的写入
os.write(msg.getBytes());
//刷新输出流数据
os.flush();
} catch (Exception e) {
Log.e("TAG", "---写入--------异常" + e.getMessage());
}
}
}

/**
* 定义接口实现数据回调
*/
interface DataShow {
//返回数据,读取到的字符串,传递过去
void getMessager(String message);

}

//定义接口对象
DataShow dateShow;

//接口的对象的设置方法
public void setDataShow(DataShow dateShow) {
this.dateShow = dateShow;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84

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

package fuxi.bluetooth20;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.util.Log;

import java.io.IOException;

/**
* 蓝牙设置的服务器端
*/

public class BlueServer extends Thread {
//可读写数据的对象
RWStream rwStream;

public RWStream getRwStream() {
return rwStream;
}

//蓝牙设备管理器
private final BluetoothAdapter adapter;

//通过构造方法传入设置管理器
public BlueServer(BluetoothAdapter adapter) {
this.adapter = adapter;
}

//线程内的任务
@Override
public void run() {
super.run();
try {
//创建蓝牙服务端的Socket,这里第一个参数是服务器的名称,第二个参数是UUID的字符串的值
BluetoothServerSocket socket = adapter.listenUsingRfcommWithServiceRecord("server", MainActivity.uuid);
Log.e("TAG", "--------------->>开始监听客户端连接");
//获取蓝牙客户端对象,这是一个同步方法,用客户端接入才有后面的操作
BluetoothSocket client = socket.accept();
Log.e("TAG", "--------------->>有客户端接入");
//获取可读可写对象
rwStream = new RWStream(client);
//开始可读可写线程的操作,这里是一直在读取数据的状态
rwStream.start();
} catch (IOException e) {
e.printStackTrace();
}

}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

(五)主方法类的设计

package fuxi.bluetooth20;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import static android.util.Log.e;

/**
* 蓝牙的使用示例
*/
public class MainActivity extends AppCompatActivity {

//控制蓝牙设备的对象
BluetoothAdapter bluetoothAdapter;
//布局内的ListView控件
ListView listView;
TextView tv_show;
TextView tv_show_service;
EditText et_send;
Button btn_send;
ArrayAdapter adapter;//适配器对象的定义
//蓝牙设备的对象的集合
ArrayList<BluetoothDevice> devices = new ArrayList<>();
//设备的名称集合
ArrayList<String> deviceNames = new ArrayList<>();
//手机蓝牙的UUID固定值
public static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final int REQUEST_LOCATION = 1000;//手机动态请求权限的请求码
private static final int REQUEST_ENABLE = 1001;//启动蓝牙设备的请求码
private static final int REQUEST_DISCOVER_MYSELF = 1002;//设置自身蓝牙设备可被发现的请求码

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//实例化
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
listView = (ListView) findViewById(R.id.lv);
tv_show = (TextView) findViewById(R.id.tv_show);
tv_show_service = (TextView) findViewById(R.id.tv_show_service);
et_send = (EditText) findViewById(R.id.et_send);
btn_send = (Button) findViewById(R.id.btn_send);
//创建适配器,使用系统布局
adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, deviceNames);
//给ListView设置适配器
listView.setAdapter(adapter);
//判断是否有了权限
checkPermission();
//给ListView设置点击事件,点击对应的条目就创建对应的客户端,并经行数据的读取和写入
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//连接服务器
connServer(devices.get(position));
}
});

}

/**
* 打开蓝牙设备
*/
public void open(View view) {
//不推荐
//bluetoothAdapter.enable();
//推荐
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), REQUEST_ENABLE);
}

/**
* 关闭蓝牙设备
*/
public void close(View view) {
bluetoothAdapter.disable();//关闭
//擦除页面数据
deviceNames.clear();
adapter.notifyDataSetChanged();
}

/**
* 扫描蓝牙设备
*/
public void scan(View view) {

if (bluetoothAdapter.isEnabled()) {
//先清除页面数据!
devices.clear();
deviceNames.clear();

//使用广播的方法去获取设备,这里就要动态创建广播,并进行接听了
//定义一个系统规定action的广播,
//当系统没扫描到一个蓝牙设备就会发送一条广播
// 当系统做完扫描后,系统会发送广播,你只需在广播接收者做好处理
bluetoothAdapter.startDiscovery();
} else {
Toast.makeText(this, "请先开启蓝牙", Toast.LENGTH_SHORT).show();
}
}

/**
* 让自身蓝牙设备可被发现
*/
public void found(View view) {
getMyBondedDevices();
if (bluetoothAdapter.isDiscovering()) {//如果蓝牙设置正在扫描
Toast.makeText(this, "正在扫描,别急", Toast.LENGTH_SHORT).show();
} else {
//这里可以设置显示自己蓝牙设备的时间,默认是300秒,也可以自定义单位是秒
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 600);
startActivityForResult(intent, REQUEST_DISCOVER_MYSELF);
}
}

/**
* 开启蓝牙服务的服务端
*/
boolean isServer = false;//默认是普通客户端
BlueServer server;//创建蓝牙服务器的对象,在服务器做相关操作

public void createServer(View view) {
server = new BlueServer(bluetoothAdapter);
server.start();
isServer = true;//设置为服务端
}

/**
* 发送数据
*/
public void send(View view) {
String et = et_send.getText().toString();//获取输入框的数据
//给另一端写入数据
write(et);
}

/**
* 数据的传递
*/
public void write(String msg) {
if (isServer) {//服务器的写数据
btn_send.setText("服务器");
handlerSendMessager(1, msg);
e("TAG", "----------(server != null && server.getRwStream() != null) " + (server != null && server.getRwStream() != null));
if (server != null && server.getRwStream() != null) {
server.getRwStream().write(msg);
}
} else {//客户端的写数据
btn_send.setText("客户端");
handlerSendMessager(2, msg);
if (client != null) {
client.write(msg);
}
}
}

/**
* 监听数据的接收
*/
public void listen(View view) {

//服务器的监听数据
if (server != null) {
server.getRwStream().setDataShow(new RWStream.DataShow() {
@Override
public void getMessager(final String message) {
//要在主线程中改变UI
Log.e("TAG", "-------listen---Service" + message);
handlerSendMessager(2, message);
}
});
//客户端的监听数据
} else if (client != null) {
client.setDataShow(new RWStream.DataShow() {
@Override
public void getMessager(final String message) {
//要在主线程中改变UI
e("TAG", "-------listen---client" + message);
handlerSendMessager(1, message);
}
});
}
}

/**
* Handler包装类
*/
private void handlerSendMessager(int what, String messge) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = messge;
handler.sendMessage(msg);
}

/**
* 创建Handler对象用于线程间通信
*/
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//显示数据在文本中
if (msg.what == 1) {
//服务器的数据在右边
tv_show_service.setTextColor(Color.RED);
tv_show_service.setTextSize(20);
tv_show_service.append(msg.obj + "\n");
} else {
//客户端的数据在左边
tv_show.setTextColor(Color.BLUE);
tv_show.setTextSize(20);
tv_show.append(msg.obj + "\n");
}

}
};

/**
* 客户端连接服务器
*/
RWStream client;

private void connServer(BluetoothDevice device) {
try {
//创建蓝牙客户端的Socket对象,这里是类BluetoothSocket,服务端是BluetoothServerSocket
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(uuid);
socket.connect();//连接Socket
//创建可读写的客户对象,传入Socket对象
client = new RWStream(socket);
//开始客户端的线程
client.start();
} catch (IOException e) {
e.printStackTrace();
}

}

/**
* 判断是否有蓝牙的权限,如果手机系统是6.0以上的就要动态创建权限
*/
private void checkPermission() {
if (Build.VERSION.SDK_INT >= 23) {
//23
int check = checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
if (check != PackageManager.PERMISSION_GRANTED) {
//请求权限
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_LOCATION);
}
}
}

/**
* 动态请求权限后,返回页面时的回调方法
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "权限已获取", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "权限未获取", Toast.LENGTH_SHORT).show();
finish();//关闭页面
}
}

/**
* 数据回调方法
*/
@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();
}
} else {

}
}

/**
* 获取已经绑定的蓝牙设置
*/
private void getMyBondedDevices() {
//获取所有已经绑定了的设备
deviceNames.clear();//清除设备名称的集合
devices.clear();//清除蓝牙设备对象的集合
if (bluetoothAdapter.getBondedDevices() != null) {//如果蓝牙适配器对象不为空时
//获取里面的数据的对象
List<BluetoothDevice> liset = new ArrayList<>(bluetoothAdapter.getBondedDevices());
devices.addAll(liset);
//拿到适配器对象的名称数据
for (BluetoothDevice device : liset) {
deviceNames.add(device.getName());
}
}
}

/**
* 广播的注册
*/
@Override
protected void onResume() {
super.onResume();
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
//添加蓝牙广播的Action
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(receiver, intentFilter);//注册广播接收者
}

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

/**
* 广播接收者的创建
*/
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//获取设备的发送的广播
if (intent.getAction().equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
Toast.makeText(context, "蓝牙扫描完毕", Toast.LENGTH_SHORT).show();
} else {
//获取蓝牙设置对象
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//把对象和名称放到对应的集合中
devices.add(device);
deviceNames.add(TextUtils.isEmpty(device.getName()) ? "未命名" : device.getName());
adapter.notifyDataSetChanged();
}
}
};

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
      上面程序运行后就可以完成蓝牙通信了,上面程序设计中显示多个按钮是为了蓝牙的使用示范,实际程序中,可以减少扫描按钮,比如程序运行就打开蓝牙,监听数据的按钮的方法也是可以嵌入到程序当中。

      本程序虽然可以进行简单通信,但是也是有一些bug的,比如没有按照上面的步骤操作,直接点击后面的按钮是会空指针的!逻辑处理没有完善。

      如果要传输文件,或其他数据都是可以的,那就要对输入流和输出流进行设置了。

      这里对Socket编程简单做几句介绍,其实Socket编程并不是很难理解。Socket编程一般需要一个服务端SocketServie和一个或多个客户端Socket,通过SocketService或Socket对象获取它的输入流实现对数据的读取,获取输出流实现对数据的写入。其中数据如果要一直读取就要写一个一直执行的子线程,让它一直在跑。

      而Tcp/Udp编程就要用到ip地址和端口号了。Socket编程可以结合Tcp使用网络。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: