Android 藍牙開發之搜索、配對、連接、通信大全

 注:原文鏈接

        藍牙( Bluetooth®):是一種無線技術標準,可實現固定設備、移動設備和樓宇個人域網之間的短距離數據

交換(使用2.4—2.485GHz的ISM波段的UHF無線電波)。藍牙設備最多可以同時和7個其它藍牙設備建立連接,進

行通信,當然並不是每一個藍牙都可以達到最大值。下面,我們從藍牙的基本概念開始,一步一步開始瞭解藍牙。

 

(尊重勞動成果,轉載請註明出處http://blog.csdn.net/qq_25827845/article/details/52997523

 源碼下載地址:http://download.csdn.net/download/qq_25827845/9757403


基本概念: 

         安卓平臺提供對藍牙的通訊棧的支持,允許設別和其他的設備進行無線傳輸數據。應用程序層通過安卓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 中常用方法如下所示:

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

Alphabetic characters must be uppercase to be valid.

void closeProfileProxy(int profile, BluetoothProfile proxy)
Close the connection of the profile proxy to the Service.
boolean disable()
Turn off the local Bluetooth adapter—do not use without explicit user action to turn off Bluetooth.
boolean enable()
Turn on the local Bluetooth adapter—do not use without explicit user action to turn on Bluetooth.
String getAddress()
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 BluetoothAdapter getDefaultAdapter()
Get a handle to the default local Bluetooth adapter.
String getName()
Get the friendly Bluetooth name of the local Bluetooth adapter.
int getProfileConnectionState(int profile)
Get the current connection state of a profile.
boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener, int profile)
Get the profile proxy object associated with the profile.
BluetoothDevice getRemoteDevice(byte[] address)
Get a BluetoothDevice object for the given Bluetooth hardware address.
BluetoothDevice getRemoteDevice(String address)
Get a BluetoothDevice object for the given Bluetooth hardware address.
int getScanMode()
Get the current Bluetooth scan mode of the local Bluetooth adapter.
int getState()
Get the current state of the local Bluetooth adapter.
boolean isDiscovering()
Return true if the local Bluetooth adapter is currently in the device discovery process.
boolean isEnabled()
Return true if Bluetooth is currently enabled and ready for use.
BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(String name, UUID uuid)
Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
Create a listening, secure RFCOMM Bluetooth socket with Service Record.
boolean setName(String name)
Set the friendly Bluetooth name of the local Bluetooth adapter.
boolean startDiscovery()
Start the remote device discovery process.
boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
Starts a scan for Bluetooth LE devices.
boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
Starts a scan for Bluetooth LE devices, looking for devices that advertise given services.
void stopLeScan(BluetoothAdapter.LeScanCallback callback)
Stops an ongoing Bluetooth LE device scan.

 

BluetoothDevice 中常用方法如下所示:

BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
Connect to GATT Server hosted by this device.
boolean createBond()
Start the bonding (pairing) process with the remote device.
BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid)
Create an RFCOMM BluetoothSocket socket ready to start an insecure outgoing connection to this remote device using SDP lookup of uuid.
BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid)
Create an RFCOMM BluetoothSocket ready to start a secure outgoing connection to this remote device using SDP lookup of uuid.
int describeContents()
Describe the kinds of special objects contained in this Parcelable's marshalled representation.
boolean equals(Object o)
Compares this instance with the specified object and indicates if they are equal.
boolean fetchUuidsWithSdp()
Perform a service discovery on the remote device to get the UUIDs supported.
String getAddress()
Returns the hardware address of this BluetoothDevice.
BluetoothClass getBluetoothClass()
Get the Bluetooth class of the remote device.
int getBondState()
Get the bond state of the remote device.
String getName()
Get the friendly Bluetooth name of the remote device.
int getType()
Get the Bluetooth device type of the remote device.
ParcelUuid[] getUuids()
Returns the supported features (UUIDs) of the remote device.
int hashCode()
Returns an integer hash code for this object.
boolean setPairingConfirmation(boolean confirm)
Confirm passkey for PAIRING_VARIANT_PASSKEY_CONFIRMATION pairing.
boolean setPin(byte[] pin)
Set the pin during pairing when the pairing method is PAIRING_VARIANT_PIN

Requires BLUETOOTH_ADMIN.

String toString()
Returns a string representation of this BluetoothDevice.
void writeToParcel(Parcel out, int flags)
Flatten this object in to a Parcel.

 

BluetoothSocket 中常用方法如下所示:

void close()
Closes the object and release any system resources it holds.
void connect()
Attempt to connect to a remote device.
InputStream getInputStream()
Get the input stream associated with this socket.
OutputStream getOutputStream()
Get the output stream associated with this socket.
BluetoothDevice getRemoteDevice()
Get the remote device this socket is connecting, or connected, to.
boolean isConnected()
Get the connection status of this socket, ie, whether there is an active connection with remote device.

 

BluetoothServerSocket 中常用方法如下所示:

BluetoothSocket accept(int timeout)
Block until a connection is established, with timeout.
BluetoothSocket accept()
Block until a connection is established.
void close()
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、得到藍牙適配器:

  1. BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();  
BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();

若mBluetoothAdapter爲 null,則說明當前手機不支持藍牙功能(現在幾乎所有手機都支持了吧。。。)

 

2、判斷藍牙是否打開:

  1. if (!mBluetoothAdapter.isEnabled()) {  
  2.        //若沒打開則打開藍牙  
  3.        mBluetoothAdapter.enable();  
  4.     
  5. }  
if (!mBluetoothAdapter.isEnabled()) {
       //若沒打開則打開藍牙
       mBluetoothAdapter.enable();
  
}

值得注意的是,強制打開藍牙設備的情況有三種:

(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);  
//註冊設備被發現時的廣播
IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver,filter);
//註冊一個搜索結束時的廣播
IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        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>  
<!-- 廣播接收 -->
        <receiver android:name="包名.類名" >
    		<intent-filter android:priority="1000">
        		<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>
        		<action android:name="android.bluetooth.device.action.FOUND" />
    		</intent-filter>
	</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.        }  
  7.   
  8. }  
public class BluetoothReceiver extends BroadcastReceiver{
        @Override
	public void onReceive(Context context, Intent intent) {

          ...................
       }

}

方法2如下:

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

        @Override
        public void onReceive(Context context, Intent intent) {
              .......................
                   }
    };


 5、開始廣播:

       通過  mBluetoothAdapter.startDiscovery( ); 來開始廣播。當廣播的事件是我們剛剛註冊的事件時就會觸發廣播接收器,並且觸

發廣播接收器中的onReceiver()方法。

 

6、解除註冊:

通過 unregisterReceiver(mReceiver); 來解除剛剛的註冊。

 

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

下邊給出一個完整Demo實例。

功能爲:點擊按鈕將搜索附近的藍牙設備,並且判斷是否與本設備已經配對,分類顯示。

代碼如下:

mainActivity.Java

  1. package com.example.administrator.myapplication;  
  2.   
  3. import android.bluetooth.BluetoothAdapter;  
  4. import android.bluetooth.BluetoothDevice;  
  5. import android.content.BroadcastReceiver;  
  6. import android.content.Context;  
  7. import android.content.Intent;  
  8. import android.content.IntentFilter;  
  9. import android.support.v7.app.AppCompatActivity;  
  10. import android.os.Bundle;  
  11. import android.util.Log;  
  12. import android.view.View;  
  13. import android.widget.Button;  
  14. import android.widget.TextView;  
  15. import android.widget.Toast;  
  16.   
  17. public class MainActivity extends AppCompatActivity {  
  18.   
  19.     //定義  
  20.     private BluetoothAdapter mBluetoothAdapter;  
  21.     private TextView text,text2,text3;  
  22.     private Button botton;  
  23.   
  24.     @Override  
  25.     protected void onCreate(Bundle savedInstanceState) {  
  26.         super.onCreate(savedInstanceState);  
  27.         setContentView(R.layout.activity_main);  
  28.   
  29.   
  30.         text=(TextView) this.findViewById(R.id.textView);  //已配對  
  31.         text2= (TextView) this.findViewById(R.id.textView2); //狀態信息  
  32.         text3= (TextView) this.findViewById(R.id.textView3); //未配對  
  33.         botton=(Button) this.findViewById(R.id.button);  
  34.   
  35.         mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();  
  36.   
  37.         IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND);  
  38.         registerReceiver(mReceiver,filter);  
  39.         IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
  40.         registerReceiver(mReceiver,filter2);  
  41.   
  42.         botton.setOnClickListener(new View.OnClickListener(){  
  43.   
  44.             @Override  
  45.             public void onClick(View arg0) {  
  46.   
  47.                 if(!mBluetoothAdapter.isEnabled())  
  48.                 {  
  49.                     mBluetoothAdapter.enable();  
  50.   
  51.                 }  
  52.   
  53.                     mBluetoothAdapter.startDiscovery();  
  54.                     text2.setText("正在搜索...");  
  55.   
  56.             }  
  57.   
  58.   
  59.         });  
  60.   
  61.   
  62.     }  
  63.   
  64.   
  65.     public void onDestroy() {  
  66.   
  67.         super.onDestroy();  
  68.         //解除註冊  
  69.         unregisterReceiver(mReceiver);  
  70.         Log.e("destory","解除註冊");  
  71.     }  
  72.   
  73.   
  74.   
  75.     //定義廣播接收  
  76.     private BroadcastReceiver mReceiver=new BroadcastReceiver(){  
  77.   
  78.   
  79.   
  80.         @Override  
  81.         public void onReceive(Context context, Intent intent) {  
  82.   
  83.             String action=intent.getAction();  
  84.   
  85.             Log.e("ywq", action);  
  86.             if(action.equals(BluetoothDevice.ACTION_FOUND))  
  87.             {  
  88.                 BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
  89.   
  90.                 if(device.getBondState()==BluetoothDevice.BOND_BONDED)  
  91.                 {    //顯示已配對設備  
  92.                     text.append("\n"+device.getName()+"==>"+device.getAddress()+"\n");  
  93.                 }else if(device.getBondState()!=BluetoothDevice.BOND_BONDED)  
  94.                 {  
  95.                     text3.append("\n"+device.getName()+"==>"+device.getAddress()+"\n");  
  96.                 }  
  97.   
  98.             }else if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)){  
  99.   
  100.                 text2.setText("搜索完成...");  
  101.   
  102.   
  103.             }  
  104.   
  105.         }  
  106.   
  107.   
  108.     };  
  109.   
  110. }  
package com.example.administrator.myapplication;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    //定義
    private BluetoothAdapter mBluetoothAdapter;
    private TextView text,text2,text3;
    private Button botton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        text=(TextView) this.findViewById(R.id.textView);  //已配對
        text2= (TextView) this.findViewById(R.id.textView2); //狀態信息
        text3= (TextView) this.findViewById(R.id.textView3); //未配對
        botton=(Button) this.findViewById(R.id.button);

        mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();

        IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver,filter);
        IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(mReceiver,filter2);

        botton.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View arg0) {

                if(!mBluetoothAdapter.isEnabled())
                {
                    mBluetoothAdapter.enable();

                }

                    mBluetoothAdapter.startDiscovery();
                    text2.setText("正在搜索...");

            }


        });


    }


    public void onDestroy() {

        super.onDestroy();
        //解除註冊
        unregisterReceiver(mReceiver);
        Log.e("destory","解除註冊");
    }



    //定義廣播接收
    private BroadcastReceiver mReceiver=new BroadcastReceiver(){



        @Override
        public void onReceive(Context context, Intent intent) {

            String action=intent.getAction();

            Log.e("ywq", action);
            if(action.equals(BluetoothDevice.ACTION_FOUND))
            {
                BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);

                if(device.getBondState()==BluetoothDevice.BOND_BONDED)
                {    //顯示已配對設備
                    text.append("\n"+device.getName()+"==>"+device.getAddress()+"\n");
                }else if(device.getBondState()!=BluetoothDevice.BOND_BONDED)
                {
                    text3.append("\n"+device.getName()+"==>"+device.getAddress()+"\n");
                }

            }else if(action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)){

                text2.setText("搜索完成...");


            }

        }


    };

}


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

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

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

    <Button
        android:text="搜索藍牙"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="19dp"
        android:id="@+id/button"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <TextView
        android:text="初始狀態"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/button"
        android:layout_toRightOf="@+id/button"
        android:layout_toEndOf="@+id/button"
        android:layout_marginLeft="45dp"
        android:layout_marginStart="45dp"
        android:layout_marginBottom="14dp"
        android:id="@+id/textView2" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:text="已配對藍牙設備如下:"
        android:layout_marginLeft="12dp"
        android:layout_marginStart="12dp"
        android:layout_marginTop="53dp"
        android:layout_below="@+id/button"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <TextView
        android:text="未配對藍牙設備如下:"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="107dp"
        android:id="@+id/textView3"
        android:layout_below="@+id/textView"
        android:layout_alignLeft="@+id/textView"
        android:layout_alignStart="@+id/textView" />

</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.   
  4.  SerialBT.begin(9600);  //初始化一個串口用來作爲藍牙通信  
  5.   
  6. }  
  7.   
  8. void loop() {  
  9.   
  10.  if(SerialBT.available()){  //如果串口可用,即串口中有數據傳過來  
  11.       char rece=SerialBT.read();   //rece是來自手機藍牙的信息  
  12.       Serial.println("已經接收到來自Android藍牙的信息"); //這句話將打印在Arduino自帶的串口監視窗裏  
  13.   if(rece=='A'){  
  14.        SerialBT.println("這是來自Arduino的信息");   //這句話的內容將顯示在Android手機端  
  15.     }  
  16.     
  17.   }  
  18.   
  19. }  
void setup() {
 Serial.begin(9600);   //初始化原有串口

 SerialBT.begin(9600);  //初始化一個串口用來作爲藍牙通信

}

void loop() {

 if(SerialBT.available()){  //如果串口可用,即串口中有數據傳過來
      char rece=SerialBT.read();   //rece是來自手機藍牙的信息
      Serial.println("已經接收到來自Android藍牙的信息"); //這句話將打印在Arduino自帶的串口監視窗裏
  if(rece=='A'){
       SerialBT.println("這是來自Arduino的信息");   //這句話的內容將顯示在Android手機端
    }
  
  }

}


(2)手機端藍牙模塊:

 Demo運行結果如下所示:

 

 

Demo代碼如下:

MainActivity.java

  1. package com.ywq;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.OutputStream;  
  6. import java.util.UUID;  
  7. import com.example.alltest.R;  
  8. import android.os.AsyncTask;  
  9. import android.os.Bundle;  
  10. import android.os.Handler;  
  11. import android.os.Message;  
  12. import android.app.Activity;  
  13. import android.bluetooth.BluetoothAdapter;  
  14. import android.bluetooth.BluetoothDevice;  
  15. import android.bluetooth.BluetoothSocket;  
  16. import android.util.Log;  
  17. import android.view.Menu;  
  18. import android.view.View;  
  19. import android.widget.Button;  
  20. import android.widget.EditText;  
  21. import android.widget.TextView;  
  22. import android.widget.Toast;  
  23.   
  24. public class MainActivity extends Activity {  
  25.   
  26.     //定義組件  
  27.     TextView statusLabel;   
  28.     Button btnConnect,btnSend,btnQuit;  
  29.     EditText etReceived,etSend;  
  30.       
  31.     //device var  
  32.     private BluetoothAdapter mBluetoothAdapter = null;    
  33.         
  34.     private BluetoothSocket btSocket = null;    
  35.        
  36.     private OutputStream outStream = null;    
  37.             
  38.     private InputStream inStream = null;    
  39.     
  40.     //這條是藍牙串口通用的UUID,不要更改    
  41.     private static final UUID MY_UUID =   
  42.             UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");    
  43.         
  44.     private static String address = "20:16:07:26:18:46"// <==要連接的目標藍牙設備MAC地址    
  45.   
  46.       
  47.     private ReceiveThread rThread=null;  //數據接收線程  
  48.       
  49.     //接收到的字符串  
  50.     String ReceiveData="";  
  51.       
  52.     MyHandler handler;  
  53.       
  54.       
  55.         
  56.     @Override  
  57.     protected void onCreate(Bundle savedInstanceState) {  
  58.         super.onCreate(savedInstanceState);  
  59.         setContentView(R.layout.activity_main);  
  60.         //首先調用初始化函數  
  61.         Init();   
  62.         InitBluetooth();  
  63.           
  64.         handler=new MyHandler();          
  65.           
  66.         btnConnect.setOnClickListener(new View.OnClickListener() {    
  67.             @Override  
  68.             public void onClick(View v) {  
  69.                 //判斷藍牙是否打開  
  70.                 if(!mBluetoothAdapter.isEnabled())     
  71.                 {    
  72.                    mBluetoothAdapter.enable();  
  73.                 }  
  74.                  mBluetoothAdapter.startDiscovery();                  
  75.       
  76.             //創建連接    
  77.             new ConnectTask().execute(address);  
  78.                   
  79.             }  
  80.         });  
  81.           
  82.           
  83.         btnQuit.setOnClickListener(new View.OnClickListener() {  
  84.               
  85.             @Override  
  86.             public void onClick(View v) {  
  87.                 // TODO Auto-generated method stub  
  88.                   
  89.                 if(btSocket!=null)  
  90.                 {  
  91.                     try {  
  92.                         btSocket.close();  
  93.                         btSocket=null;  
  94.                         if(rThread!=null)  
  95.                         {  
  96.                             rThread.join();  
  97.                         }     
  98.                         statusLabel.setText("當前連接已斷開");  
  99. //                      etReceived.setText("");  
  100.                     } catch (IOException e) {  
  101.                           
  102.                         e.printStackTrace();  
  103.                     } catch (InterruptedException e) {  
  104.   
  105.                         e.printStackTrace();  
  106.                     }  
  107.                 }  
  108.                   
  109.                   
  110.                   
  111.             }  
  112.         });  
  113.           
  114.         btnSend.setOnClickListener(new View.OnClickListener() {  
  115.               
  116.             @Override  
  117.             public void onClick(View v) {  
  118.                 // TODO Auto-generated method stub  
  119.                 new SendInfoTask().execute(etSend.getText().toString());  
  120.                                   
  121.             }  
  122.         });  
  123.     }  
  124.           
  125.     public void Init()  
  126.     {  
  127.         statusLabel=(TextView)this.findViewById(R.id.textView1);  
  128.         btnConnect=(Button)this.findViewById(R.id.button1);  
  129.         btnSend=(Button)this.findViewById(R.id.button2);  
  130.         btnQuit=(Button)this.findViewById(R.id.button3);  
  131.         etSend=(EditText)this.findViewById(R.id.editText1);  
  132.         etReceived=(EditText)this.findViewById(R.id.editText2);  
  133.     }  
  134.   
  135.     public void InitBluetooth()  
  136.     {  
  137.         //得到一個藍牙適配器  
  138.         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();    
  139.             if(mBluetoothAdapter == null)     
  140.                 {    
  141.                     Toast.makeText(this"你的手機不支持藍牙", Toast.LENGTH_LONG).show();    
  142.                     finish();    
  143.                     return;    
  144.                 }    
  145.   
  146.     }  
  147.       
  148.     @Override  
  149.     public boolean onCreateOptionsMenu(Menu menu) {  
  150.         // Inflate the menu; this adds items to the action bar if it is present.  
  151.         getMenuInflater().inflate(R.menu.main, menu);  
  152.         return true;  
  153.     }  
  154.       
  155.     //連接藍牙設備的異步任務  
  156.     class ConnectTask extends AsyncTask<String,String,String>  
  157.         {  
  158.               
  159.   
  160.             @Override  
  161.             protected String doInBackground(String... params) {  
  162.                 // TODO Auto-generated method stub  
  163.              BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(params[0]);    
  164.                    
  165.                     try {    
  166.                 
  167.                         btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);    
  168.             
  169.                             
  170.                         btSocket.connect();    
  171.                 
  172.                         Log.e("error""ON RESUME: BT connection established, data transfer link open.");    
  173.                 
  174.                     } catch (IOException e) {    
  175.                
  176.                        try {    
  177.                             btSocket.close();   
  178.                             return "Socket 創建失敗";  
  179.         
  180.                         } catch (IOException e2) {    
  181.                               
  182.                            Log .e("error","ON RESUME: Unable to close socket during connection failure", e2);  
  183.                            return "Socket 關閉失敗";  
  184.                        }    
  185.                 
  186.                     }   
  187.                     //取消搜索  
  188.                     mBluetoothAdapter.cancelDiscovery();    
  189.                       
  190.                     try {   
  191.                          outStream = btSocket.getOutputStream();   
  192.                                              
  193.                        } catch (IOException e) {   
  194.                          Log.e("error""ON RESUME: Output stream creation failed.", e);  
  195.                          return "Socket 流創建失敗";  
  196.                        }   
  197.                         
  198.                       
  199.                     return "藍牙連接正常,Socket 創建成功";  
  200.             }  
  201.   
  202.             @Override    //這個方法是在主線程中運行的,所以可以更新界面  
  203.             protected void onPostExecute(String result) {  
  204.                 // TODO Auto-generated method stub  
  205.                   
  206.                 //連接成功則啓動監聽   
  207.                 rThread=new ReceiveThread();  
  208.                   
  209.                 rThread.start();  
  210.                   
  211.                 statusLabel.setText(result);  
  212.                   
  213.                 super.onPostExecute(result);  
  214.             }  
  215.               
  216.               
  217.               
  218.         }  
  219.       
  220.     //發送數據到藍牙設備的異步任務  
  221.     class SendInfoTask extends AsyncTask<String,String,String>  
  222.     {  
  223.   
  224.         @Override  
  225.         protected void onPostExecute(String result) {  
  226.             // TODO Auto-generated method stub  
  227.             super.onPostExecute(result);  
  228.               
  229.             statusLabel.setText(result);  
  230.               
  231.             //將發送框清空  
  232.             etSend.setText("");  
  233.         }  
  234.   
  235.         @Override  
  236.         protected String doInBackground(String... arg0) {  
  237.             // TODO Auto-generated method stub  
  238.               
  239.             if(btSocket==null)  
  240.             {  
  241.                 return "還沒有創建連接";  
  242.             }  
  243.               
  244.             if(arg0[0].length()>0)//不是空白串  
  245.             {  
  246.                      //String target=arg0[0];  
  247.               
  248.                       byte[] msgBuffer = arg0[0].getBytes();   
  249.                   
  250.                       try {   
  251.                       //  將msgBuffer中的數據寫到outStream對象中  
  252.                       outStream.write(msgBuffer);   
  253.                    
  254.                        } catch (IOException e) {   
  255.                            Log.e("error""ON RESUME: Exception during write.", e);  
  256.                            return "發送失敗";  
  257.                    }   
  258.   
  259.             }  
  260.               
  261.             return "發送成功";  
  262.         }  
  263.           
  264.     }  
  265.       
  266.       
  267.     //從藍牙接收信息的線程  
  268.     class ReceiveThread extends Thread  
  269.     {  
  270.   
  271.         String buffer="";  
  272.           
  273.         @Override  
  274.         public void run() {  
  275.               
  276.             while(btSocket!=null )  
  277.             {        
  278.                     //定義一個存儲空間buff  
  279.                     byte[] buff=new byte[1024];  
  280.                     try {  
  281.                         inStream = btSocket.getInputStream();   
  282.                         System.out.println("waitting for instream");   
  283.                         inStream.read(buff); //讀取數據存儲在buff數組中  
  284. //                        System.out.println("buff receive :"+buff.length);  
  285.                           
  286.                             
  287.                          processBuffer(buff,1024);   
  288.                            
  289.                         //System.out.println("receive content:"+ReceiveData);  
  290.                     } catch (IOException e) {  
  291.                           
  292.                         e.printStackTrace();  
  293.                     }  
  294.             }         
  295.         }     
  296.           
  297.         private void processBuffer(byte[] buff,int size)  
  298.         {  
  299.             int length=0;  
  300.             for(int i=0;i<size;i++)  
  301.             {  
  302.                 if(buff[i]>'\0')  
  303.                 {  
  304.                     length++;  
  305.                 }  
  306.                 else  
  307.                 {  
  308.                     break;  
  309.                 }  
  310.             }  
  311.               
  312. //          System.out.println("receive fragment size:"+length);   
  313.               
  314.             byte[] newbuff=new byte[length];  //newbuff字節數組,用於存放真正接收到的數據  
  315.               
  316.             for(int j=0;j<length;j++)  
  317.             {  
  318.                 newbuff[j]=buff[j];  
  319.             }         
  320.               
  321.             ReceiveData=ReceiveData+new String(newbuff);  
  322.             Log.e("Data",ReceiveData);  
  323. //          System.out.println("result :"+ReceiveData);  
  324.                         Message msg=Message.obtain();  
  325.             msg.what=1;   
  326.             handler.sendMessage(msg);  //發送消息:系統會自動調用handleMessage( )方法來處理消息    
  327.               
  328.         }  
  329.           
  330.     }  
  331.       
  332.   
  333.       
  334.     //更新界面的Handler類  
  335.     class MyHandler extends Handler{  
  336.   
  337.         @Override  
  338.         public void handleMessage(Message msg) {  
  339.   
  340.             switch(msg.what){  
  341.             case 1:  
  342.                 etReceived.setText(ReceiveData);  
  343.                 break;  
  344.             }  
  345.         }     
  346.     }  
  347.   
  348.     @Override  
  349.     protected void onDestroy() {  
  350.         // TODO Auto-generated method stub  
  351.         super.onDestroy();  
  352.           
  353.         try {  
  354.             if(rThread!=null)  
  355.             {  
  356.                   
  357.                 btSocket.close();  
  358.                 btSocket=null;  
  359.                   
  360.                 rThread.join();  
  361.             }  
  362.               
  363.             this.finish();  
  364.               
  365.         } catch (IOException e) {  
  366.             // TODO Auto-generated catch block  
  367.             e.printStackTrace();  
  368.         } catch (InterruptedException e) {  
  369.             // TODO Auto-generated catch block  
  370.             e.printStackTrace();  
  371.         }         
  372.           
  373.     }  
  374.   
  375. }  
package com.ywq;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;
import com.example.alltest.R;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

	//定義組件
	TextView statusLabel; 
	Button btnConnect,btnSend,btnQuit;
	EditText etReceived,etSend;
	
	//device var
	private BluetoothAdapter mBluetoothAdapter = null;  
	  
    private BluetoothSocket btSocket = null;  
	 
    private OutputStream outStream = null;  
	      
	private InputStream inStream = null;  
  
	//這條是藍牙串口通用的UUID,不要更改  
	private static final UUID MY_UUID = 
			UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");  
	  
    private static String address = "20:16:07:26:18:46"; // <==要連接的目標藍牙設備MAC地址  

    
    private ReceiveThread rThread=null;  //數據接收線程
    
    //接收到的字符串
    String ReceiveData="";
    
    MyHandler handler;
    
    
      
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//首先調用初始化函數
		Init(); 
		InitBluetooth();
		
		handler=new MyHandler();		
		
		btnConnect.setOnClickListener(new View.OnClickListener() {  
			@Override
			public void onClick(View v) {
				//判斷藍牙是否打開
				if(!mBluetoothAdapter.isEnabled())   
		        {  
		           mBluetoothAdapter.enable();
		        }
		    	 mBluetoothAdapter.startDiscovery();		    	
	
			//創建連接	
			new ConnectTask().execute(address);
				
			}
		});
		
		
		btnQuit.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				
				if(btSocket!=null)
				{
					try {
						btSocket.close();
						btSocket=null;
						if(rThread!=null)
						{
							rThread.join();
						}	
						statusLabel.setText("當前連接已斷開");
//						etReceived.setText("");
					} catch (IOException e) {
						
						e.printStackTrace();
					} catch (InterruptedException e) {

						e.printStackTrace();
					}
				}
				
				
				
			}
		});
		
		btnSend.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				new SendInfoTask().execute(etSend.getText().toString());
								
			}
		});
	}
		
	public void Init()
	{
		statusLabel=(TextView)this.findViewById(R.id.textView1);
		btnConnect=(Button)this.findViewById(R.id.button1);
		btnSend=(Button)this.findViewById(R.id.button2);
		btnQuit=(Button)this.findViewById(R.id.button3);
		etSend=(EditText)this.findViewById(R.id.editText1);
		etReceived=(EditText)this.findViewById(R.id.editText2);
	}

	public void InitBluetooth()
	{
		//得到一個藍牙適配器
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
	        if(mBluetoothAdapter == null)   
		        {  
		            Toast.makeText(this, "你的手機不支持藍牙", Toast.LENGTH_LONG).show();  
		            finish();  
		            return;  
		        }  

	}
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	//連接藍牙設備的異步任務
	class ConnectTask extends AsyncTask<String,String,String>
		{
			

			@Override
			protected String doInBackground(String... params) {
				// TODO Auto-generated method stub
			 BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(params[0]);  
				 
			        try {  
			  
			            btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);  
		  
			       		  
			            btSocket.connect();  
			  
			            Log.e("error", "ON RESUME: BT connection established, data transfer link open.");  
			  
			        } catch (IOException e) {  
			 
			           try {  
			                btSocket.close(); 
			                return "Socket 創建失敗";
	  
			            } catch (IOException e2) {  
			            	
			               Log .e("error","ON RESUME: Unable to close socket during connection failure", e2);
			               return "Socket 關閉失敗";
			           }  
			  
			        } 
			        //取消搜索
			        mBluetoothAdapter.cancelDiscovery();  
			        
			        try { 
			             outStream = btSocket.getOutputStream(); 
			 			        	       
			           } catch (IOException e) { 
			             Log.e("error", "ON RESUME: Output stream creation failed.", e);
			             return "Socket 流創建失敗";
			           } 
                      
                    
			        return "藍牙連接正常,Socket 創建成功";
			}

			@Override    //這個方法是在主線程中運行的,所以可以更新界面
			protected void onPostExecute(String result) {
				// TODO Auto-generated method stub
				
				//連接成功則啓動監聽	
				rThread=new ReceiveThread();
				
				rThread.start();
				
				statusLabel.setText(result);
				
				super.onPostExecute(result);
			}
			
			
			
		}
	
	//發送數據到藍牙設備的異步任務
	class SendInfoTask extends AsyncTask<String,String,String>
	{

		@Override
		protected void onPostExecute(String result) {
			// TODO Auto-generated method stub
			super.onPostExecute(result);
			
			statusLabel.setText(result);
			
			//將發送框清空
			etSend.setText("");
		}

		@Override
		protected String doInBackground(String... arg0) {
			// TODO Auto-generated method stub
			
			if(btSocket==null)
			{
				return "還沒有創建連接";
			}
			
			if(arg0[0].length()>0)//不是空白串
			{
				     //String target=arg0[0];
			
				      byte[] msgBuffer = arg0[0].getBytes(); 
				
				      try { 
                      //  將msgBuffer中的數據寫到outStream對象中
			          outStream.write(msgBuffer); 
				 
				       } catch (IOException e) { 
				           Log.e("error", "ON RESUME: Exception during write.", e);
				           return "發送失敗";
			       } 

			}
			
			return "發送成功";
		}
		
	}
	
	
	//從藍牙接收信息的線程
	class ReceiveThread extends Thread
	{

		String buffer="";
		
		@Override
		public void run() {
			
			while(btSocket!=null )
			{	   
				    //定義一個存儲空間buff
                    byte[] buff=new byte[1024];
                    try {
                    	inStream = btSocket.getInputStream(); 
                    	System.out.println("waitting for instream"); 
                        inStream.read(buff); //讀取數據存儲在buff數組中
//                        System.out.println("buff receive :"+buff.length);
                        
                          
                         processBuffer(buff,1024); 
                         
                        //System.out.println("receive content:"+ReceiveData);
                    } catch (IOException e) {
                        
                        e.printStackTrace();
                    }
			}		
		}	
		
		private void processBuffer(byte[] buff,int size)
		{
			int length=0;
			for(int i=0;i<size;i++)
			{
				if(buff[i]>'\0')
				{
					length++;
				}
				else
				{
					break;
				}
			}
			
//			System.out.println("receive fragment size:"+length); 
			
			byte[] newbuff=new byte[length];  //newbuff字節數組,用於存放真正接收到的數據
			
			for(int j=0;j<length;j++)
			{
				newbuff[j]=buff[j];
			}		
			
			ReceiveData=ReceiveData+new String(newbuff);
			Log.e("Data",ReceiveData);
//			System.out.println("result :"+ReceiveData);
						Message msg=Message.obtain();
	        msg.what=1; 
	        handler.sendMessage(msg);  //發送消息:系統會自動調用handleMessage( )方法來處理消息  
			
		}
		
	}
	

	
	//更新界面的Handler類
	class MyHandler extends Handler{

		@Override
		public void handleMessage(Message msg) {

			switch(msg.what){
			case 1:
				etReceived.setText(ReceiveData);
				break;
			}
		}	
	}

	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
		
		try {
			if(rThread!=null)
			{
				
				btSocket.close();
				btSocket=null;
				
				rThread.join();
			}
			
			this.finish();
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		
	}

}

 

BluetoothReceiver.java

  1. package com.ywq.broadcast;  
  2.   
  3. import com.ywq.tools.ClsUtils;  
  4. import android.bluetooth.BluetoothDevice;  
  5. import android.content.BroadcastReceiver;  
  6. import android.content.Context;  
  7. import android.content.Intent;  
  8. import android.util.Log;  
  9.   
  10.   
  11. public class BluetoothReceiver extends BroadcastReceiver{  
  12.   
  13.     String pin = "1234";  //此處爲你要連接的藍牙設備的初始密鑰,一般爲1234或0000  
  14.     public BluetoothReceiver() {  
  15.           
  16.     }  
  17.   
  18.     //廣播接收器,當遠程藍牙設備被發現時,回調函數onReceiver()會被執行   
  19.     @Override  
  20.     public void onReceive(Context context, Intent intent) {  
  21.           
  22.         String action = intent.getAction(); //得到action  
  23.         Log.e("action1=", action);  
  24.         BluetoothDevice btDevice=null;  //創建一個藍牙device對象  
  25.          // 從Intent中獲取設備對象  
  26.         btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);   
  27.           
  28.         if(BluetoothDevice.ACTION_FOUND.equals(action)){  //發現設備  
  29.             Log.e("發現設備:""["+btDevice.getName()+"]"+":"+btDevice.getAddress());  
  30.               
  31.             if(btDevice.getName().contains("HC-05"))//HC-05設備如果有多個,第一個搜到的那個會被嘗試。  
  32.             {  
  33.                 if (btDevice.getBondState() == BluetoothDevice.BOND_NONE) {    
  34.                       
  35.                     Log.e("ywq""attemp to bond:"+"["+btDevice.getName()+"]");  
  36.                     try {  
  37.                         //通過工具類ClsUtils,調用createBond方法  
  38.                         ClsUtils.createBond(btDevice.getClass(), btDevice);  
  39.                     } catch (Exception e) {  
  40.                         // TODO Auto-generated catch block  
  41.                         e.printStackTrace();  
  42.                     }  
  43.                 }  
  44.             }else  
  45.                 Log.e("error""Is faild");  
  46.         }else if(action.equals("android.bluetooth.device.action.PAIRING_REQUEST")) //再次得到的action,會等於PAIRING_REQUEST  
  47.         {  
  48.             Log.e("action2=", action);  
  49.             if(btDevice.getName().contains("HC-05"))  
  50.             {  
  51.                 Log.e("here""OKOKOK");  
  52.                   
  53.                 try {  
  54.                       
  55.                     //1.確認配對  
  56.                     ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);  
  57.                     //2.終止有序廣播  
  58.                     Log.i("order...""isOrderedBroadcast:"+isOrderedBroadcast()+",isInitialStickyBroadcast:"+isInitialStickyBroadcast());  
  59.                     abortBroadcast();//如果沒有將廣播終止,則會出現一個一閃而過的配對框。  
  60.                     //3.調用setPin方法進行配對...  
  61.                     boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, pin);  
  62.                   
  63.                 } catch (Exception e) {  
  64.                     // TODO Auto-generated catch block  
  65.                     e.printStackTrace();  
  66.                 }  
  67.             }else  
  68.                 Log.e("提示信息""這個設備不是目標藍牙設備");  
  69.               
  70.         }  
  71.     }  
  72. }  
package com.ywq.broadcast;

import com.ywq.tools.ClsUtils;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;


public class BluetoothReceiver extends BroadcastReceiver{

	String pin = "1234";  //此處爲你要連接的藍牙設備的初始密鑰,一般爲1234或0000
	public BluetoothReceiver() {
		
	}

	//廣播接收器,當遠程藍牙設備被發現時,回調函數onReceiver()會被執行 
	@Override
	public void onReceive(Context context, Intent intent) {
		
		String action = intent.getAction(); //得到action
		Log.e("action1=", action);
		BluetoothDevice btDevice=null;  //創建一個藍牙device對象
		 // 從Intent中獲取設備對象
		btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 
		
		if(BluetoothDevice.ACTION_FOUND.equals(action)){  //發現設備
			Log.e("發現設備:", "["+btDevice.getName()+"]"+":"+btDevice.getAddress());
			
			if(btDevice.getName().contains("HC-05"))//HC-05設備如果有多個,第一個搜到的那個會被嘗試。
			{
				if (btDevice.getBondState() == BluetoothDevice.BOND_NONE) {  
					
					Log.e("ywq", "attemp to bond:"+"["+btDevice.getName()+"]");
					try {
						//通過工具類ClsUtils,調用createBond方法
						ClsUtils.createBond(btDevice.getClass(), btDevice);
					} catch (Exception e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
                }
			}else
				Log.e("error", "Is faild");
		}else if(action.equals("android.bluetooth.device.action.PAIRING_REQUEST")) //再次得到的action,會等於PAIRING_REQUEST
		{
			Log.e("action2=", action);
			if(btDevice.getName().contains("HC-05"))
			{
				Log.e("here", "OKOKOK");
				
				try {
					
					//1.確認配對
					ClsUtils.setPairingConfirmation(btDevice.getClass(), btDevice, true);
					//2.終止有序廣播
					Log.i("order...", "isOrderedBroadcast:"+isOrderedBroadcast()+",isInitialStickyBroadcast:"+isInitialStickyBroadcast());
					abortBroadcast();//如果沒有將廣播終止,則會出現一個一閃而過的配對框。
					//3.調用setPin方法進行配對...
					boolean ret = ClsUtils.setPin(btDevice.getClass(), btDevice, pin);
				
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}else
				Log.e("提示信息", "這個設備不是目標藍牙設備");
			
		}
	}
}


配對工具類ClsUtils.java如下:

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

/************************************ 藍牙配對函數 * **************/

import java.lang.reflect.Method;  
import java.lang.reflect.Field;  
import android.bluetooth.BluetoothDevice;  
import android.util.Log;  
  
public class ClsUtils   
{  
    /** 
     * 與設備配對 參考源碼:platform/packages/apps/Settings.git 
     * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java 
     */  
    static public boolean createBond(Class btClass, BluetoothDevice btDevice)  
    throws Exception  
    {  
        Method createBondMethod = btClass.getMethod("createBond");  
        Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);  
        return returnValue.booleanValue();  
    }  
   
    /** 
     * 與設備解除配對 參考源碼:platform/packages/apps/Settings.git 
     * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java 
     */  
    static public boolean removeBond(Class<?> btClass, BluetoothDevice btDevice)  
            throws Exception  
    {  
        Method removeBondMethod = btClass.getMethod("removeBond");  
        Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);  
        return returnValue.booleanValue();  
    }  
   
    static public boolean setPin(Class<? extends BluetoothDevice> btClass, BluetoothDevice btDevice,  
            String str) throws Exception  
    {  
        try  
        {  
            Method removeBondMethod = btClass.getDeclaredMethod("setPin",  
                    new Class[]  
                    {byte[].class});  
            Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice,  
                    new Object[]  
                    {str.getBytes()});  
            Log.e("returnValue", "" + returnValue);  
        }  
        catch (SecurityException e)  
        {  
            // throw new RuntimeException(e.getMessage());  
            e.printStackTrace();  
        }  
        catch (IllegalArgumentException e)  
        {  
            // throw new RuntimeException(e.getMessage());  
            e.printStackTrace();  
        }  
        catch (Exception e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
        return true;  
   
    }  
   
    // 取消用戶輸入  
    static public boolean cancelPairingUserInput(Class<?> btClass,  
            BluetoothDevice device)  throws Exception  
    {  
        Method createBondMethod = btClass.getMethod("cancelPairingUserInput");  
//        cancelBondProcess(btClass, device);
        Boolean returnValue = (Boolean) createBondMethod.invoke(device);  
        return returnValue.booleanValue();  
    }  
   
    // 取消配對  
    static public boolean cancelBondProcess(Class<?> btClass,  
            BluetoothDevice device)  
   
    throws Exception  
    {  
        Method createBondMethod = btClass.getMethod("cancelBondProcess");  
        Boolean returnValue = (Boolean) createBondMethod.invoke(device);  
        return returnValue.booleanValue();  
    } 
    
    //確認配對
    
    static public void setPairingConfirmation(Class<?> btClass,BluetoothDevice device,boolean isConfirm)throws Exception 
    {
    	Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation",boolean.class); 
    	setPairingConfirmation.invoke(device,isConfirm);
    }
    
   
    /** 
     * 
     * @param clsShow 
     */  
    static public void printAllInform(Class clsShow)  
    {  
        try  
        {  
            // 取得所有方法  
            Method[] hideMethod = clsShow.getMethods();  
            int i = 0;  
            for (; i < hideMethod.length; i++)  
            {  
                Log.e("method name", hideMethod[i].getName() + ";and the i is:"  
                        + i);  
            }
            // 取得所有常量  
            Field[] allFields = clsShow.getFields();  
            for (i = 0; i < allFields.length; i++)  
            {  
                Log.e("Field name", allFields[i].getName());  
            }
        }  
        catch (SecurityException e)  
        {  
            // throw new RuntimeException(e.getMessage());  
            e.printStackTrace();  
        }  
        catch (IllegalArgumentException e)  
        {  
            // throw new RuntimeException(e.getMessage());  
            e.printStackTrace();  
        }  
        catch (Exception e)  
        {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}  


配置文件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.alltest"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     <uses-sdk  
  8.         android:minSdkVersion="8"  
  9.         android:targetSdkVersion="21" />  
  10.       
  11.     <!-- 藍牙使用權限 -->  
  12.     <uses-permission android:name="android.permission.BLUETOOTH"/>  
  13.     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>  
  14.       
  15.       
  16.     <application  
  17.         android:allowBackup="true"  
  18.         android:icon="@drawable/ic_launcher"  
  19.         android:label="@string/app_name"  
  20.         android:theme="@style/AppTheme" >  
  21.           
  22.         <activity  
  23.             android:name="com.ywq.MainActivity"  
  24.             android:label="@string/app_name" >  
  25.             <intent-filter>  
  26.                 <action android:name="android.intent.action.MAIN" />  
  27.                 <category android:name="android.intent.category.LAUNCHER" />  
  28.             </intent-filter>  
  29.         </activity>  
  30.           
  31.         <!-- 廣播接收 -->  
  32.         <receiver android:name="com.ywq.broadcast.BluetoothReceiver" >  
  33.             <intent-filter android:priority="1000">  
  34.                 <action android:name="android.bluetooth.device.action.PAIRING_REQUEST"/>  
  35.                 <action android:name="android.bluetooth.device.action.FOUND" />  
  36.             </intent-filter>  
  37.         </receiver>  
  38.           
  39.     </application>  
  40.    
  41. </manifest>  
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.alltest"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />
    
    <!-- 藍牙使用權限 -->
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        
        <activity
            android:name="com.ywq.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!-- 廣播接收 -->
        <receiver android:name="com.ywq.broadcast.BluetoothReceiver" >
    		<intent-filter android:priority="1000">
        		<action android:name="android.bluetooth.device.action.PAIRING_REQUEST"/>
        		<action android:name="android.bluetooth.device.action.FOUND" />
    		</intent-filter>
		</receiver>
        
    </application>
 
</manifest>


佈局文件如下:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:id="@+id/LinearLayout1"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:orientation="vertical"  
  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=".MainActivity" >  
  12.          
  13.     <LinearLayout  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content" >  
  16.   
  17.         <Button  
  18.             android:id="@+id/button1"  
  19.             android:layout_width="wrap_content"  
  20.             android:layout_height="wrap_content"  
  21.             android:text="連接" />  
  22.           
  23.   
  24.         <TextView  
  25.             android:id="@+id/textView1"  
  26.             android:layout_width="wrap_content"  
  27.             android:layout_height="wrap_content"  
  28.             android:text="當前沒有連接任何設備" />  
  29.   
  30.     </LinearLayout>  
  31.   
  32.     <LinearLayout  
  33.         android:layout_width="match_parent"  
  34.         android:layout_height="wrap_content" >  
  35.   
  36.         <EditText  
  37.             android:id="@+id/editText1"  
  38.             android:layout_width="wrap_content"  
  39.             android:layout_height="wrap_content"  
  40.             android:layout_weight="1"  
  41.             android:ems="10" >  
  42.   
  43.             <requestFocus />  
  44.         </EditText>  
  45.   
  46.         <Button  
  47.             android:id="@+id/button2"  
  48.             android:layout_width="wrap_content"  
  49.             android:layout_height="wrap_content"  
  50.             android:layout_weight="1"  
  51.             android:text="發送" />  
  52.   
  53.     </LinearLayout>  
  54.   
  55.     <LinearLayout  
  56.         android:layout_width="match_parent"  
  57.         android:layout_height="wrap_content" >  
  58.   
  59.         <TextView  
  60.             android:id="@+id/textView2"  
  61.             android:layout_width="wrap_content"  
  62.             android:layout_height="wrap_content"  
  63.             android:text="接收到的數據" />  
  64.   
  65.     </LinearLayout>  
  66.   
  67.     <LinearLayout  
  68.         android:layout_width="match_parent"  
  69.         android:layout_height="wrap_content" >  
  70.   
  71.         <EditText  
  72.             android:id="@+id/editText2"  
  73.             android:layout_width="wrap_content"  
  74.             android:layout_height="300dp"  
  75.             android:layout_weight="1"  
  76.             android:ems="10"  
  77.             android:inputType="textMultiLine" />  
  78.   
  79.     </LinearLayout>  
  80.   
  81.     <LinearLayout  
  82.         android:layout_width="match_parent"  
  83.         android:layout_height="wrap_content" >  
  84.   
  85.         <Button  
  86.             android:id="@+id/button3"  
  87.             android:layout_width="wrap_content"  
  88.             android:layout_height="wrap_content"  
  89.             android:text="斷開連接" />  
  90.   
  91.     </LinearLayout>  
  92.   
  93. </LinearLayout>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/LinearLayout1"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >
       
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="連接" />
        

        <TextView
            android:id="@+id/textView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="當前沒有連接任何設備" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/editText1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ems="10" >

            <requestFocus />
        </EditText>

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="發送" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            android:id="@+id/textView2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="接收到的數據" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <EditText
            android:id="@+id/editText2"
            android:layout_width="wrap_content"
            android:layout_height="300dp"
            android:layout_weight="1"
            android:ems="10"
            android:inputType="textMultiLine" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="斷開連接" />

    </LinearLayout>

</LinearLayout>


 程序分析:

       程序主要分爲:自動配對==>>建立連接==>>開啓線程監聽是否收到信息==>>向Arduino 端發送信息==>>斷開連接


 自動配對:通過mBluetoothAdapter.startDiscovery();來實現。我們來看一下API中對該方法的描述:

 

public boolean startDiscovery ()

開始對遠程設備進行查找的進程

它通常牽涉到一個大概需時12秒的查詢掃描過程,緊跟着是一個對每個獲取到自身藍牙名稱的新設備的頁面掃描。

這是一個異步調用方法:該方法將馬上獲得返回值,註冊ACTION_DISCOVERY_STARTED and

ACTION_DISCOVERY_FINISHED意圖準確地確定該探索是處於開始階段或者完成階段。註冊ACTION_FOUND以活動遠程藍牙設

備 已找到的通知。

設備查找是一個重量級過程。當查找正在進行的時候,用戶不能嘗試對新的遠程藍牙設備進行連接,同時存在的連接將獲得有限制

的帶寬以 及高等待時間。用戶可用cencelDiscovery()類來取消正在執行的查找進程。發現的過程不會由活動來進行管理,但是它會

作爲一個系統服務來運 行,因此即使它不能直接請求這樣的一個查詢動作,也必需取消該搜索進程。

設備搜尋只尋找已經被連接的遠程設備。許多藍牙設備默認不會被搜尋到,並且需要進入到一個特殊的模式當中。

如果藍牙狀態不是STATE_ON,這個API將返回false。藍牙打開後,等待ACTION_STATE_CHANGED更新成STATE_ON。

需要BLUETOOTH_ADMIN權限。

返回值

成功返回true,錯誤返回false。

 

 由上面我們可以看出,當調用該方法並且發現設備時,將執行我們自定義的廣播接收類中的onReceiver()會被執行,實現自動配對具體可以參考:

                                                                   Android藍牙自動配對Demo,親測好使!!!


建立連接:使用了一個異步AsyncTask任務。關於AsyncTask的使用,可以參考本博客:

                                                                                android AsyncTask介紹

 我們首先利用遠程藍牙的mac地址得到了遠程藍牙設備:

  1. BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);  
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

其次利用UUID得到了一個BluetoothSocket對象:

  1. btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);   
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID); 

然後調用connect()方法建立了socket連接

最後通過:

  1. mBluetoothAdapter.cancelDiscovery();  
mBluetoothAdapter.cancelDiscovery();

來取消了搜索。



開啓線程,監聽輸入:當socket創建成功後,需要監聽輸入。

我們首先通過BluetoothSocket對象得到輸入流。

  1. inStream = btSocket.getInputStream();   
inStream = btSocket.getInputStream(); 

通過read()方法來讀取來自Arduino端的信息。其中,read()方法是一個可以阻塞的方法。阻塞的意思是,當輸入流中沒有數據

傳來時,該方法被阻塞,程序不會執行下邊的內容,直到有數據傳來。

如果有數據傳來,則通過Message和Handler來更新UI,實現數據的顯示。

 

向Arduino發送信息:同樣適用了AsyncTask類來實現,  android AsyncTask介紹  。

當我們點擊發送按鈕時,首先判斷socket是否成功創建,成功則使用輸出流發送信息。否則,給出提示。


 斷開連接:

首先我們需要通過btSocket.close( )來關閉socket,然後調用線程的join( )來將線程停止。實現了藍牙之間的斷開連接操作。

 

 

 源碼下載地址:http://download.csdn.net/download/qq_25827845/9757403

 至此,我們學習了Android藍牙的搜索、配對、連接、通信,對藍牙開發有了一個較爲初步的認識。

 

如果對你有幫助,記得點贊哦~歡迎大家關注我的博客,可以進羣366533258一起交流學習哦~

 

 

 

 

 

 

發佈了78 篇原創文章 · 獲贊 7 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章