Android -傳統藍牙通訊聊天

概述

Android 傳統藍牙的使用,包括開關藍牙、搜索設備、藍牙鏈接、通訊等。

詳細

原文地址:html

Android 藍牙開發(一)藍牙通訊 CSDNjava

Android 藍牙開發(一)藍牙通訊 簡書android

1、準備工做

 

開發環境:安全

jdk1.8服務器

Eclipse Luna Service Release 1 (4.4.1)異步

運行環境:socket

華爲榮耀6(Android4.4)、華爲p9(Android7.0)ide

實現功能:函數

  • Android 藍牙開發 (開關藍牙、搜索設備、藍牙配對、鏈接、通訊、斷開鏈接等)。ui

2、代碼結構

代碼包裏面,有兩個部分,一個是源碼,一個是V7支持包。

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

3、程序實現-藍牙通訊

1 藍牙基本操做

 

隨着可穿戴設備的流行,研究藍牙是必不可少的一門技術了。

總結了下藍牙開發使用的一些東西分享一下。

 

藍牙權限

首先須要AndroidManifest.xml文件中添加操做藍牙的權限。

<uses-permissionandroid:name="Android.permission.BLUETOOTH" />
//容許程序鏈接到已配對的藍牙設備。
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
//容許程序發現和配對藍牙設備。

 

BluetoothAdapter

操做藍牙主要用到的類 BluetoothAdapter類,使用時導包
import android.bluetooth.BluetoothAdapter;
源碼具體位置frameworks/base/core/Java/android/bluetooth/BluetoothAdapter.java

BluetoothAdapter 表明本地設備的藍牙適配器。該BluetoothAdapter能夠執行基本的藍牙任務,例如啓
動設備發現,查詢配對的設備列表,使用已知的MAC地址實例化一個BluetoothDevice類,並建立一個
BluetoothServerSocket監聽來自其餘設備的鏈接請求。

獲取藍牙適配器

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

開啓藍牙

if(!mBluetoothAdapter.isEnabled()){  
//彈出對話框提示用戶是後打開  
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
startActivityForResult(enabler, REQUEST_ENABLE);  
      //不作提示,直接打開,不建議用下面的方法,有的手機會有問題。  
      // mBluetoothAdapter.enable();  
}

 

獲取本地藍牙信息

//獲取本機藍牙名稱  
String name = mBluetoothAdapter.getName();  
//獲取本機藍牙地址  
String address = mBluetoothAdapter.getAddress();  
Log.d(TAG,"bluetooth name ="+name+" address ="+address);  
//獲取已配對藍牙設備  
Set<BluetoothDevice> devices = mBluetoothAdapter.getBondedDevices();  
Log.d(TAG, "bonded device size ="+devices.size());  
for(BluetoothDevice bonddevice:devices){  
    Log.d(TAG, "bonded device name ="+bonddevice.getName()+" address"+bonddevice.getAddress());  
}

搜索設備

mBluetoothAdapter.startDiscovery();

 

中止搜索

mBluetoothAdapter.cancelDiscovery();

搜索藍牙設備,該過程是異步的,經過下面註冊廣播接受者,能夠監聽是否搜到設備。

IntentFilter filter = new IntentFilter();  
//發現設備  
filter.addAction(BluetoothDevice.ACTION_FOUND);  
//設備鏈接狀態改變  
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);  
//藍牙設備狀態改變  
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);  
registerReceiver(mBluetoothReceiver, filter);

 

監聽掃描結果

經過廣播接收者查看掃描到的藍牙設備,每掃描到一個設備,系統都會發送此廣播(BluetoothDevice.ACTION_FOUNDE)。其中參數intent能夠獲取藍牙設備BluetoothDevice。

該demo中是鏈接指定名稱的藍牙設備,BLUETOOTH_NAME爲"Galaxy Nexus",若是掃描不到,記得改這個藍牙名稱。

private BroadcastReceiver mBluetoothReceiver = new BroadcastReceiver(){  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            String action = intent.getAction();  
            Log.d(TAG,"mBluetoothReceiver action ="+action);  
            if(BluetoothDevice.ACTION_FOUND.equals(action)){//每掃描到一個設備,系統都會發送此廣播。  
                //獲取藍牙設備  
                BluetoothDevice scanDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
                if(scanDevice == null || scanDevice.getName() == null) return;  
                Log.d(TAG, "name="+scanDevice.getName()+"address="+scanDevice.getAddress());  
                //藍牙設備名稱  
                String name = scanDevice.getName();  
                if(name != null && name.equals(BLUETOOTH_NAME)){  
                    mBluetoothAdapter.cancelDiscovery();  
                    //取消掃描  
                    mProgressDialog.setTitle(getResources().getString(R.string.progress_connecting));                   //鏈接到設備。  
                    mBlthChatUtil.connect(scanDevice);  
                }  
            }else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){  
  
            }
        }          
};

 

 

設置藍牙可見性

有時候掃描不到某設備,這是由於該設備對外不可見或者距離遠,須要設備該藍牙可見,這樣該才能被搜索到。

可見時間默認值爲120s,最多可設置300。

 

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

 

2 服務端

 

 

android 藍牙之間能夠經過SDP協議創建鏈接進行通訊,通訊方式相似於日常使用socket。

首先建立BluetoothServerSocket ,BluetoothAdapter中提供了兩種建立BluetoothServerSocket 方式,以下圖所示爲建立安全的RFCOMM Bluetooth socket,該鏈接是安全的須要進行配對。而經過listenUsingInsecureRfcommWithServiceRecord建立的RFCOMM Bluetooth socket是不安全的,鏈接時不須要進行配對。

其中的uuid須要服務器端和客戶端進行統一。

private class AcceptThread extends Thread {  
        // 本地服務器套接字  
        private final BluetoothServerSocket mServerSocket;  
        public AcceptThread() {           
            BluetoothServerSocket tmp = null;  
            // 建立一個新的偵聽服務器套接字  
            try {  
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(  
                        SERVICE_NAME, SERVICE_UUID);  
                //tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);  
            } catch (IOException e) {  
                Log.e(TAG, "listen() failed", e);  
            }  
            mServerSocket = tmp;  
        }  
  
        public void run() {  
            BluetoothSocket socket = null;  
            // 循環,直到鏈接成功  
            while (mState != STATE_CONNECTED) {  
                try {  
                    // 這是一個阻塞調用 返回成功的鏈接  
                    // mServerSocket.close()在另外一個線程中調用,能夠停止該阻塞  
                    socket = mServerSocket.accept();  
                } catch (IOException e) {  
                    Log.e(TAG, "accept() failed", e);  
                    break;  
                }  
                // 若是鏈接被接受  
                if (socket != null) {  
                    synchronized (BluetoothChatUtil.this) {  
                        switch (mState) {  
                        case STATE_LISTEN:  
                        case STATE_CONNECTING:  
                            // 正常狀況。啓動ConnectedThread。  
                            connected(socket, socket.getRemoteDevice());  
                            break;  
                        case STATE_NONE:  
                        case STATE_CONNECTED:  
                            // 沒有準備或已鏈接。新鏈接終止。  
                            try {  
                                socket.close();  
                            } catch (IOException e) {  
                                Log.e(TAG, "Could not close unwanted socket", e);  
                            }  
                            break;  
                        }  
                    }  
                }  
            }  
            if (D) Log.i(TAG, "END mAcceptThread");  
        }  
  
        public void cancel() {  
            if (D) Log.d(TAG, "cancel " + this);  
            try {  
                mServerSocket.close();  
            } catch (IOException e) {  
                Log.e(TAG, "close() of server failed", e);  
            }  
        }  
}

mServerSocket經過accept()等待客戶端的鏈接(阻塞),直到鏈接成功或失敗。

 

3 客戶端

 

客戶端主要用來建立RFCOMM socket,並鏈接服務端。

先掃描周圍的藍牙設備,若是掃描到指定設備則進行鏈接。mBlthChatUtil.connect(scanDevice)鏈接到設備,

鏈接過程主要在ConnectThread線程中進行,先建立socket,方式有兩種,

以下代碼中是安全的(createRfcommSocketToServiceRecord)。另外一種不安全鏈接對應的函數是createInsecureRfcommSocketToServiceRecord。

private class ConnectThread extends Thread {  
        private BluetoothSocket mmSocket;  
        private final BluetoothDevice mmDevice;  
        public ConnectThread(BluetoothDevice device) {  
            mmDevice = device;  
            BluetoothSocket tmp = null;  
            // 獲得一個bluetoothsocket  
            try {  
                mmSocket = device.createRfcommSocketToServiceRecord  
                        (SERVICE_UUID);  
            } catch (IOException e) {  
                Log.e(TAG, "create() failed", e);  
                mmSocket = null;  
            }  
        }  
  
        public void run() {  
            Log.i(TAG, "BEGIN mConnectThread");  
            try {   
                // socket 鏈接,該調用會阻塞,直到鏈接成功或失敗  
                mmSocket.connect();  
            } catch (IOException e) {  
                connectionFailed();  
                try {//關閉這個socket  
                    mmSocket.close();  
                } catch (IOException e2) {  
                    e2.printStackTrace();  
                }  
                return;  
            }  
            // 啓動鏈接線程  
            connected(mmSocket, mmDevice);  
        }  
  
        public void cancel() {  
            try {  
                mmSocket.close();  
            } catch (IOException e) {  
                Log.e(TAG, "close() of connect socket failed", e);  
            }  
        }  
}

接着客戶端socket主動鏈接服務端。鏈接過程當中會自動進行配對,須要雙方贊成才能夠鏈接成功。

 

4 數據傳輸

客戶端與服務端鏈接成功後都會調用connected(mmSocket, mmDevice),建立一個ConnectedThread線程()。

該線程主要用來接收和發送數據。客戶端和服務端處理方式同樣。該線程經過socket得到輸入輸出流。

 

private InputStream mmInStream = socket.getInputStream();

private OutputStream mmOutStream =socket.getOutputStream();

發送數據

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

 

接收數據

 

線程循環進行接收數據。

public void run() {  
    // 監聽輸入流  
    while (true) {  
        try {  
            byte[] buffer = new byte[1024];  
            // 讀取輸入流  
            int bytes = mmInStream.read(buffer);  
            // 發送得到的字節的ui activity  
            Message msg = mHandler.obtainMessage(MESSAGE_READ);  
            Bundle bundle = new Bundle();  
            bundle.putByteArray(READ_MSG, buffer);  
            msg.setData(bundle);  
            mHandler.sendMessage(msg);            
        } catch (IOException e) {  
            Log.e(TAG, "disconnected", e);  
                connectionLost();  
                break;  
            }  
        }  
}

 

4、運行效果

一、運行,右鍵項目:Run as -》Android Application (備註:Eclipse須要配置Android開發環境)

二、運行效果以下:

 

客戶端

1500550538898087682.jpeg

 

服務端

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

注:本文著做權歸做者,由demo大師發表,拒絕轉載,轉載須要做者受權

相關文章
相關標籤/搜索