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

发布时间 - 2026-01-10 21:50:40    点击率:

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

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

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

1.打开和关闭蓝牙

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

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

<uses-permission android:name="android.permission.BLUETOOTH" />
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);

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

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

在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();

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();

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

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

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

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


# android蓝牙开发  # Android蓝牙技术  # android  # 蓝牙  # 如何正确实现Android启动屏画面的方法(避免白屏)  # Android编程之简单启动画面实现方法  # Android简单实现启动画面的方法  # Android编程中调用Camera时预览画面有旋转问题的解决方法  # Android启动画面的实现方法  # Android开机画面的具体修改方法  # android Socket实现简单聊天功能以及文件传输  # Android实时获取摄像头画面传输至PC端思路详解  # 服务端  # 客户端  # 就会  # 对话框  # 链路  # 种方法  # 是一个  # 有很多  # 弹出  # 就先  # 完后  # 创建一个  # 的是  # 都是  # 另一台  # 我就  # 也不  # 来了  # 也有  # 是一种 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 制作电商网页,电商供应链怎么做?  南京网站制作费用,南京远驱官方网站?  HTML5打空格有哪些误区_新手常犯的空格使用错误【技巧】  Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程  如何用搬瓦工VPS快速搭建个人网站?  公司网站制作需要多少钱,找人做公司网站需要多少钱?  深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?  高端企业智能建站程序:SEO优化与响应式模板定制开发  北京的网站制作公司有哪些,哪个视频网站最好?  如何为不同团队 ID 动态生成多个非值班状态按钮  如何在万网ECS上快速搭建专属网站?  制作旅游网站html,怎样注册旅游网站?  Laravel如何实现事件和监听器?(Event & Listener实战)  如何在橙子建站上传落地页?操作指南详解  如何确保FTP站点访问权限与数据传输安全?  如何获取PHP WAP自助建站系统源码?  北京网页设计制作网站有哪些,继续教育自动播放怎么设置?  Laravel怎么实现一对多关联查询_Laravel Eloquent模型关系定义与预加载【实战】  东莞专业网站制作公司有哪些,东莞招聘网站哪个好?  Laravel如何使用Gate和Policy进行授权?(权限控制)  Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案  如何在阿里云香港服务器快速搭建网站?  Android自定义控件实现温度旋转按钮效果  Laravel如何使用.env文件管理环境变量?(最佳实践)  如何快速生成可下载的建站源码工具?  阿里云高弹*务器配置方案|支持分布式架构与多节点部署  如何在万网自助建站平台快速创建网站?  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  HTML透明颜色代码怎么让下拉菜单透明_下拉菜单透明背景指南【技巧】  UC浏览器如何设置启动页 UC浏览器启动页设置方法  javascript如何操作浏览器历史记录_怎样实现无刷新导航  linux写shell需要注意的问题(必看)  C语言设计一个闪闪的圣诞树  Laravel如何使用Vite进行前端资源打包?(配置示例)  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  C++用Dijkstra(迪杰斯特拉)算法求最短路径  手机网站制作与建设方案,手机网站如何建设?  长沙企业网站制作哪家好,长沙水业集团官方网站?  Laravel怎么配置不同环境的数据库_Laravel本地测试与生产环境动态切换【方法】  Laravel Eloquent模型如何创建_Laravel ORM基础之Model创建与使用教程  Laravel N+1查询问题如何解决_Eloquent预加载(Eager Loading)优化数据库查询  安克发布新款氮化镓充电宝:体积缩小 30%,支持 200W 输出  新三国志曹操传主线渭水交兵攻略  太平洋网站制作公司,网络用语太平洋是什么意思?  奇安信“盘古石”团队突破 iOS 26.1 提权  Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】  如何在万网主机上快速搭建网站?  JS经典正则表达式笔试题汇总  浅述节点的创建及常见功能的实现  百度浏览器网页无法复制文字怎么办 百度浏览器复制修复