Android 藍牙開發之搜索、配對、鏈接、通訊大全

 

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

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

行通訊,固然並非每個藍牙均可以達到最大值。下面,咱們從藍牙的基本概念開始,一步一步開始瞭解藍牙。android

 

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

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


基本概念: api

         安卓平臺提供對藍牙的通信棧的支持,容許設別和其餘的設備進行無線傳輸數據。應用程序層經過安卓API來調用藍牙的相關功數組

能,這些API使程序無線鏈接到藍牙設備,並擁有P2P或者多端無線連接的特性。安全

 

藍牙的功能:

一、掃描其餘藍牙設備

二、爲可配對的藍牙設備查詢藍牙適配器

三、創建RFCOMM通道

四、經過服務搜索來連接其餘的設備

五、與其餘的設備進行數據傳輸

六、管理多個鏈接
服務器



藍牙創建鏈接必需要求:

一、打開藍牙

二、查找附近已配對或可用設備

三、鏈接設備

四、設備間數據交換
app

 

 

經常使用的藍牙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、藍牙搜索功能的實現:

 

一、獲得藍牙適配器:

BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();

若mBluetoothAdapter爲 null,則說明當前手機不支持藍牙功能(如今幾乎全部手機都支持了吧。。。)

 

二、判斷藍牙是否打開:

if (!mBluetoothAdapter.isEnabled()) {
       //若沒打開則打開藍牙
       mBluetoothAdapter.enable();
  
}

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

(1)沒有任何提示,直接打開了藍牙。如Nexus 5 Android 4.4.4 手機。

(2)會彈出提示框,提示安全警告 「 ***應用嘗試開啓藍牙」,能夠選擇「拒絕」或「容許」。大多數手機都是這樣的。

(3)強制打開藍牙失敗,而且沒有任何提示。

 

 

三、註冊藍牙搜索廣播接收者:

(1)Android 的廣播機制:

     Adnroid的廣播機制(以intent對象的形式廣播出去),Android系統廣播的時候不會關心你是否收穫得消息、只負責廣播出去,而

且廣播的對象只是在應用程序中註冊了的廣播接收器。咱們要作的就是自定義廣播接收器並將其註冊給應用程序,在廣播接收器中

將接收到廣播事件做出相應的處理。若是廣播的事件並非咱們定義的廣播接收器須要的事件類型,通常是會過濾掉不被接收。只

有當廣播事件和咱們寫的接收器定義的接收的事件類型一致的時候纔會觸發廣播接收器。而且觸發廣播接收器的onReceive方法。當

然咱們自定義的廣播接收器須要接受事件的類型是在XML清單文件的<intent-filter>中本身定義聲明的或者本身在程序代碼中定義一

個IntentFilter對象而後經過對象的addAction()方法來自定義接收事件類型。而後咱們須要將接收到的事件的處理代碼寫在onReceive

方法中。

(2)註冊分爲兩種:靜態註冊和動態註冊。

  • 靜態註冊就是在AndroidManifest.xml文件中定義,註冊的廣播接收器必須繼承BroadReceiver
  • 動態註冊就是在程序中使用Context.registerReceiver註冊。

 

咱們先演示動態註冊:

//註冊設備被發現時的廣播
IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver,filter);
//註冊一個搜索結束時的廣播
IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(mReceiver,filter2);


對應的靜態註冊以下:

<!-- 廣播接收 -->
        <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

 

 

四、定義廣播接收:

自定義的廣播接收器對象必需要繼承BroadcastReceiver,而後重寫onReceive方法,處理接收的數據的代碼就寫在這個方法裏面。

兩種方法:

  • 自定義一個類實現BroadcastReceiver抽象類,而且實現其onReceiver(Context context, Intent intent )方法。
  • 直接new BroadcastReceiver()來搞定。

方法1以下:

public class BluetoothReceiver extends BroadcastReceiver{
        @Override
	public void onReceive(Context context, Intent intent) {

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

}

方法2以下:

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

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


 五、開始廣播:

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

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

 

六、解除註冊:

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

 

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

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

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

代碼以下:

mainActivity.java

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代碼以下:

<?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代碼以下:

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


程序運行結果以下:


 

 

2、藍牙自動配對功能實現:

 

藍牙配對是創建鏈接的基礎和前提。爲何不配對便沒法創建鏈接?

        任何無線通訊技術都存在被監聽和破解的可能,藍牙SIG爲了保證藍牙通訊的安全性,採用認證的方式進行數據交互。同時爲

了保證使用的方便性,以配對的形式完成兩個藍牙設備之間的首次通信認證,經配對以後,隨後的通信鏈接就沒必要每次都要作確

認。因此認證碼的產生是從配對開始的,通過配對,設備之間以PIN碼創建約定的link key用於產生初始認證碼,以用於之後創建的

鏈接。

       因此若是不配對,兩個設備之間便沒法創建認證關係,沒法進行鏈接及其以後的操做,因此配對在必定程度上保證了藍牙通訊

的安全,固然這個安全保證機制是比較容易被破解的,由於如今不少我的設備沒有人機接口,因此PIN碼都是固定的並且大都設置爲

通用的0000或者1234之類的,因此很容易被猜到並進而創建配對和鏈接。

 

關於藍牙的自動配對,你們能夠參考個人這篇博客:Android藍牙自動配對Demo,親測好使!!!

這裏自詡一下,這篇博客仍是受到了你們的一些好評。該自動配對方法,博主在魅藍、華爲、聯想、紅米以及Nexus手機上都有測

試過,使用的Android系統包括4.0+和5.0+,因此各位能夠仔細閱讀該博客。

 

 

 

3、藍牙通訊的實現:

 

本文所述的藍牙通訊爲:Android 端藍牙設備與其餘藍牙設備之間的通訊

 

   下邊講述  Android手機端藍牙與Arduino外接藍牙模塊之間進行通訊。

 

(1)Arduino 端藍牙模塊

        藍牙模塊在Arduino 端只是一個串口,將藍牙模塊的Tx、Rx接在Arduino開發板上。

        初始化與Android藍牙通訊的串口,使用串口.read()來讀取來自手機藍牙的信息;使用串口.println(「XXXXXX」)來向手機

端藍牙發送信息。

 

Demo代碼以下:

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

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

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以下:

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以下:

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


佈局文件以下:

<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地址獲得了遠程藍牙設備:

BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);

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

btSocket = device.createRfcommSocketToServiceRecord(MY_UUID); 

而後調用connect()方法創建了socket鏈接

最後經過:

mBluetoothAdapter.cancelDiscovery();

來取消了搜索。



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

咱們首先經過BluetoothSocket對象獲得輸入流。

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一塊兒交流學習哦~

相關文章
相關標籤/搜索