首页 > 其他分享 >Android -传统蓝牙通信聊天

Android -传统蓝牙通信聊天

时间:2023-06-08 19:03:27浏览次数:57  
标签:Log BluetoothAdapter 蓝牙 TAG 聊天 Android 连接 socket


概述


Android 传统蓝牙的使用,包括开关蓝牙、搜索设备、蓝牙连接、通信等。


详细


Android 蓝牙开发(一)蓝牙通信 简书

一、准备工作


开发环境:

jdk1.8

Eclipse Luna Service Release 1 (4.4.1)

运行环境:

华为荣耀6(Android4.4)、华为p9(Android7.0)

实现功能:

  • Android 蓝牙开发 (开关蓝牙、搜索设备、蓝牙配对、连接、通信、断开连接等)。
二、代码结构

代码包里面,有两个部分,一个是源码,一个是V7支持包。

Android -传统蓝牙通信聊天_Android

三、程序实现-蓝牙通信

1 蓝牙基本操作

随着可穿戴设备的流行,研究蓝牙是必不可少的一门技术了。

总结了下蓝牙开发使用的一些东西分享一下。


蓝牙权限

首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。



<uses-permissionandroid:name="Android.permission.BLUETOOTH" />
//允许程序连接到已配对的蓝牙设备。
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
//允许程序发现和配对蓝牙设备。



BluetoothAdapter

操作蓝牙主要用到的类 BluetoothAdapter类,使用时导包
import android.bluetooth.BluetoothAdapter;
源码具体位置frameworks/base/core/Java/android/bluetooth/BluetoothAdapter.java

BluetoothAdapter 代表本地设备的蓝牙适配器。该BluetoothAdapter可以执行基本的蓝牙任务,例如启
动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并创建一个
BluetoothServerSocket监听来自其他设备的连接请求。

获取蓝牙适配器

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

开启蓝牙

if(!mBluetoothAdapter.isEnabled()){  
//弹出对话框提示用户是后打开  
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
startActivityForResult(enabler, REQUEST_ENABLE);  
      //不做提示,直接打开,不建议用下面的方法,有的手机会有问题。  
      // mBluetoothAdapter.enable();  
}

获取本地蓝牙信息

//获取本机蓝牙名称  
String name = mBluetoothAdapter.getName();  
//获取本机蓝牙地址  
String address = mBluetoothAdapter.getAddress();  
Log.d(TAG,"bluetooth name ="+name+" address ="+address);  
//获取已配对蓝牙设备  
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();  
Log.d(TAG, "bonded device size ="+devices.size());  
for(BluetoothDevice bonddevice:devices){  
    Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress());  
}

搜索设备

mBluetoothAdapter.startDiscovery();

停止搜索

mBluetoothAdapter.cancelDiscovery();

搜索蓝牙设备,该过程是异步的,通过下面注册广播接受者,可以监听是否搜到设备。

IntentFilter filter = new IntentFilter();  
//发现设备  
filter.addAction(BluetoothDevice.ACTION_FOUND);  
//设备连接状态改变  
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);  
//蓝牙设备状态改变  
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);  
registerReceiver(mBluetoothReceiver, filter);


监听扫描结果

通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUNDE)。其中参数intent可以获取蓝牙设备BluetoothDevice。

该demo中是连接指定名称的蓝牙设备,BLUETOOTH_NAME为"Galaxy Nexus",如果扫描不到,记得改这个蓝牙名称。

private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            String action = intent.getAction();  
            Log.d(TAG,"mBluetoothReceiver action ="+action);  
            if(BluetoothDevice.ACTION_FOUND.equals(action)){//每扫描到一个设备,系统都会发送此广播。  
                //获取蓝牙设备  
                BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
                if(scanDevice == null || scanDevice.getName() == null) return;  
                Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());  
                //蓝牙设备名称  
                String name = scanDevice.getName();  
                if(name != null && name.equals(BLUETOOTH_NAME)){  
                    mBluetoothAdapter.cancelDiscovery();  
                    //取消扫描  
                    mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting));                   //连接到设备。  
                    mBlthChatUtil.connect(scanDevice);  
                }  
            }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){  
  
            }
        }          
};

设置蓝牙可见性

有时候扫描不到某设备,这是因为该设备对外不可见或者距离远,需要设备该蓝牙可见,这样该才能被搜索到。

可见时间默认值为120s,最多可设置300。

if (mBluetoothAdapter.isEnabled()) {  
    if (mBluetoothAdapter.getScanMode() !=   
            BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {  
        Intent discoverableIntent = new Intent(  
                BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);  
        discoverableIntent.putExtra(  
                BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 120);  
        startActivity(discoverableIntent);  
    }  
}

2 服务端


SDP协议建立连接进行通信,通信方式类似于平常使用socket。

安全的RFCOMM Bluetooth socket,该连接是安全的需要进行配对。而通过listenUsingInsecureRfcommWithServiceRecord创建的RFCOMM Bluetooth socket是不安全的,连接时不需要进行配对。

uuid需要服务器端和客户端进行统一。

private class AcceptThread extends Thread {  
        // 本地服务器套接字  
        private final BluetoothServerSocket mServerSocket;  
        public AcceptThread() {           
            BluetoothServerSocket tmp = null;  
            // 创建一个新的侦听服务器套接字  
            try {  
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(  
                        SERVICE_NAME, SERVICE_UUID);  
                //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);  
            } catch (IOException e) {  
                Log.e(TAG, "listen() failed", e);  
            }  
            mServerSocket = tmp;  
        }  
  
        public void run() {  
            BluetoothSocket socket = null;  
            // 循环,直到连接成功  
            while (mState != STATE_CONNECTED) {  
                try {  
                    // 这是一个阻塞调用 返回成功的连接  
                    // mServerSocket.close()在另一个线程中调用,可以中止该阻塞  
                    socket = mServerSocket.accept();  
                } catch (IOException e) {  
                    Log.e(TAG, "accept() failed", e);  
                    break;  
                }  
                // 如果连接被接受  
                if (socket != null) {  
                    synchronized (BluetoothChatUtil.this) {  
                        switch (mState) {  
                        case STATE_LISTEN:  
                        case STATE_CONNECTING:  
                            // 正常情况。启动ConnectedThread。  
                            connected(socket, socket.getRemoteDevice());  
                            break;  
                        case STATE_NONE:  
                        case STATE_CONNECTED:  
                            // 没有准备或已连接。新连接终止。  
                            try {  
                                socket.close();  
                            } catch (IOException e) {  
                                Log.e(TAG, "Could not close unwanted socket", e);  
                            }  
                            break;  
                        }  
                    }  
                }  
            }  
            if (D) Log.i(TAG, "END mAcceptThread");  
        }  
  
        public void cancel() {  
            if (D) Log.d(TAG, "cancel " + this);  
            try {  
                mServerSocket.close();  
            } catch (IOException e) {  
                Log.e(TAG, "close() of server failed", e);  
            }  
        }  
}

mServerSocket通过accept()等待客户端的连接(阻塞),直到连接成功或失败。


3 客户端


客户端主要用来创建RFCOMM socket,并连接服务端。

mBlthChatUtil.connect(scanDevice)连接到设备,

连接过程主要在ConnectThread线程中进行,先创建socket,方式有两种,

安全的(createRfcommSocketToServiceRecord)。另一种不安全连接对应的函数是createInsecureRfcommSocketToServiceRecord。

private class ConnectThread extends Thread {  
        private BluetoothSocket mmSocket;  
        private final BluetoothDevice mmDevice;  
        public ConnectThread(BluetoothDevice device) {  
            mmDevice = device;  
            BluetoothSocket tmp = null;  
            // 得到一个bluetoothsocket  
            try {  
                mmSocket = device.createRfcommSocketToServiceRecord  
                        (SERVICE_UUID);  
            } catch (IOException e) {  
                Log.e(TAG, "create() failed", e);  
                mmSocket = null;  
            }  
        }  
  
        public void run() {  
            Log.i(TAG, "BEGIN mConnectThread");  
            try {   
                // socket 连接,该调用会阻塞,直到连接成功或失败  
                mmSocket.connect();  
            } catch (IOException e) {  
                connectionFailed();  
                try {//关闭这个socket  
                    mmSocket.close();  
                } catch (IOException e2) {  
                    e2.printStackTrace();  
                }  
                return;  
            }  
            // 启动连接线程  
            connected(mmSocket, mmDevice);  
        }  
  
        public void cancel() {  
            try {  
                mmSocket.close();  
            } catch (IOException e) {  
                Log.e(TAG, "close() of connect socket failed", e);  
            }  
        }  
}

接着客户端socket主动连接服务端。连接过程中会自动进行配对,需要双方同意才可以连接成功。


4 数据传输

客户端与服务端连接成功后都会调用connected(mmSocket, mmDevice),创建一个ConnectedThread线程()。

该线程主要用来接收和发送数据。客户端和服务端处理方式一样。该线程通过socket获得输入输出流。

private InputStream mmInStream = socket.getInputStream();

private OutputStream mmOutStream =socket.getOutputStream();

发送数据

public void write(byte[] buffer) {  
    try {  
        mmOutStream.write(buffer);  
        // 分享发送的信息到Activity  
        mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)  
                .sendToTarget();  
    } catch (IOException e) {  
        Log.e(TAG, "Exception during write", e);  
    }  
}

接收数据

线程循环进行接收数据。

public void run() {  
    // 监听输入流  
    while (true) {  
        try {  
            byte[] buffer = new byte[1024];  
            // 读取输入流  
            int bytes = mmInStream.read(buffer);  
            // 发送获得的字节的ui activity  
            Message msg = mHandler.obtainMessage(MESSAGE_READ);  
            Bundle bundle = new Bundle();  
            bundle.putByteArray(READ_MSG, buffer);  
            msg.setData(bundle);  
            mHandler.sendMessage(msg);            
        } catch (IOException e) {  
            Log.e(TAG, "disconnected", e);  
                connectionLost();  
                break;  
            }  
        }  
}


四、运行效果

1、运行,右键项目:Run as -》Android Application (备注:Eclipse需要配置Android开发环境)

2、运行效果如下:


客户端

Android -传统蓝牙通信聊天_传统蓝牙通信聊天_02


服务端

Android -传统蓝牙通信聊天_客户端_03



标签:Log,BluetoothAdapter,蓝牙,TAG,聊天,Android,连接,socket
From: https://blog.51cto.com/u_7583030/6442277

相关文章

  • 智能座舱之蓝牙模块测试要点
    智能座舱蓝牙模块是车辆中的一项重要功能,用于提供与移动设备的无线连接,例如手机、音乐播放器等。下面是对智能座舱蓝牙模块测试的详细介绍:1.连接和配对测试测试蓝牙模块的连接性能,验证其能够与各种类型的设备进行配对和连接。验证蓝牙模块的连接稳定性,包括在连接过程中的自......
  • Android问题解决:android.util.Base64.encode 导致签名不匹配 SignatureDoesNotMatch
    文章目录前文:遇到问题一问:为什么SignatureDoesNotMatch二问:为什么SignatureDoesNotMatch三问:Signature请求参数为什么多了%0A四问:Signature为什么多了换行五问:android.util.Base64.encode的用法前文:遇到问题在折腾《ESP32-C3入门教程——导读》时,需要对接阿里云物联网平台。想要......
  • 使用 ADB 命令为 Windows Subsystem Android(WSA)配置代理
    注意!以下命令需在命令提示符中执行,不要使用PowerShell,会有字符错误!设置代理:adbconnect127.0.0.1:58526&&adbshell"settingsputglobalhttp_proxy`iproutelistmatch0tableallscopeglobal|cut-F3`:7890"中间一大段会自动识别宿主机ip注意修改7890端口......
  • android-脱离AndroidStudio使用AVD
    android-脱离AndroidStudio使用AVD实际上,AVD不需要AndroidStudio图形界面也能独立运行。直接启动模拟器的方法是在终端中输入emulator-avd模拟器名称。默认情况下,emulator程序在C:\Users\users\AppData\Local\Android\Sdk\emulator\文件夹中。进入此文件夹,为emulator.exe......
  • Android获取当前连接的wifi名称
    首先AndroidMainfest.xml文件里加入权限: <uses-permissionandroid:name="android.permission.ACCESS_NETWORK_STATE"/><!--获取WIFI信息状态的权限--><uses-permissionandroid:name="android.permission.ACCESS_WIFI_STATE"/><!--获取网络状态改......
  • Android开发 ViewDragHelper使用讲解
     前言  ViewDragHelper需要自定义ViewGroup实现,并且只是针对ViewGroup里的子View进行拖放,在拖放的过程中不能携带数据。也不能跨进程,甚至不能跨activity。所以ViewDragHelper本质上更像是一个ViewGroup里简单实现拖放效果的帮助类。 一个简单拖动的例子  快速了解一下,......
  • CodeGeeX 2.0版本重大升级:通过聊天对话的方式直接操作代码
    CodeGeeX2.0版本正式上线!从命名上看这是一次大版本的升级。上个月,CodeGeeX在VSCode和JetBrainsIDEs的插件中,加入了智能问答(AskCodeGeeX)功能,让用户可以在IDE中通过问答对话的方式解决技术问题。本周,这一功能全新升级!在CodeGeeX2.0正式版中,将问答与IDE编程环境深度融合,可以通过......
  • 一次简单的蓝牙相关安卓代码逆向记录
    前言本来工作方面和安卓根本没任何交集,把这个过程记录下来,只是一个小总结。涉及到的知识点有,安卓逆向,Smali修改,安卓apk签名,蓝牙连接,ADB。基本需求手里有一个设备,是支持双模蓝牙的。也就是经典蓝牙使用的名称和MAC地址,和低功耗蓝牙使用的是一样的。之前其他人开发过一个......
  • 即时通信聊天工具的原理与设计
     该软件采用P2P方式,各个客户端之间直接发消息进行会话聊天,服务器在其中只扮演协调者的角色(混合型P2P)。1.会话流程设计     当一个新用户通过自己的客户端登陆系统后,从服务器获取当前在线的用户信息列表,列表信息包括了系统中每个用户的地址。用户就可以开始独立工作,自主地......
  • 语音聊天室源码技术美颜滤镜功能的配置
    爱美之心人皆有之,从古至今,大部分人都希望自己的容颜相貌完美无缺,都希望自己会被别人夸赞自己长得漂亮或是英俊,但是,容貌是天生的,是父母给的,就算是不太好看我们也只能去接受。随着科技的发展,有一个功能的出现,虽然不能从我们自身将我们的容貌改造变好,但是在拍照或是上网视频时可以将我......