欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解Android——蓝牙技术 带你实现终端间数据传输

程序员文章站 2024-02-25 18:34:03
蓝牙技术在智能硬件方面有很多用武之地,今天我就为大家分享一下蓝牙在android系统下的使用方法技巧,并实现一下两个终端间数据的传输。 蓝牙(bluetooth)是一种短...

蓝牙技术在智能硬件方面有很多用武之地,今天我就为大家分享一下蓝牙在android系统下的使用方法技巧,并实现一下两个终端间数据的传输。

蓝牙(bluetooth)是一种短距离的无线通信技术标准,蓝牙协议分为4层,即核心协议层、电缆替代协议层、电话控制协议层和采纳的其它协议层。

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

1.打开和关闭蓝牙

第一种方法相对简单,直接调用系统对话框启动蓝牙:

在androidmanifest文件中添加需要的权限,高版本也不需要动态授权:

<uses-permission android:name="android.permission.bluetooth" />
startactivityforresult(new intent(bluetoothadapter.action_request_enable), 1);

详解Android——蓝牙技术 带你实现终端间数据传输

如果不想让用户看到这个对话框,那么我们还可以选择第二种方法,进行静默开启蓝牙。

第二种方法,静默开启,不会有方法一的对话框:

在androidmanifest文件中添加需要的权限:

<!-- 已适配android6.0 -->
<uses-permission android:name="android.permission.bluetooth" />
<uses-permission android:name="android.permission.bluetooth_admin" />
<uses-permission android:name="android.permission.access_fine_location" /> 
<uses-permission android:name="android.permission.access_coarse_location" /> 
<uses-feature
  android:name="android.hardware.bluetooth_le"
  android:required="true" />

由于蓝牙所需要的权限包含dangerous permissions,所以我们需要在java代码中进行动态授权处理:

private static final int request_bluetooth_permission=10;

private void requestbluetoothpermission(){
  //判断系统版本
  if (build.version.sdk_int >= 23) {
    //检测当前app是否拥有某个权限
    int checkcallphonepermission = contextcompat.checkselfpermission(this, 
        manifest.permission.access_coarse_location);
    //判断这个权限是否已经授权过
    if(checkcallphonepermission != packagemanager.permission_granted){
      //判断是否需要 向用户解释,为什么要申请该权限
      if(activitycompat.shouldshowrequestpermissionrationale(this, 
          manifest.permission.access_coarse_location))
        toast.maketext(this,"need bluetooth permission.", 
            toast.length_short).show();
      activitycompat.requestpermissions(this ,new string[]
          {manifest.permission.access_coarse_location},request_bluetooth_permission);
      return;
    }else{
    }
  } else {
  }
}

接下来我们就可以静默开启蓝牙了:

bluetoothadapter mbluetoothadapter = bluetoothadapter.getdefaultadapter();
mbluetoothadapter.enable(); //开启
//mbluetoothadapter.disable(); //关闭

下面我们来看一下如何通过代码搜索蓝牙设备。

2.通过代码搜索蓝牙设备

搜索分为主动搜索和被动搜索。

我们开始进行主动搜索:

(1)创建bluetoothadapter对象

textview tvdevices = (textview)findviewbyid(r.id.tv_devices);
bluetoothadapter mbluetoothadapter = bluetoothadapter.getdefaultadapter();

(2)我们先获取并显示一下已经配对的蓝牙设备列表

//获取已经配对的蓝牙设备
set<bluetoothdevice> paireddevices = mbluetoothadapter.getbondeddevices();
if (paireddevices.size() > 0) {
  for (bluetoothdevice device : paireddevices) {
    tvdevices.append(device.getname() + ":" + device.getaddress());
  }
}

(3)下面我们定义广播接收器

// 设置广播信息过滤
intentfilter filter = new intentfilter();
filter.addaction(bluetoothdevice.action_found);//每搜索到一个设备就会发送一个该广播
filter.addaction(bluetoothadapter.action_discovery_finished);//当全部搜索完后发送该广播
filter.setpriority(integer.max_value);//设置优先级
// 注册蓝牙搜索广播接收者,接收并处理搜索结果
this.registerreceiver(receiver, filter);

蓝牙设备的广播接收器如下:

/**
 * 定义广播接收器
 */
private final broadcastreceiver receiver = new broadcastreceiver() {
  @override
  public void onreceive(context context, intent intent) {
    string action = intent.getaction();
    if (bluetoothdevice.action_found.equals(action)) {
      bluetoothdevice device = intent.getparcelableextra(bluetoothdevice.extra_device);
      if (device.getbondstate() != bluetoothdevice.bond_bonded) {
        tvdevices.append(device.getname() + ":"+ device.getaddress());
      }
    } else if (bluetoothadapter.action_discovery_finished.equals(action)) {
      //已搜素完成
    }
  }
};

(4)我们创建一个button按钮,当点击button时进行搜索,button点击事件如下:

//如果当前在搜索,就先取消搜索
if (mbluetoothadapter.isdiscovering()) {
  mbluetoothadapter.canceldiscovery();
}
//开启搜索
mbluetoothadapter.startdiscovery();

详解Android——蓝牙技术 带你实现终端间数据传输

3.蓝牙的uuid

两个蓝牙设备进行连接时需要使用同一个uuid。但很多读者可能发现,有很多型号的手机(可能是非android系统的手机)之间使用了不同的程序也可以使用蓝牙进行通讯。从表面上看,它们之间几乎不可能使用同一个uuid。

uuid的格式如下:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

uuid的格式被分成5段,其中中间3段的字符数相同,都是4,第1段是8个字符,最后一段是12个字符。所以uuid实际上是一个8-4-4-4-12的字符串。

实际上,uuid和tcp的端口一样,也有一些默认的值。例如,将蓝牙模拟成串口的服务就使用了一个标准的uuid:

00001101-0000-1000-8000-00805f9b34fb

除此之外,还有很多标准的uuid,如下面就是两个标准的uuid:

信息同步服务:00001104-0000-1000-8000-00805f9b34fb

文件传输服务:00001106-0000-1000-8000-00805f9b34fb

4.蓝牙终端间数据传输

通过蓝牙传输数据与socket类似。在网络中使用socket和serversocket控制客户端和服务端的数据读写。而蓝牙通讯也由客户端和服务端socket来完成。蓝牙客户端socket是bluetoothsocket,蓝牙服务端socket是bluetoothserversocket。这两个类都在android.bluetooth包中。

无论是bluetoothsocket,还是bluetoothserversocket,都需要一个uuid(全局唯一标识符,universally unique identifier),uuid相当于socket的端口,而蓝牙地址相当于socket的ip。

我们开始进行模拟一个蓝牙数据的传输:

首先来看客户端:

(1)定义全局常量变量

private listview lvdevices;
private bluetoothadapter mbluetoothadapter;
private list<string> bluetoothdevices = new arraylist<string>();
private arrayadapter<string> arrayadapter;
private final uuid my_uuid = uuid
    .fromstring("abcd1234-ab12-ab12-ab12-abcdef123456");//随便定义一个
private bluetoothsocket clientsocket;
private bluetoothdevice device; 
private outputstream os;//输出流

(2)在oncreate方法中做初始化操作

mbluetoothadapter = bluetoothadapter.getdefaultadapter();

lvdevices = (listview) findviewbyid(r.id.lv_devices);
//获取已经配对的蓝牙设备
set<bluetoothdevice> paireddevices = mbluetoothadapter.getbondeddevices();
if (paireddevices.size() > 0) {
  for (bluetoothdevice device : paireddevices) {
    bluetoothdevices.add(device.getname() + ":"+ device.getaddress());
  }
}
arrayadapter = new arrayadapter<string>(this,
    android.r.layout.simple_list_item_1, android.r.id.text1,bluetoothdevices);
lvdevices.setadapter(arrayadapter);
lvdevices.setonitemclicklistener(this);//activity实现onitemclicklistener接口

//每搜索到一个设备就会发送一个该广播
intentfilter filter = new intentfilter(bluetoothdevice.action_found);
this.registerreceiver(receiver, filter);
//当全部搜索完后发送该广播
filter = new intentfilter(bluetoothadapter.action_discovery_finished);
this.registerreceiver(receiver, filter);

蓝牙设备的广播接收器如下:

/**
 * 定义广播接收器
 */
private final broadcastreceiver receiver = new broadcastreceiver() {
  @override
  public void onreceive(context context, intent intent) {
    string action = intent.getaction();
    if (bluetoothdevice.action_found.equals(action)) {
      bluetoothdevice device = intent.getparcelableextra(bluetoothdevice.extra_device);
      if (device.getbondstate() != bluetoothdevice.bond_bonded) {
        bluetoothdevices.add(device.getname() + ":" + device.getaddress());
        arrayadapter.notifydatasetchanged();//更新适配器
      }

    } else if (bluetoothadapter.action_discovery_finished.equals(action)) {
      //已搜素完成
    }
  }
};

(4)我们创建一个button按钮,当点击button时进行搜索,button点击事件如下:

//如果当前在搜索,就先取消搜索
if (mbluetoothadapter.isdiscovering()) {
  mbluetoothadapter.canceldiscovery();
}
//开启搜索
mbluetoothadapter.startdiscovery();

(5)接下来我们设置列表的点击事件:

@override
public void onitemclick(adapterview<?> parent, view view, int position, long id) {
  string s = arrayadapter.getitem(position);
  string address = s.substring(s.indexof(":") + 1).trim();//把地址解析出来
  //主动连接蓝牙服务端
  try {
    //判断当前是否正在搜索
    if (mbluetoothadapter.isdiscovering()) {
      mbluetoothadapter.canceldiscovery();
    }
    try {
      if (device == null) {
        //获得远程设备
        device = mbluetoothadapter.getremotedevice(address);
      }
      if (clientsocket == null) {
        //创建客户端蓝牙socket
        clientsocket = device.createrfcommsockettoservicerecord(my_uuid);
        //开始连接蓝牙,如果没有配对则弹出对话框提示我们进行配对
        clientsocket.connect();
        //获得输出流(客户端指向服务端输出文本)
        os = clientsocket.getoutputstream();
      }
    } catch (exception e) {
    }
    if (os != null) {
      //往服务端写信息
      os.write("蓝牙信息来了".getbytes("utf-8"));
    }
  } catch (exception e) {
  }
}

接下来看服务端:

服务端使用的是另一部手机,接受上面手机通过蓝牙发送过来的信息并显示。

(1)定义全局常量变量:

private bluetoothadapter mbluetoothadapter;
private acceptthread acceptthread;
private final uuid my_uuid = uuid
    .fromstring("abcd1234-ab12-ab12-ab12-abcdef123456");//和客户端相同的uuid
private final string name = "bluetooth_socket";
private bluetoothserversocket serversocket;
private bluetoothsocket socket;
private inputstream is;//输入流

(2)定义服务端线程类:

private handler handler = new handler() {
  public void handlemessage(message msg) {
    toast.maketext(getapplicationcontext(), string.valueof(msg.obj),
        toast.length_long).show();
    super.handlemessage(msg);
  }
};

//服务端监听客户端的线程类
private class acceptthread extends thread {
  public acceptthread() {
    try {
      serversocket = mbluetoothadapter.listenusingrfcommwithservicerecord(name, my_uuid);
    } catch (exception e) {
    }
  }
  public void run() {
    try {
      socket = serversocket.accept();
      is = socket.getinputstream();
      while(true) {
        byte[] buffer =new byte[1024];
        int count = is.read(buffer);
        message msg = new message();
        msg.obj = new string(buffer, 0, count, "utf-8");
        handler.sendmessage(msg);
      }
    }
    catch (exception e) {
    }
  }
}

(3)在oncreate方法中初始化线程类并开启

mbluetoothadapter = bluetoothadapter.getdefaultadapter();
acceptthread = new acceptthread();
acceptthread.start();

我们运行程序看一下效果图:

详解Android——蓝牙技术 带你实现终端间数据传输

点击“搜索蓝牙设备”按钮,就会搜索到另一台手机的蓝牙信息,我们点击条目,另一台手机会出现如下变化:

详解Android——蓝牙技术 带你实现终端间数据传输

弹出toast,此时证明我们的蓝牙数据已经传输过来了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。