Android - 传统蓝牙通讯聊天

Android -传统蓝牙通讯聊天

技术:java+Android4.4+jdk1.8

概述

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

详细

原文地址html

Android 蓝牙开发(一)蓝牙通讯 CSDNjava

Android 蓝牙开发(一)蓝牙通讯 简书android

1、准备工做

 

开发环境:安全

    jdk1.8 服务器

    Eclipse Luna Service Release 1 (4.4.1)异步

运行环境:socket

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

实现功能:函数

  • Android 蓝牙开发 (开关蓝牙、搜索设备、蓝牙配对、链接、通讯、断开链接等)。ui

2、代码结构

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

屏幕快照 2017-07-20 下午5.54.54.png

3、程序实现-蓝牙通讯

1 蓝牙基本操做

 

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

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

 

蓝牙权限

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

1
2
3
4
< 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监听来自其余设备的链接请求。

获取蓝牙适配器

1
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

开启蓝牙

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

 

获取本地蓝牙信息

1
2
3
4
5
6
7
8
9
10
11
//获取本机蓝牙名称  
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());  
}

搜索设备

1
mBluetoothAdapter.startDiscovery();

 

中止搜索

1
mBluetoothAdapter.cancelDiscovery();

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

1
2
3
4
5
6
7
8
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",若是扫描不到,记得改这个蓝牙名称。

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

 

1
2
3
4
5
6
7
8
9
10
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 服务端

 

 

android 蓝牙之间能够经过SDP协议创建链接进行通讯,通讯方式相似于日常使用socket。

首先建立BluetoothServerSocket ,BluetoothAdapter中提供了两种建立BluetoothServerSocket 方式,以下图所示为建立安全的RFCOMM Bluetooth socket,该链接是安全的须要进行配对。而经过listenUsingInsecureRfcommWithServiceRecord建立的RFCOMM Bluetooth socket是不安全的,链接时不须要进行配对。

其中的uuid须要服务器端和客户端进行统一。

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

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

发送数据

1
2
3
4
5
6
7
8
9
10
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);  
     }  
}

 

接收数据

 

线程循环进行接收数据。

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

 

4、运行效果

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

二、运行效果以下: 

 

客户端 

1500550538898087682.jpeg

 

服务端

Screenshot_2017-07-20-19-26-57.png

 

出处:http://www.demodashi.com/demo/10676.html

相关文章
相关标签/搜索