1. 程式人生 > >Android 藍芽技術 帶你實現終端間資料傳輸

Android 藍芽技術 帶你實現終端間資料傳輸

藍芽技術在智慧硬體方面有很多用武之地,今天我就為大家分享一下藍芽在Android系統下的使用方法技巧,並實現一下兩個終端間資料的傳輸。

藍芽(Bluetooth)是一種短距離的無線通訊技術標準,藍芽協議分為4層,即核心協議層、電纜替代協議層、電話控制協議層和採納的其它協議層。

這4種協議中最重要的是核心協議。藍芽的核心協議包括基帶、鏈路管理、邏輯鏈路控制和適應協議四部分。其中鏈路管理(LMP)負責藍芽元件間連線的建立。邏輯鏈路控制與適應協議(L2CAP)位於基帶協議層上,屬於資料鏈路層,是一個為高層傳輸和應用層協議遮蔽基帶協議的適配協議。

1.開啟和關閉藍芽

第一種方法相對簡單,直接呼叫系統對話方塊啟動藍芽:

在AndroidManifest檔案中新增需要的許可權,高版本也不需要動態授權:

<uses-permission android:name="android.permission.BLUETOOTH" />
  • 1
  • 1
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 1);
  • 1
  • 1

第一種方法

如果不想讓使用者看到這個對話方塊,那麼我們還可以選擇第二種方法,進行靜默開啟藍芽。

第二種方法,靜默開啟,不會有方法一的對話方塊:

在AndroidManifest檔案中新增需要的許可權:

<!-- 已適配Android6.0 -->
<uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"
/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

由於藍芽所需要的許可權包含Dangerous Permissions,所以我們需要在Java程式碼中進行動態授權處理:

private static final int REQUEST_BLUETOOTH_PERMISSION=10;

private void requestBluetoothPermission(){
    //判斷系統版本
    if (Build.VERSION.SDK_INT >= 23) {
        //檢測當前app是否擁有某個許可權
        int checkCallPhonePermission = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.ACCESS_COARSE_LOCATION);
        //判斷這個許可權是否已經授權過
        if(checkCallPhonePermission != PackageManager.PERMISSION_GRANTED){
            //判斷是否需要 向用戶解釋,為什麼要申請該許可權
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, 
                    Manifest.permission.ACCESS_COARSE_LOCATION))
                Toast.makeText(this,"Need bluetooth permission.", 
                        Toast.LENGTH_SHORT).show();
            ActivityCompat.requestPermissions(this ,new String[]
                    {Manifest.permission.ACCESS_COARSE_LOCATION},REQUEST_BLUETOOTH_PERMISSION);
            return;
        }else{
        }
    } else {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

接下來我們就可以靜默開啟藍芽了:

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mBluetoothAdapter.enable(); //開啟
//mBluetoothAdapter.disable(); //關閉
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

下面我們來看一下如何通過程式碼搜尋藍芽裝置。

2.通過程式碼搜尋藍芽裝置

搜尋分為主動搜尋和被動搜尋。

我們開始進行主動搜尋:

(1)建立BluetoothAdapter物件

TextView tvDevices = (TextView)findViewById(R.id.tv_devices);
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  • 1
  • 2
  • 1
  • 2

(2)我們先獲取並顯示一下已經配對的藍芽裝置列表

//獲取已經配對的藍芽裝置
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
    for (BluetoothDevice device : pairedDevices) {
        tvDevices.append(device.getName() + ":" + device.getAddress());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

(3)下面我們定義廣播接收器

// 設定廣播資訊過濾
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_FOUND);//每搜尋到一個裝置就會發送一個該廣播
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//當全部搜尋完後傳送該廣播
filter.setPriority(Integer.MAX_VALUE);//設定優先順序
// 註冊藍芽搜尋廣播接收者,接收並處理搜尋結果
this.registerReceiver(receiver, filter);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

藍芽裝置的廣播接收器如下:

/**
 * 定義廣播接收器
 */
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                tvDevices.append(device.getName() + ":"+ device.getAddress());
            }
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            //已搜素完成
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

(4)我們建立一個Button按鈕,當點選Button時進行搜尋,Button點選事件如下:

//如果當前在搜尋,就先取消搜尋
if (mBluetoothAdapter.isDiscovering()) {
    mBluetoothAdapter.cancelDiscovery();
}
//開啟搜尋
mBluetoothAdapter.startDiscovery();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

搜尋藍芽裝置

3.藍芽的UUID

兩個藍芽裝置進行連線時需要使用同一個UUID。但很多讀者可能發現,有很多型號的手機(可能是非Android系統的手機)之間使用了不同的程式也可以使用藍芽進行通訊。從表面上看,它們之間幾乎不可能使用同一個UUID。

UUID的格式如下:

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

UUID的格式被分成5段,其中中間3段的字元數相同,都是4,第1段是8個字元,最後一段是12個字元。所以UUID實際上是一個8-4-4-4-12的字串。

實際上,UUID和TCP的埠一樣,也有一些預設的值。例如,將藍芽模擬成串列埠的服務就使用了一個標準的UUID:

00001101-0000-1000-8000-00805F9B34FB

除此之外,還有很多標準的UUID,如下面就是兩個標準的UUID:

資訊同步服務:00001104-0000-1000-8000-00805F9B34FB
檔案傳輸服務:00001106-0000-1000-8000-00805F9B34FB

4.藍芽終端間資料傳輸

通過藍芽傳輸資料與Socket類似。在網路中使用Socket和ServerSocket控制客戶端和服務端的資料讀寫。而藍芽通訊也由客戶端和服務端Socket來完成。藍芽客戶端Socket是BluetoothSocket,藍芽服務端Socket是BluetoothServerSocket。這兩個類都在android.bluetooth包中。

無論是BluetoothSocket,還是BluetoothServerSocket,都需要一個UUID(全域性唯一識別符號,Universally Unique Identifier),UUID相當於Socket的埠,而藍芽地址相當於Socket的IP。

我們開始進行模擬一個藍芽資料的傳輸:

首先來看客戶端:

(1)定義全域性常量變數

private ListView lvDevices;
private BluetoothAdapter mBluetoothAdapter;
private List<String> bluetoothDevices = new ArrayList<String>();
private ArrayAdapter<String> arrayAdapter;
private final UUID MY_UUID = UUID
        .fromString("abcd1234-ab12-ab12-ab12-abcdef123456");//隨便定義一個
private BluetoothSocket clientSocket;
private BluetoothDevice device;  
private OutputStream os;//輸出流
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

(2)在onCreate方法中做初始化操作

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

lvDevices = (ListView) findViewById(R.id.lv_devices);
//獲取已經配對的藍芽裝置
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
    for (BluetoothDevice device : pairedDevices) {
        bluetoothDevices.add(device.getName() + ":"+ device.getAddress());
    }
}
arrayAdapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, android.R.id.text1,bluetoothDevices);
lvDevices.setAdapter(arrayAdapter);
lvDevices.setOnItemClickListener(this);//Activity實現OnItemClickListener介面

//每搜尋到一個裝置就會發送一個該廣播
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(receiver, filter);
//當全部搜尋完後傳送該廣播
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(receiver, filter);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

藍芽裝置的廣播接收器如下:

/**
 * 定義廣播接收器
 */
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
                bluetoothDevices.add(device.getName() + ":" + device.getAddress());
                arrayAdapter.notifyDataSetChanged();//更新介面卡
            }

        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            //已搜素完成
        }
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

(4)我們建立一個Button按鈕,當點選Button時進行搜尋,Button點選事件如下:

//如果當前在搜尋,就先取消搜尋
if (mBluetoothAdapter.isDiscovering()) {
    mBluetoothAdapter.cancelDiscovery();
}
//開啟搜尋
mBluetoothAdapter.startDiscovery();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

(5)接下來我們設定列表的點選事件:

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    String s = arrayAdapter.getItem(position);
    String address = s.substring(s.indexOf(":") + 1).trim();//把地址解析出來
    //主動連線藍芽服務端
    try {
        //判斷當前是否正在搜尋
        if (mBluetoothAdapter.isDiscovering()) {
            mBluetoothAdapter.cancelDiscovery();
        }
        try {
            if (device == null) {
                //獲得遠端裝置
                device = mBluetoothAdapter.getRemoteDevice(address);
            }
            if (clientSocket == null) {
                //建立客戶端藍芽Socket
                clientSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
                //開始連線藍芽,如果沒有配對則彈出對話方塊提示我們進行配對
                clientSocket.connect();
                //獲得輸出流(客戶端指向服務端輸出文字)
                os = clientSocket.getOutputStream();
            }
        } catch (Exception e) {
        }
        if (os != null) {
            //往服務端寫資訊
            os.write("藍芽資訊來了".getBytes("utf-8"));
        }
    } catch (Exception e) {
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

接下來看服務端:

服務端使用的是另一部手機,接受上面手機通過藍芽傳送過來的資訊並顯示。

(1)定義全域性常量變數:

private BluetoothAdapter mBluetoothAdapter;
private AcceptThread acceptThread;
private final UUID MY_UUID = UUID
        .fromString("abcd1234-ab12-ab12-ab12-abcdef123456");//和客戶端相同的UUID
private final String NAME = "Bluetooth_Socket";
private BluetoothServerSocket serverSocket;
private BluetoothSocket socket;
private InputStream is;//輸入流
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

(2)定義服務端執行緒類:

private Handler handler = new Handler() {
    public void handleMessage(Message msg) {
        Toast.makeText(getApplicationContext(), String.valueOf(msg.obj),
                Toast.LENGTH_LONG).show();
        super.handleMessage(msg);
    }
};

//服務端監聽客戶端的執行緒類
private class AcceptThread extends Thread {
    public AcceptThread() {
        try {
            serverSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (Exception e) {
        }
    }
    public void run() {
        try {
            socket = serverSocket.accept();
            is = socket.getInputStream();
            while(true) {
                byte[] buffer =new byte[1024];
                int count = is.read(buffer);
                Message msg = new Message();
                msg.obj = new String(buffer, 0, count, "utf-8");
                handler.sendMessage(msg);
            }
        }
        catch (Exception e) {
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

(3)在onCreate方法中初始化執行緒類並開啟

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
acceptThread = new AcceptThread();
acceptThread.start();
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

我們執行程式看一下效果圖:

client

點選“搜尋藍芽裝置”按鈕,就會搜尋到另一臺手機的藍芽資訊,我們點選條目,另一臺手機會出現如下變化:

server

彈出Toast,此時證明我們的藍芽資料已經傳輸過來了。