android中的无线通信蓝牙

一.蓝牙基础知识

      蓝牙(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状态值 ,即扫描状态

  1. 无功能状态 : int SCAN_MODE_NONE //值为20, 查询扫描和页面扫描都失效,
    该状态下蓝牙模块既不能扫描其它设备, 也不可见;
  2. 扫描状态 : int SCAN_MODE_CONNECTABLE //值为21, 查询扫描失效, 页面扫描有效,
    该状态下蓝牙模块可以扫描其它设备, 从可见性来说只对已配对的蓝牙设备可见, 只有配对的设备才能主动连接本设备;
  3. 可见状态 : 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 ()
     功能: 取消扫描过程。
     注意: 如果蓝牙没有开启,该方法会返回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通信示例

      程序实现两台手机的蓝牙发送消息,并接受消息。这里的蓝牙是不需要网络支持的。
程序运行后,扫描蓝牙后的界面:
s1
程序进行通信后的界面,要两个手机哦!
t1
      两个手机,其中一个设置为服务器,然后点击连接的手机,就可以进行消息发送和接收了。
上面只是实现文本通信,文本也只是进行简单处理。

程序设计代码:

(一)添加权限

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

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

(二)布局文件的设计


<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 devices = new ArrayList<>();
    //设备的名称集合
    ArrayList 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 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使用网络。

你可能感兴趣的:(android中的无线通信蓝牙)