Android 蓝牙开发之搜索、配对、连接、通信大全

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/qq_25827845/article/details/52997523

 

        蓝牙( Bluetooth®):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据

交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。蓝牙设备最多可以同时和7个其它蓝牙设备建立连接,进

行通信,当然并不是每一个蓝牙都可以达到最大值。下面,我们从蓝牙的基本概念开始,一步一步开始了解蓝牙。

 

(尊重劳动成果,转载请注明出处http://blog.csdn.net/qq_25827845/article/details/52997523

 源码下载地址:https://github.com/chaohuangtianjie994/BlueTooth-AutoPair


基本概念: 

         安卓平台提供对蓝牙的通讯栈的支持,允许设别和其他的设备进行无线传输数据。应用程序层通过安卓API来调用蓝牙的相关功

能,这些API使程序无线连接到蓝牙设备,并拥有P2P或者多端无线连接的特性。

 

蓝牙的功能:

1、扫描其他蓝牙设备

2、为可配对的蓝牙设备查询蓝牙适配器

3、建立RFCOMM通道

4、通过服务搜索来链接其他的设备

5、与其他的设备进行数据传输

6、管理多个连接



蓝牙建立连接必须要求:

1、打开蓝牙

2、查找附近已配对或可用设备

3、连接设备

4、设备间数据交换

 

 

常用的蓝牙API如下:

 

BluetoothAdapter

代表本地蓝牙适配器(蓝牙无线电)。BluetoothAdapter是所有蓝牙交互的入口。使用这个你可以发现其他蓝牙设备,查询已配对的设备列表,使用一个已知的MAC地址来实例化一个BluetoothDevice,以及创建一个BluetoothServerSocket来为监听与其他设备的通信。

 

BlueDevice代表一个远程蓝牙设备,使用这个来请求一个与远程设备的BluetoothSocket连接,或者查询关于设备名称、地址、类和连接状态等设备信息。
BluetoothSocket代表一个蓝牙socket的接口(和TCP Socket类似)。这是一个连接点,它允许一个应用与其他蓝牙设备通过InputStream和OutputStream交换数据。
BluetoothServerSocket代表一个开放的服务器socket,它监听接受的请求(与TCP ServerSocket类似)。为了连接两台Android设备,一个设备必须使用这个类开启一个服务器socket。当一个远程蓝牙设备开始一个和该设备的连接请求,BluetoothServerSocket将会返回一个已连接的BluetoothSocket,接受该连接。

 

BluetoothAdapter 中常用方法如下所示:

booleancancelDiscovery()
Cancel the current device discovery process.
static booleancheckBluetoothAddress(String address)
Validate a String Bluetooth address, such as "00:43:A8:23:10:F0"

Alphabetic characters must be uppercase to be valid.

voidcloseProfileProxy(int profile, BluetoothProfile proxy)
Close the connection of the profile proxy to the Service.
booleandisable()
Turn off the local Bluetooth adapter—do not use without explicit user action to turn off Bluetooth.
booleanenable()
Turn on the local Bluetooth adapter—do not use without explicit user action to turn on Bluetooth.
StringgetAddress()
Returns the hardware address of the local Bluetooth adapter.
Set<BluetoothDevice>getBondedDevices()
Return the set of   BluetoothDevice  objects that are bonded (paired) to the local adapter.
synchronized static BluetoothAdaptergetDefaultAdapter()
Get a handle to the default local Bluetooth adapter.
StringgetName()
Get the friendly Bluetooth name of the local Bluetooth adapter.
intgetProfileConnectionState(int profile)
Get the current connection state of a profile.
booleangetProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile)
Get the profile proxy object associated with the profile.
BluetoothDevicegetRemoteDevice(byte[] address)
Get a   BluetoothDevice  object for the given Bluetooth hardware address.
BluetoothDevicegetRemoteDevice(String address)
Get a   BluetoothDevice  object for the given Bluetooth hardware address.
intgetScanMode()
Get the current Bluetooth scan mode of the local Bluetooth adapter.
intgetState()
Get the current state of the local Bluetooth adapter.
booleanisDiscovering()
Return true if the local Bluetooth adapter is currently in the device discovery process.
booleanisEnabled()
Return true if Bluetooth is currently enabled and ready for use.
BluetoothServerSocketlistenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
BluetoothServerSocketlistenUsingRfcommWithServiceRecord(String name, UUID uuid)
Create a listening, secure RFCOMM Bluetooth socket with Service Record.
booleansetName(String name)
Set the friendly Bluetooth name of the local Bluetooth adapter.
booleanstartDiscovery()
Start the remote device discovery process.
booleanstartLeScan(BluetoothAdapter.LeScanCallback callback)
Starts a scan for Bluetooth LE devices.
booleanstartLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
Starts a scan for Bluetooth LE devices, looking for devices that advertise given services.
voidstopLeScan(BluetoothAdapter.LeScanCallback callback)
Stops an ongoing Bluetooth LE device scan.

 

BluetoothDevice 中常用方法如下所示:

BluetoothGattconnectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
Connect to GATT Server hosted by this device.
booleancreateBond()
Start the bonding (pairing) process with the remote device.
BluetoothSocketcreateInsecureRfcommSocketToServiceRecord(UUID uuid)
Create an RFCOMM   BluetoothSocket  socket ready to start an insecure outgoing connection to this remote device using SDP lookup of uuid.
BluetoothSocketcreateRfcommSocketToServiceRecord(UUID uuid)
Create an RFCOMM   BluetoothSocket  ready to start a secure outgoing connection to this remote device using SDP lookup of uuid.
intdescribeContents()
Describe the kinds of special objects contained in this Parcelable's marshalled representation.
booleanequals(Object o)
Compares this instance with the specified object and indicates if they are equal.
booleanfetchUuidsWithSdp()
Perform a service discovery on the remote device to get the UUIDs supported.
StringgetAddress()
Returns the hardware address of this BluetoothDevice.
BluetoothClassgetBluetoothClass()
Get the Bluetooth class of the remote device.
intgetBondState()
Get the bond state of the remote device.
StringgetName()
Get the friendly Bluetooth name of the remote device.
intgetType()
Get the Bluetooth device type of the remote device.
ParcelUuid[]getUuids()
Returns the supported features (UUIDs) of the remote device.
inthashCode()
Returns an integer hash code for this object.
booleansetPairingConfirmation(boolean confirm)
Confirm passkey for   PAIRING_VARIANT_PASSKEY_CONFIRMATION  pairing.
booleansetPin(byte[] pin)
Set the pin during pairing when the pairing method is   PAIRING_VARIANT_PIN

Requires BLUETOOTH_ADMIN.

StringtoString()
Returns a string representation of this BluetoothDevice.
voidwriteToParcel(Parcel out, int flags)
Flatten this object in to a Parcel.

 

BluetoothSocket 中常用方法如下所示:

voidclose()
Closes the object and release any system resources it holds.
voidconnect()
Attempt to connect to a remote device.
InputStreamgetInputStream()
Get the input stream associated with this socket.
OutputStreamgetOutputStream()
Get the output stream associated with this socket.
BluetoothDevicegetRemoteDevice()
Get the remote device this socket is connecting, or connected, to.
booleanisConnected()
Get the connection status of this socket, ie, whether there is an active connection with remote device.

 

BluetoothServerSocket 中常用方法如下所示:

BluetoothSocketaccept(int timeout)
Block until a connection is established, with timeout.
BluetoothSocketaccept()
Block until a connection is established.
voidclose()
Immediately close this socket, and release all associated resources.

 

 以上四个类贯穿于我们蓝牙通信的全过程,包括蓝牙搜索、配对、连接以及通信。

 

 

使用蓝牙需要在配置文件Androidmanifest.xml 中注册两种权限:

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

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

其中,权限1在得到默认蓝牙适配器时需要,即BluetoothAdapter  mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter( )

权限2在mBluetoothAdapter.enable( )或者mBluetoothAdapter.disable( ) 时需要使用到。

 


 

 一、蓝牙搜索功能的实现:

 

1、得到蓝牙适配器:

BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
   
   

若mBluetoothAdapter为 null,则说明当前手机不支持蓝牙功能(现在几乎所有手机都支持了吧。。。)

 

2、判断蓝牙是否打开:


   
   
  1. if (!mBluetoothAdapter.isEnabled()) {
  2. //若没打开则打开蓝牙
  3. mBluetoothAdapter.enable();
  4. }

值得注意的是,强制打开蓝牙设备的情况有三种:

(1)没有任何提示,直接打开了蓝牙。如Nexus 5 Android 4.4.4 手机。

(2)会弹出提示框,提示安全警告 “ ***应用尝试开启蓝牙”,可以选择“拒绝”或“允许”。大多数手机都是这样的。

(3)强制打开蓝牙失败,并且没有任何提示。

 

 

3、注册蓝牙搜索广播接收者:

(1)Android 的广播机制:

     Adnroid的广播机制(以intent对象的形式广播出去),Android系统广播的时候不会关心你是否收得到消息、只负责广播出去,而

且广播的对象只是在应用程序中注册了的广播接收器。我们要做的就是自定义广播接收器并将其注册给应用程序,在广播接收器中

将接收到广播事件作出相应的处理。如果广播的事件并不是我们定义的广播接收器需要的事件类型,一般是会过滤掉不被接收。只

有当广播事件和我们写的接收器定义的接收的事件类型一致的时候才会触发广播接收器。并且触发广播接收器的onReceive方法。当

然我们自定义的广播接收器需要接受事件的类型是在XML清单文件的<intent-filter>中自己定义声明的或者自己在程序代码中定义一

个IntentFilter对象然后通过对象的addAction()方法来自定义接收事件类型。然后我们需要将接收到的事件的处理代码写在onReceive

方法中。

(2)注册分为两种:静态注册和动态注册。

  • 静态注册就是在AndroidManifest.xml文件中定义,注册的广播接收器必须继承BroadReceiver
  • 动态注册就是在程序中使用Context.registerReceiver注册。

 

我们先演示动态注册:


   
   
  1. //注册设备被发现时的广播
  2. IntentFilter filter= new IntentFilter(BluetoothDevice.ACTION_FOUND);
  3. registerReceiver(mReceiver,filter);
  4. //注册一个搜索结束时的广播
  5. IntentFilter filter2= new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  6. registerReceiver(mReceiver,filter2);


对应的静态注册如下:


   
   
  1. <!-- 广播接收 -->
  2. <receiver android:name="包名.类名" >
  3. <intent-filter android:priority="1000">
  4. <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>
  5. <action android:name="android.bluetooth.device.action.FOUND" />
  6. </intent-filter>
  7. </receiver>


我们如何知道BluetoothAdapter.ACTION_DISCOVERY_FINISHED对应着android.bluetooth.adapter.action.DISCOVERY_FINISHED呢?

这就要看强大的API了。如图就是一种对应关系:

此处推荐别人上传的中文API:

                                  点我打开Android中文API

 

 

4、定义广播接收:

自定义的广播接收器对象必须要继承BroadcastReceiver,然后重写onReceive方法,处理接收的数据的代码就写在这个方法里面。

两种方法:

  • 自定义一个类实现BroadcastReceiver抽象类,并且实现其onReceiver(Context context, Intent intent )方法。
  • 直接new BroadcastReceiver()来搞定。

方法1如下:


   
   
  1. public class BluetoothReceiver extends BroadcastReceiver{
  2. @Override
  3. public void onReceive(Context context, Intent intent) {
  4. ...................
  5. }
  6. }

方法2如下:


   
   
  1. //定义广播接收
  2. private BroadcastReceiver mReceiver= new BroadcastReceiver(){
  3. @Override
  4. public void onReceive(Context context, Intent intent) {
  5. .......................
  6. }
  7. };


 5、开始广播:

       通过  mBluetoothAdapter.startDiscovery( ); 来开始广播。当广播的事件是我们刚刚注册的事件时就会触发广播接收器,并且触

发广播接收器中的onReceiver()方法。

 

6、解除注册:

通过 unregisterReceiver(mReceiver); 来解除刚刚的注册。

 

至此我们完成了蓝牙通信的第一步:蓝牙搜索。

下边给出一个完整Demo实例。

功能为:点击按钮将搜索附近的蓝牙设备,并且判断是否与本设备已经配对,分类显示。

代码如下:

mainActivity.java


   
   
  1. package com.example.administrator.myapplication;
  2. import android.bluetooth.BluetoothAdapter;
  3. import android.bluetooth.BluetoothDevice;
  4. import android.content.BroadcastReceiver;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.content.IntentFilter;
  8. import android.support.v7.app.AppCompatActivity;
  9. import android.os.Bundle;
  10. import android.util.Log;
  11. import android.view.View;
  12. import android.widget.Button;
  13. import android.widget.TextView;
  14. import android.widget.Toast;
  15. public class MainActivity extends AppCompatActivity {
  16. //定义
  17. private BluetoothAdapter mBluetoothAdapter;
  18. private TextView text,text2,text3;
  19. private Button botton;
  20. @Override
  21. protected void onCreate(Bundle savedInstanceState) {
  22. super.onCreate(savedInstanceState);
  23. setContentView(R.layout.activity_main);
  24. text=(TextView) this.findViewById(R.id.textView); //已配对
  25. text2= (TextView) this.findViewById(R.id.textView2); //状态信息
  26. text3= (TextView) this.findViewById(R.id.textView3); //未配对
  27. botton=(Button) this.findViewById(R.id.button);
  28. mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
  29. IntentFilter filter= new IntentFilter(BluetoothDevice.ACTION_FOUND);
  30. registerReceiver(mReceiver,filter);
  31. IntentFilter filter2= new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
  32. registerReceiver(mReceiver,filter2);
  33. botton.setOnClickListener( new View.OnClickListener(){
  34. @Override
  35. public void onClick(View arg0) {
  36. if(!mBluetoothAdapter.isEnabled())
  37. {
  38. mBluetoothAdapter.enable();
  39. }
  40. mBluetoothAdapter.startDiscovery();
  41. text2.setText( "正在搜索...");
  42. }
  43. });
  44. }
  45. public void onDestroy() {
  46. super.onDestroy();
  47. //解除注册
  48. unregisterReceiver(mReceiver);
  49. Log.e( "destory", "解除注册");
  50. }
  51. //定义广播接收
  52. private BroadcastReceiver mReceiver= new BroadcastReceiver(){
  53. @Override
  54. public void onReceive(Context context, Intent intent) {
  55. String action=intent.getAction();
  56. Log.e( "ywq", action);
  57. if(action.equals(BluetoothDevice.ACTION_FOUND))
  58. {
  59. BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  60. if(device.getBondState()==BluetoothDevice.BOND_BONDED)
  61. { //显示已配对设备
  62. text.append( "\n"+device.getName()+ "==>"+device.getAddress()+ "\n");
  63. } else if(device.getBondState()!=BluetoothDevice.BOND_BONDED)
  64. {
  65. text3.append( "\n"+device.getName()+ "==>"+device.getAddress()+ "\n");
  66. }
  67. } else if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)){
  68. text2.setText( "搜索完成...");
  69. }
  70. }
  71. };
  72. }


AndroidManifest.xml代码如下:


   
   
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package= "com.example.administrator.myapplication">
  4. <uses-permission android:name="android.permission.BLUETOOTH" />
  5. <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  6. <application
  7. android:allowBackup= "true"
  8. android:icon= "@mipmap/ic_launcher"
  9. android:label= "@string/app_name"
  10. android:supportsRtl= "true"
  11. android:theme= "@style/AppTheme">
  12. <activity android:name=".MainActivity">
  13. <intent-filter>
  14. <action android:name="android.intent.action.MAIN" />
  15. <category android:name="android.intent.category.LAUNCHER" />
  16. </intent-filter>
  17. </activity>
  18. </application>
  19. </manifest>

 

布局文件activity_main.xml代码如下:


   
   
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools= "http://schemas.android.com/tools"
  4. android:id= "@+id/activity_main"
  5. android:layout_width= "match_parent"
  6. android:layout_height= "match_parent"
  7. android:paddingBottom= "@dimen/activity_vertical_margin"
  8. android:paddingLeft= "@dimen/activity_horizontal_margin"
  9. android:paddingRight= "@dimen/activity_horizontal_margin"
  10. android:paddingTop= "@dimen/activity_vertical_margin"
  11. tools:context= "com.example.administrator.myapplication.MainActivity">
  12. <Button
  13. android:text= "搜索蓝牙"
  14. android:layout_width= "wrap_content"
  15. android:layout_height= "wrap_content"
  16. android:layout_marginTop= "19dp"
  17. android:id= "@+id/button"
  18. android:layout_alignParentLeft= "true"
  19. android:layout_alignParentStart= "true" />
  20. <TextView
  21. android:text= "初始状态"
  22. android:layout_width= "wrap_content"
  23. android:layout_height= "wrap_content"
  24. android:layout_alignBottom= "@+id/button"
  25. android:layout_toRightOf= "@+id/button"
  26. android:layout_toEndOf= "@+id/button"
  27. android:layout_marginLeft= "45dp"
  28. android:layout_marginStart= "45dp"
  29. android:layout_marginBottom= "14dp"
  30. android:id= "@+id/textView2" />
  31. <TextView
  32. android:layout_width= "wrap_content"
  33. android:layout_height= "wrap_content"
  34. android:id= "@+id/textView"
  35. android:text= "已配对蓝牙设备如下:"
  36. android:layout_marginLeft= "12dp"
  37. android:layout_marginStart= "12dp"
  38. android:layout_marginTop= "53dp"
  39. android:layout_below= "@+id/button"
  40. android:layout_alignParentLeft= "true"
  41. android:layout_alignParentStart= "true" />
  42. <TextView
  43. android:text= "未配对蓝牙设备如下:"
  44. android:layout_width= "wrap_content"
  45. android:layout_height= "wrap_content"
  46. android:layout_marginTop= "107dp"
  47. android:id= "@+id/textView3"
  48. android:layout_below= "@+id/textView"
  49. android:layout_alignLeft= "@+id/textView"
  50. android:layout_alignStart= "@+id/textView" />
  51. </RelativeLayout>


程序运行结果如下:


 

 

二、蓝牙自动配对功能实现:

 

蓝牙配对是建立连接的基础和前提。为什么不配对便无法建立连接?

        任何无线通信技术都存在被监听和破解的可能,蓝牙SIG为了保证蓝牙通信的安全性,采用认证的方式进行数据交互。同时为

了保证使用的方便性,以配对的形式完成两个蓝牙设备之间的首次通讯认证,经配对之后,随后的通讯连接就不必每次都要做确

认。所以认证码的产生是从配对开始的,经过配对,设备之间以PIN码建立约定的link key用于产生初始认证码,以用于以后建立的

连接。

       所以如果不配对,两个设备之间便无法建立认证关系,无法进行连接及其之后的操作,所以配对在一定程度上保证了蓝牙通信

的安全,当然这个安全保证机制是比较容易被破解的,因为现在很多个人设备没有人机接口,所以PIN码都是固定的而且大都设置为

通用的0000或者1234之类的,所以很容易被猜到并进而建立配对和连接。

 

关于蓝牙的自动配对,大家可以参考我的这篇博客:Android蓝牙自动配对Demo,亲测好使!!!

这里自夸一下,这篇博客还是受到了大家的一些好评。该自动配对方法,博主在魅蓝、华为、联想、红米以及Nexus手机上都有测

试过,使用的Android系统包括4.0+和5.0+,所以各位可以仔细阅读该博客。

 

 

 

三、蓝牙通信的实现:

 

本文所述的蓝牙通信为:Android 端蓝牙设备与其他蓝牙设备之间的通信

 

   下边讲述  Android手机端蓝牙与Arduino外接蓝牙模块之间进行通信。

 

(1)Arduino 端蓝牙模块

        蓝牙模块在Arduino 端只是一个串口,将蓝牙模块的Tx、Rx接在Arduino开发板上。

        初始化与Android蓝牙通信的串口,使用串口.read()来读取来自手机蓝牙的信息;使用串口.println(“XXXXXX”)来向手机

端蓝牙发送信息。

 

Demo代码如下:


   
   
  1. void setup() {
  2. Serial.begin( 9600); //初始化原有串口
  3. SerialBT.begin( 9600); //初始化一个串口用来作为蓝牙通信
  4. }
  5. void loop() {
  6. if(SerialBT.available()){ //如果串口可用,即串口中有数据传过来
  7. char rece=SerialBT.read(); //rece是来自手机蓝牙的信息
  8. Serial.println( "已经接收到来自Android蓝牙的信息"); //这句话将打印在Arduino自带的串口监视窗里
  9. if(rece== 'A'){
  10. SerialBT.println( "这是来自Arduino的信息"); //这句话的内容将显示在Android手机端
  11. }
  12. }
  13. }


(2)手机端蓝牙模块:

 Demo运行结果如下所示:

 

 

Demo代码如下:

MainActivity.java


   
   
  1. package com.ywq;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.util.UUID;
  6. import com.example.alltest.R;
  7. import android.os.AsyncTask;
  8. import android.os.Bundle;
  9. import android.os.Handler;
  10. import android.os.Message;
  11. import android.app.Activity;
  12. import android.bluetooth.BluetoothAdapter;
  13. import android.bluetooth.BluetoothDevice;
  14. import android.bluetooth.BluetoothSocket;
  15. import android.util.Log;
  16. import android.view.Menu;
  17. import android.view.View;
  18. import android.widget.Button;
  19. import android.widget.EditText;
  20. import android.widget.TextView;
  21. import android.widget.Toast;
  22. public class MainActivity extends Activity {
  23. //定义组件
  24. TextView statusLabel;
  25. Button btnConnect,btnSend,btnQuit;
  26. EditText etReceived,etSend;
  27. //device var
  28. private BluetoothAdapter mBluetoothAdapter = null;
  29. private BluetoothSocket btSocket = null;
  30. private OutputStream outStream = null;
  31. private InputStream inStream = null;
  32. //这条是蓝牙串口通用的UUID,不要更改
  33. private static final UUID MY_UUID =
  34. UUID.fromString( "00001101-0000-1000-8000-00805F9B34FB");
  35. private static String address = "20:16:07:26:18:46"; // <==要连接的目标蓝牙设备MAC地址
  36. private ReceiveThread rThread= null; //数据接收线程
  37. //接收到的字符串
  38. String ReceiveData= "";
  39. MyHandler handler;
  40. @Override
  41. protected void onCreate(Bundle savedInstanceState) {
  42. super.onCreate(savedInstanceState);
  43. setContentView(R.layout.activity_main);
  44. //首先调用初始化函数
  45. Init();
  46. InitBluetooth();
  47. handler= new MyHandler();
  48. btnConnect.setOnClickListener( new View.OnClickListener() {
  49. @Override
  50. public void onClick(View v) {
  51. //判断蓝牙是否打开
  52. if(!mBluetoothAdapter.isEnabled())
  53. {
  54. mBluetoothAdapter.enable();
  55. }
  56. mBluetoothAdapter.startDiscovery();
  57. //创建连接
  58. new ConnectTask().execute(address);
  59. }
  60. });
  61. btnQuit.setOnClickListener( new View.OnClickListener() {
  62. @Override
  63. public void onClick(View v) {
  64. // TODO Auto-generated method stub
  65. if(btSocket!= null)
  66. {
  67. try {
  68. btSocket.close();
  69. btSocket= null;
  70. if(rThread!= null)
  71. {
  72. rThread.join();
  73. }
  74. statusLabel.setText( "当前连接已断开");
  75. // etReceived.setText("");
  76. } catch (IOException e) {
  77. e.printStackTrace();
  78. } catch (InterruptedException e) {
  79. e.printStackTrace();
  80. }
  81. }
  82. }
  83. });
  84. btnSend.setOnClickListener( new View.OnClickListener() {
  85. @Override
  86. public void onClick(View v) {
  87. // TODO Auto-generated method stub
  88. new SendInfoTask().execute(etSend.getText().toString());
  89. }
  90. });
  91. }
  92. public void Init()
  93. {
  94. statusLabel=(TextView) this.findViewById(R.id.textView1);
  95. btnConnect=(Button) this.findViewById(R.id.button1);
  96. btnSend=(Button) this.findViewById(R.id.button2);
  97. btnQuit=(Button) this.findViewById(R.id.button3);
  98. etSend=(EditText) this.findViewById(R.id.editText1);
  99. etReceived=(EditText) this.findViewById(R.id.editText2);
  100. }
  101. public void InitBluetooth()
  102. {
  103. //得到一个蓝牙适配器
  104. mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  105. if(mBluetoothAdapter == null)
  106. {
  107. Toast.makeText( this, "你的手机不支持蓝牙", Toast.LENGTH_LONG).show();
  108. finish();
  109. return;
  110. }
  111. }
  112. @Override
  113. public boolean onCreateOptionsMenu(Menu menu) {
  114. // Inflate the menu; this adds items to the action bar if it is present.
  115. getMenuInflater().inflate(R.menu.main, menu);
  116. return true;
  117. }
  118. //连接蓝牙设备的异步任务
  119. class ConnectTask extends AsyncTask<String,String,String>
  120. {
  121. @Override
  122. protected String doInBackground(String... params) {
  123. // TODO Auto-generated method stub
  124. BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(params[ 0]);
  125. try {
  126. btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
  127. btSocket.connect();
  128. Log.e( "error", "ON RESUME: BT connection established, data transfer link open.");
  129. } catch (IOException e) {
  130. try {
  131. btSocket.close();
  132. return "Socket 创建失败";
  133. } catch (IOException e2) {
  134. Log .e( "error", "ON RESUME: Unable to close socket during connection failure", e2);
  135. return "Socket 关闭失败";
  136. }
  137. }
  138. //取消搜索
  139. mBluetoothAdapter.cancelDiscovery();
  140. try {
  141. outStream = btSocket.getOutputStream();
  142. } catch (IOException e) {
  143. Log.e( "error", "ON RESUME: Output stream creation failed.", e);
  144. return "Socket 流创建失败";
  145. }
  146. return "蓝牙连接正常,Socket 创建成功";
  147. }
  148. @Override //这个方法是在主线程中运行的,所以可以更新界面
  149. protected void onPostExecute(String result) {
  150. // TODO Auto-generated method stub
  151. //连接成功则启动监听
  152. rThread= new ReceiveThread();
  153. rThread.start();
  154. statusLabel.setText(result);
  155. super.onPostExecute(result);
  156. }
  157. }
  158. //发送数据到蓝牙设备的异步任务
  159. class SendInfoTask extends AsyncTask<String,String,String>
  160. {
  161. @Override
  162. protected void onPostExecute(String result) {
  163. // TODO Auto-generated method stub
  164. super.onPostExecute(result);
  165. statusLabel.setText(result);
  166. //将发送框清空
  167. etSend.setText( "");
  168. }
  169. @Override
  170. protected String doInBackground(String... arg0) {
  171. // TODO Auto-generated method stub
  172. if(btSocket== null)
  173. {
  174. return "还没有创建连接";
  175. }
  176. if(arg0[ 0].length()> 0) //不是空白串
  177. {
  178. //String target=arg0[0];
  179. byte[] msgBuffer = arg0[ 0].getBytes();
  180. try {
  181. // 将msgBuffer中的数据写到outStream对象中
  182. outStream.write(msgBuffer);
  183. } catch (IOException e) {
  184. Log.e( "error", "ON RESUME: Exception during write.", e);
  185. return "发送失败";
  186. }
  187. }
  188. return "发送成功";
  189. }
  190. }
  191. //从蓝牙接收信息的线程
  192. class ReceiveThread extends Thread
  193. {
  194. String buffer= "";
  195. @Override
  196. public void run() {
  197. while(btSocket!= null )
  198. {
  199. //定义一个存储空间buff
  200. byte[] buff= new byte[ 1024];
  201. try {
  202. inStream = btSocket.getInputStream();
  203. System.out.println( "waitting for instream");
  204. inStream.read(buff); //读取数据存储在buff数组中
  205. // System.out.println("buff receive :"+buff.length);
  206. processBuffer(buff, 1024);
  207. //System.out.println("receive content:"+ReceiveData);
  208. } catch (IOException e) {
  209. e.printStackTrace();
  210. }
  211. }
  212. }
  213. private void processBuffer(byte[] buff,int size)
  214. {
  215. int length= 0;
  216. for( int i= 0;i<size;i++)
  217. {
  218. if(buff[i]> '\0')
  219. {
  220. length++;
  221. }
  222. else
  223. {
  224. break;
  225. }
  226. }
  227. // System.out.println("receive fragment size:"+length);
  228. byte[] newbuff= new byte[length]; //newbuff字节数组,用于存放真正接收到的数据
  229. for( int j= 0;j<length;j++)
  230. {
  231. newbuff[j]=buff[j];
  232. }
  233. ReceiveData=ReceiveData+ new String(newbuff);
  234. Log.e( "Data",ReceiveData);
  235. // System.out.println("result :"+ReceiveData);
  236. Message msg=Message.obtain();
  237. msg.what= 1;
  238. handler.sendMessage(msg); //发送消息:系统会自动调用handleMessage( )方法来处理消息
  239. }
  240. }
  241. //更新界面的Handler类
  242. class MyHandler extends Handler{
  243. @Override
  244. public void handleMessage(Message msg) {
  245. switch(msg.what){
  246. case 1:
  247. etReceived.setText(ReceiveData);
  248. break;
  249. }
  250. }
  251. }
  252. @Override
  253. protected void onDestroy() {
  254. // TODO Auto-generated method stub
  255. super.onDestroy();
  256. try {
  257. if(rThread!= null)
  258. {
  259. btSocket.close();
  260. btSocket= null;
  261. rThread.join();
  262. }
  263. this.finish();
  264. } catch (IOException e) {
  265. // TODO Auto-generated catch block
  266. e.printStackTrace();
  267. } catch (InterruptedException e) {
  268. // TODO Auto-generated catch block
  269. e.printStackTrace();
  270. }
  271. }
  272. }

 

BluetoothReceiver.java


   
   
  1. package com.ywq.broadcast;
  2. import com.ywq.tools.ClsUtils;
  3. import android.bluetooth.BluetoothDevice;
  4. import android.content.BroadcastReceiver;
  5. import android.content.Context;
  6. import android.content.Intent;
  7. import android.util.Log;
  8. public class BluetoothReceiver extends BroadcastReceiver{
  9. String pin = "1234"; //此处为你要连接的蓝牙设备的初始密钥,一般为1234或0000
  10. public BluetoothReceiver() {
  11. }
  12. //广播接收器,当远程蓝牙设备被发现时,回调函数onReceiver()会被执行
  13. @Override
  14. public void onReceive(Context context, Intent intent) {
  15. String action = intent.getAction(); //得到action
  16. Log.e( "action1=", action);
  17. BluetoothDevice btDevice= null; //创建一个蓝牙device对象
  18. // 从Intent中获取设备对象
  19. btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
  20. if(BluetoothDevice.ACTION_FOUND.equals(action)){ //发现设备
  21. Log.e( "发现设备:", "["+btDevice.getName()+ "]"+ ":"+btDevice.getAddress());
  22. if(btDevice.getName().contains( "HC-05")) //HC-05设备如果有多个,第一个搜到的那个会被尝试。
  23. {
  24. if (btDevice.getBondState() == BluetoothDevice.BOND_NONE) {
  25. Log.e( "ywq", "attemp to bond:"+ "["+btDevice.getName()+ "]");
  26. try {
  27. //通过工具类ClsUtils,调用createBond方法
  28. ClsUtils.createBond(btDevice.getClass(), btDevice);
  29. } catch (Exception e) {
  30. // TODO Auto-generated catch block
  31. e.printStackTrace();
  32. }
  33. }
  34. } else
  35. Log.e( "error", "Is faild");
  36. } else if(action.equals( "android.bluetooth.device.action.PAIRING_REQUEST")) //再次得到的action,会等于PAIRING_REQUEST
  37. {
  38. Log.e( "action2=", action);
  39. if(btDevice.getName().contains( "HC-05"))
  40. {
  41. Log.e( "here", "OKOKOK");
  42. try {
  43. //1.确认配对
  44. ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
  45. //2.终止有序广播
  46. Log.i( "order...", "isOrderedBroadcast:"+isOrderedBroadcast()+ ",isInitialStickyBroadcast:"+isInitialStickyBroadcast());
  47. abortBroadcast(); //如果没有将广播终止,则会出现一个一闪而过的配对框。
  48. //3.调用setPin方法进行配对...
  49. boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, pin);
  50. } catch (Exception e) {
  51. // TODO Auto-generated catch block
  52. e.printStackTrace();
  53. }
  54. } else
  55. Log.e( "提示信息", "这个设备不是目标蓝牙设备");
  56. }
  57. }
  58. }


配对工具类ClsUtils.java如下:


   
   
  1. package com.ywq.tools;
  2. /************************************ 蓝牙配对函数 * **************/
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Field;
  5. import android.bluetooth.BluetoothDevice;
  6. import android.util.Log;
  7. public class ClsUtils
  8. {
  9. /**
  10. * 与设备配对 参考源码:platform/packages/apps/Settings.git
  11. * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
  12. */
  13. static public boolean createBond(Class btClass, BluetoothDevice btDevice)
  14. throws Exception
  15. {
  16. Method createBondMethod = btClass.getMethod( "createBond");
  17. Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
  18. return returnValue.booleanValue();
  19. }
  20. /**
  21. * 与设备解除配对 参考源码:platform/packages/apps/Settings.git
  22. * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java
  23. */
  24. static public boolean removeBond(Class<?> btClass, BluetoothDevice btDevice)
  25. throws Exception
  26. {
  27. Method removeBondMethod = btClass.getMethod( "removeBond");
  28. Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
  29. return returnValue.booleanValue();
  30. }
  31. static public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice,
  32. String str) throws Exception
  33. {
  34. try
  35. {
  36. Method removeBondMethod = btClass.getDeclaredMethod( "setPin",
  37. new Class[]
  38. { byte[].class});
  39. Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,
  40. new Object[]
  41. {str.getBytes()});
  42. Log.e( "returnValue", "" + returnValue);
  43. }
  44. catch (SecurityException e)
  45. {
  46. // throw new RuntimeException(e.getMessage());
  47. e.printStackTrace();
  48. }
  49. catch (IllegalArgumentException e)
  50. {
  51. // throw new RuntimeException(e.getMessage());
  52. e.printStackTrace();
  53. }
  54. catch (Exception e)
  55. {
  56. // TODO Auto-generated catch block
  57. e.printStackTrace();
  58. }
  59. return true;
  60. }
  61. // 取消用户输入
  62. static public boolean cancelPairingUserInput(Class<?> btClass,
  63. BluetoothDevice device) throws Exception
  64. {
  65. Method createBondMethod = btClass.getMethod( "cancelPairingUserInput");
  66. // cancelBondProcess(btClass, device);
  67. Boolean returnValue = (Boolean) createBondMethod.invoke(device);
  68. return returnValue.booleanValue();
  69. }
  70. // 取消配对
  71. static public boolean cancelBondProcess(Class<?> btClass,
  72. BluetoothDevice device)
  73. throws Exception
  74. {
  75. Method createBondMethod = btClass.getMethod( "cancelBondProcess");
  76. Boolean returnValue = (Boolean) createBondMethod.invoke(device);
  77. return returnValue.booleanValue();
  78. }
  79. //确认配对
  80. static public void setPairingConfirmation(Class<?> btClass,BluetoothDevice device,boolean isConfirm)throws Exception
  81. {
  82. Method setPairingConfirmation = btClass.getDeclaredMethod( "setPairingConfirmation", boolean.class);
  83. setPairingConfirmation.invoke(device,isConfirm);
  84. }
  85. /**
  86. *
  87. * @param clsShow
  88. */
  89. static public void printAllInform(Class clsShow)
  90. {
  91. try
  92. {
  93. // 取得所有方法
  94. Method[] hideMethod = clsShow.getMethods();
  95. int i = 0;
  96. for (; i < hideMethod.length; i++)
  97. {
  98. Log.e( "method name", hideMethod[i].getName() + ";and the i is:"
  99. + i);
  100. }
  101. // 取得所有常量
  102. Field[] allFields = clsShow.getFields();
  103. for (i = 0; i < allFields.length; i++)
  104. {
  105. Log.e( "Field name", allFields[i].getName());
  106. }
  107. }
  108. catch (SecurityException e)
  109. {
  110. // throw new RuntimeException(e.getMessage());
  111. e.printStackTrace();
  112. }
  113. catch (IllegalArgumentException e)
  114. {
  115. // throw new RuntimeException(e.getMessage());
  116. e.printStackTrace();
  117. }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值