1. 程式人生 > >Android開發之BlueTooth--最簡單的Andorid傳統藍芽通訊Demo

Android開發之BlueTooth--最簡單的Andorid傳統藍芽通訊Demo

又到了Android小白的開發之路上寫筆記的時間了~~

開篇都不知道說什麼好... ...前兩個月寫了WIFI,後來也想寫一下藍芽的,可惜公司產品不給力,出現了很多BUG,一直在修啊修,最近終於有點空閒時間了。那就來簡單的嘗試一下

關於藍芽開發,優秀部落格一大片,你能來看我這篇辣雞文章真是不勝感激。基礎知識/理論什麼的我就不說了,我也是一邊看著官方文件來的,想深入瞭解去看看官方文件也是極好的。

我們知道Andorid的藍芽分為兩種,一種是傳統的一種是低功耗藍芽(BLE),一步一步來,這裡先寫一下傳統藍芽~

功能實現

主要是實現兩臺手機能通過藍芽相互發送訊息。兩手機可以任意一臺充當服務端,一臺充當客戶端。過程儘量越簡單,讓大家看的更清楚!

程式碼-準備工作

1.首先我們需要一個藍芽介面卡,用於搜尋裝置/連線裝置;一個UUID,用於建立服務端;一個廣播接收器,用於監聽搜尋結果。

    public static final String BT_UUID = "00001101-0000-1000-8000-00805F9B34FB";//uuid

    private BluetoothAdapter mBluetoothAdapter;//藍芽介面卡
    private BlueToothStateReceiver mReceiver;//廣播接收器
    private ConnectThread mConnectThread; //客戶端執行緒
    private AcceptThread mAcceptThread; //服務端執行緒


2.然後先開啟藍芽

 private void openBT() {
        if (mBluetoothAdapter == null) {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        //1.裝置不支援藍芽,結束應用
        if (mBluetoothAdapter == null) {
            finish();
            return;
        }
        //2.判斷藍芽是否開啟
        if (!mBluetoothAdapter.enable()) {
            //沒開啟請求開啟
            Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(btEnable, REQUEST_BT_ENABLE_CODE);
        }
    }

3.註冊廣播接收器
private void registerRec() {
        //3.註冊藍芽廣播
        mReceiver = new BlueToothStateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_FOUND);//搜多到藍芽
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜尋結束
        registerReceiver(mReceiver, filter);
    }

4.開始搜尋裝置
if (mBluetoothAdapter != null) {
    mBluetoothAdapter.startDiscovery();
}

5.獲取搜尋結果
class BlueToothStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(MainActivity.this, "觸發廣播", Toast.LENGTH_SHORT).show();
            String action = intent.getAction();
            switch (action) {
                case BluetoothDevice.ACTION_FOUND:
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    Toast.makeText(MainActivity.this, "找到裝置" + device.getName(), Toast.LENGTH_SHORT).show();
                    if (mRvAdapter != null) {
                        mRvAdapter.addDevice(device);
                    }
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    mMessageAdapter.addMessage("搜尋結束");
                    break;
            }
        }
    }

額~~還忘了一步,搜尋完到指定裝置後要記得關閉搜尋

if (mBluetoothAdapter != null && mBluetoothAdapter.isDiscovering()) {  
                    mBluetoothAdapter.cancelDiscovery();  
                }  


----------------------------然後基礎的就到這裡了----------------------------------

接下來,是點選列表,然後連線對應的藍芽。

開頭說了,要實現兩臺手機任意充當客戶端或者服務端,所以在程式中應該包含有服務端也要有客戶端,因此我們需要兩個執行緒,分別處理這兩種情況。

6.首先服務端執行緒程式碼--裡面有些沒用到或者註釋的程式碼還有一些通知UI更新的程式碼,大家可以忽略

 class AcceptThread extends Thread {
        private BluetoothServerSocket mServerSocket;
        private BluetoothSocket mSocket;
        private InputStream btIs;
        private OutputStream btOs;
        private PrintWriter writer;
        private boolean canAccept;
        private boolean canRecv;

        public AcceptThread() {
            canAccept = true;
            canRecv = true;
        }

        @Override
        public void run() {
            try {
                //獲取套接字
                BluetoothServerSocket temp = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("TEST", UUID.fromString(BT_UUID));
                mServerSocket = temp;
                //監聽連線請求 -- 作為測試,只允許連線一個裝置
                if (mServerSocket != null) {
                    // while (canAccept) {
                    mSocket = mServerSocket.accept();//阻塞等待客戶端連線
                    sendHandlerMsg("有客戶端連線");
                    // }
                }
                //獲取輸入輸出流
                btIs = mSocket.getInputStream();
                btOs = mSocket.getOutputStream();
                //通訊-接收訊息
                BufferedReader reader = new BufferedReader(new InputStreamReader(btIs, "UTF-8"));
                String content = null;
                while (canRecv) {
                    content = reader.readLine();
                    sendHandlerMsg("收到訊息:" + content);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (mSocket != null) {
                        mSocket.close();
                    }
                    // btIs.close();//兩個輸出流都依賴socket,關閉socket即可
                    // btOs.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                }
            }
        }

        private void sendHandlerMsg(String content) {
            Message msg = mHandler.obtainMessage();
            msg.what = 1001;
            msg.obj = content;
            mHandler.sendMessage(msg);
        }

        public void write(String msg) {
            if (btOs != null) {
                try {
                    if (writer == null) {
                        writer = new PrintWriter(new OutputStreamWriter(btOs, "UTF-8"), true);
                    }
                    writer.println(msg);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    writer.close();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                }
            }
        }
    }

7.連線執行緒程式碼--用於點選某個裝置後建立連線--同樣裡面有些沒用到或者註釋的程式碼用於以後拓展的,還有一些通知UI更新的程式碼,大家可以忽略
class ConnectThread extends Thread {
        private BluetoothDevice mDevice;
        private BluetoothSocket mSocket;
        private InputStream btIs;
        private OutputStream btOs;
        private boolean canRecv;
        private PrintWriter writer;

        public ConnectThread(BluetoothDevice device) {
            mDevice = device;//被點選裝置
            canRecv = true;
        }

        @Override
        public void run() {
            if (mDevice != null) {
                try {
                    //獲取套接字
                    BluetoothSocket temp = mDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));
                    //mDevice.createRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));//sdk 2.3以下使用
                    mSocket = temp;
                    //發起連線請求
                    if (mSocket != null) {
                        mSocket.connect();
                    }
                    sendHandlerMsg("連線 " + mDevice.getName() + "成功!");
                    //獲取輸入輸出流
                    btIs = mSocket.getInputStream();
                    btOs = mSocket.getOutputStream();

                    //通訊-接收訊息
                    BufferedReader reader = new BufferedReader(new InputStreamReader(btIs, "UTF-8"));
                    String content = null;
                    while (canRecv) {
                        content = reader.readLine();
                        sendHandlerMsg("收到訊息:" + content);
                    }


                } catch (IOException e) {
                    e.printStackTrace();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                } finally {
                    try {
                        if (mSocket != null) {
                            mSocket.close();
                        }
                        //btIs.close();//兩個輸出流都依賴socket,關閉socket即可
                        //btOs.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        sendHandlerMsg("錯誤:" + e.getMessage());
                    }
                }
            }
        }

        private void sendHandlerMsg(String content) {
            Message msg = mHandler.obtainMessage();
            msg.what = 1001;
            msg.obj = content;
            mHandler.sendMessage(msg);
        }

        public void write(String msg) {
            if (btOs != null) {
                try {
                    if (writer == null) {
                        writer = new PrintWriter(new OutputStreamWriter(btOs, "UTF-8"), true);
                    }
                    writer.println(msg);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    writer.close();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                }
            }
        }
    }

8.然後這個兩個執行緒的開啟時機我是這麼來設定的:服務端是開啟藍芽後立馬啟動,連線執行緒是點選了某個裝置發起連線請求時啟動。

                openBT();
                mMessageAdapter.addMessage("開啟藍芽");
                if (mAcceptThread == null && mBluetoothAdapter != null) {
                    mAcceptThread = new AcceptThread();
                    mAcceptThread.start();
                    mMessageAdapter.addMessage("啟動服務執行緒");
                }
mRvAdapter.setOnItemClickListener(new RvAdapter.OnItemClickListener() {
            @Override
            public void onClick(BluetoothDevice device) {
                mConnectThread = new ConnectThread(device);
                mConnectThread.start();
            }
        });

9.連線上別人或者別人連線我們之後就可以進行通訊了。要傳送訊息,直接呼叫對應執行緒的write()方法即可。大致流程就這樣。還是挺簡單的。接下來給大家看一下全部的程式碼:

看全部程式碼前給大家先看執行效果圖,我怕大家沒耐心看到後面。操作:兩個裝置都點選開啟藍芽,然後搜尋,找出裝置點選停止。最後裝置1點選結果中的裝置2,互發訊息。 ps:裝置1 Android 4.0     裝置2(OnePlus3T) Android 7.1

裝置1截圖:


裝置2截圖:


-放全部程式碼:

首先是我的佈局:訊息列表顯示的是各種訊息,什麼有客戶端連線,收到什麼訊息,傳送什麼訊息等。截圖如下:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

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

        <Button
            android:id="@+id/open"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="開啟" />

        <Button
            android:id="@+id/close"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="關閉" />

        <Button
            android:id="@+id/start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="搜尋" />

        <Button
            android:id="@+id/stop"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="停止" />

    </LinearLayout>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="裝置列表" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/devices"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

    </android.support.v7.widget.RecyclerView>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="訊息列表" />
    <android.support.v7.widget.RecyclerView
        android:id="@+id/msglist"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        >

    </android.support.v7.widget.RecyclerView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        >
        <EditText
            android:id="@+id/input"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content" />
        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="傳送"
            />
    </LinearLayout>



</LinearLayout>

然後是Activity程式碼,這裡程式碼可能有點多而且亂,大家按照上面的步驟來看就清楚多了。本來大部分都是交給服務來做的,谷歌文件官方就是這麼幹的。我這裡為了直觀看到所有程式碼,就沒用服務。大家將就看看
package cn.small_qi.bluetoothtest;

import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.UUID;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    public static final int REQUEST_BT_ENABLE_CODE = 200;
    public static final String BT_UUID = "00001101-0000-1000-8000-00805F9B34FB";//uuid

    private BluetoothAdapter mBluetoothAdapter;//藍芽介面卡
    private BlueToothStateReceiver mReceiver;//廣播接收器
    private ConnectThread mConnectThread; //客戶端執行緒
    private AcceptThread mAcceptThread; //服務端執行緒

    private RecyclerView mRecyclerView;
    private RvAdapter mRvAdapter;

    private RecyclerView mMessageView;
    private static MsgAdapter mMessageAdapter;

    private EditText inputEt;

    private static Handler mHandler = new Handler() {
        @Override
        public void dispatchMessage(Message msg) {
            mMessageAdapter.addMessage((String) msg.obj);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //這是我是為了6.0以上的裝置能搜尋到結果,動態申請了位置許可權。但是沒有處理結果,因為我測試肯定點同意~- -
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 1001);
        }
        initUI();
        registerRec();
    }

    private void initUI() {
        findViewById(R.id.open).setOnClickListener(this);
        findViewById(R.id.close).setOnClickListener(this);
        findViewById(R.id.start).setOnClickListener(this);
        findViewById(R.id.stop).setOnClickListener(this);
        findViewById(R.id.send).setOnClickListener(this);

        inputEt = (EditText) findViewById(R.id.input);

        mRecyclerView = (RecyclerView) findViewById(R.id.devices);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRvAdapter = new RvAdapter(this);
        mRecyclerView.setAdapter(mRvAdapter);
        mRvAdapter.setOnItemClickListener(new RvAdapter.OnItemClickListener() {
            @Override
            public void onClick(BluetoothDevice device) {
                mConnectThread = new ConnectThread(device);
                mConnectThread.start();
            }
        });

        mMessageView = (RecyclerView) findViewById(R.id.msglist);
        mMessageView.setLayoutManager(new LinearLayoutManager(this));
        mMessageAdapter = new MsgAdapter(this);
        mMessageView.setAdapter(mMessageAdapter);
    }


    private void openBT() {
        if (mBluetoothAdapter == null) {
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        }
        //1.裝置不支援藍芽,結束應用
        if (mBluetoothAdapter == null) {
            finish();
            return;
        }
        //2.判斷藍芽是否開啟
        if (!mBluetoothAdapter.enable()) {
            //沒開啟請求開啟
            Intent btEnable = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(btEnable, REQUEST_BT_ENABLE_CODE);
        }
    }

    private void registerRec() {
        //3.註冊藍芽廣播
        mReceiver = new BlueToothStateReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothDevice.ACTION_FOUND);//搜多到藍芽
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//搜尋結束
        registerReceiver(mReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        if (mReceiver != null) {
            unregisterReceiver(mReceiver);
        }
        super.onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_BT_ENABLE_CODE) {
            if (resultCode == RESULT_OK) {
                //使用者允許開啟藍芽
                mMessageAdapter.addMessage("使用者同意開啟藍芽");
            } else if (resultCode == RESULT_CANCELED) {
                //使用者取消開啟藍芽
                mMessageAdapter.addMessage("使用者拒絕開啟藍芽");
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.open:
                openBT();
                mMessageAdapter.addMessage("開啟藍芽");
                if (mAcceptThread == null && mBluetoothAdapter != null) {
                    mAcceptThread = new AcceptThread();
                    mAcceptThread.start();
                    mMessageAdapter.addMessage("啟動服務執行緒");
                }
                break;
            case R.id.close:
                mBluetoothAdapter.disable();
                break;
            case R.id.start:
                if (mBluetoothAdapter != null) {
                    mRvAdapter.clearDevices();//開始搜尋前清空上一次的列表
                    mBluetoothAdapter.startDiscovery();
                    mMessageAdapter.addMessage("開始搜尋藍芽");
                } else {
                    openBT();
                    if (mBluetoothAdapter != null) {
                        mRvAdapter.clearDevices();//開始搜尋前清空上一次的列表
                        mBluetoothAdapter.startDiscovery();
                        mMessageAdapter.addMessage("開始搜尋藍芽");
                    }
                }
                break;
            case R.id.stop:
                if (mBluetoothAdapter != null && mBluetoothAdapter.isDiscovering()) {
                    mBluetoothAdapter.cancelDiscovery();
                }
                break;
            case R.id.send:
                /**
                 * 如何區分我是使用服務端的socket還是使用客戶端的socket傳送訊息
                 *  1. 在單一環境下,手機一般作為客戶端,外圍裝置是伺服器。所以手機完全可以不用建立伺服器,不存在這個問題。
                 *  2. 假如是兩臺手機用來聊天,可分別充當伺服器和客戶端,那就是發起連線方(即點選裝置列表連線)作為客戶端。
                 *  3. 假如我連結了別人,另一個人又連線了我,那我怎麼區分?那你寫兩個介面啊~你要回復其他客戶端發來的訊息就用伺服器的socket
                 *  否則就用客戶端的。我這裡偷懶了,一但我主動連線別人,相當於我就關閉服務端了,不給別人連我了。
                 *  4. 那那些藍芽對戰遊戲都怎麼區分的?你發現藍芽對戰需要一個人先建立房間沒?那個人就是服務端,其他都是客戶端,沒這個問題。
                 */
                String msg = inputEt.getText().toString();
                if (TextUtils.isEmpty(msg)) {
                    Toast.makeText(this, "訊息為空", Toast.LENGTH_SHORT).show();
                    return;
                }
                if (mConnectThread != null) {//證明我主動去連結別人了
                    mConnectThread.write(msg);
                } else if (mAcceptThread != null) {
                    mAcceptThread.write(msg);
                }
                mMessageAdapter.addMessage("傳送訊息:" + msg);
                break;
        }
    }

    class BlueToothStateReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(MainActivity.this, "觸發廣播", Toast.LENGTH_SHORT).show();
            String action = intent.getAction();
            switch (action) {
                case BluetoothDevice.ACTION_FOUND:
                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    Toast.makeText(MainActivity.this, "找到裝置" + device.getName(), Toast.LENGTH_SHORT).show();
                    if (mRvAdapter != null) {
                        mRvAdapter.addDevice(device);
                    }
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    mMessageAdapter.addMessage("搜尋結束");
                    break;
            }
        }
    }


    class ConnectThread extends Thread {
        private BluetoothDevice mDevice;
        private BluetoothSocket mSocket;
        private InputStream btIs;
        private OutputStream btOs;
        private boolean canRecv;
        private PrintWriter writer;

        public ConnectThread(BluetoothDevice device) {
            mDevice = device;
            canRecv = true;
        }

        @Override
        public void run() {
            if (mDevice != null) {
                try {
                    //獲取套接字
                    BluetoothSocket temp = mDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));
                    //mDevice.createRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));//sdk 2.3以下使用
                    mSocket = temp;
                    //發起連線請求
                    if (mSocket != null) {
                        mSocket.connect();
                    }
                    sendHandlerMsg("連線 " + mDevice.getName() + "成功!");
                    //獲取輸入輸出流
                    btIs = mSocket.getInputStream();
                    btOs = mSocket.getOutputStream();

                    //通訊-接收訊息
                    BufferedReader reader = new BufferedReader(new InputStreamReader(btIs, "UTF-8"));
                    String content = null;
                    while (canRecv) {
                        content = reader.readLine();
                        sendHandlerMsg("收到訊息:" + content);
                    }


                } catch (IOException e) {
                    e.printStackTrace();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                } finally {
                    try {
                        if (mSocket != null) {
                            mSocket.close();
                        }
                        //btIs.close();//兩個輸出流都依賴socket,關閉socket即可
                        //btOs.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        sendHandlerMsg("錯誤:" + e.getMessage());
                    }
                }
            }
        }

        private void sendHandlerMsg(String content) {
            Message msg = mHandler.obtainMessage();
            msg.what = 1001;
            msg.obj = content;
            mHandler.sendMessage(msg);
        }

        public void write(String msg) {
            if (btOs != null) {
                try {
                    if (writer == null) {
                        writer = new PrintWriter(new OutputStreamWriter(btOs, "UTF-8"), true);
                    }
                    writer.println(msg);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    writer.close();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                }
            }
        }
    }

    class AcceptThread extends Thread {
        private BluetoothServerSocket mServerSocket;
        private BluetoothSocket mSocket;
        private InputStream btIs;
        private OutputStream btOs;
        private PrintWriter writer;
        private boolean canAccept;
        private boolean canRecv;

        public AcceptThread() {
            canAccept = true;
            canRecv = true;
        }

        @Override
        public void run() {
            try {
                //獲取套接字
                BluetoothServerSocket temp = mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord("TEST", UUID.fromString(BT_UUID));
                mServerSocket = temp;
                //監聽連線請求 -- 作為測試,只允許連線一個裝置
                if (mServerSocket != null) {
                    // while (canAccept) {
                    mSocket = mServerSocket.accept();
                    sendHandlerMsg("有客戶端連線");
                    // }
                }
                //獲取輸入輸出流
                btIs = mSocket.getInputStream();
                btOs = mSocket.getOutputStream();
                //通訊-接收訊息
                BufferedReader reader = new BufferedReader(new InputStreamReader(btIs, "UTF-8"));
                String content = null;
                while (canRecv) {
                    content = reader.readLine();
                    sendHandlerMsg("收到訊息:" + content);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (mSocket != null) {
                        mSocket.close();
                    }
                    // btIs.close();//兩個輸出流都依賴socket,關閉socket即可
                    // btOs.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                }
            }
        }

        private void sendHandlerMsg(String content) {
            Message msg = mHandler.obtainMessage();
            msg.what = 1001;
            msg.obj = content;
            mHandler.sendMessage(msg);
        }

        public void write(String msg) {
            if (btOs != null) {
                try {
                    if (writer == null) {
                        writer = new PrintWriter(new OutputStreamWriter(btOs, "UTF-8"), true);
                    }
                    writer.println(msg);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                    writer.close();
                    sendHandlerMsg("錯誤:" + e.getMessage());
                }
            }
        }
    }


}


然後是兩個RecycleView的介面卡

訊息列表:

package cn.small_qi.bluetoothtest;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by small_qi on 2017/9/13.
 */

public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.MsgHolder>{
    private Context mContext;
    private List<String> msgList;


    public MsgAdapter(Context mContext) {
        this.mContext = mContext;
        msgList = new ArrayList<>();
    }

    @Override
    public MsgAdapter.MsgHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new MsgAdapter.MsgHolder(LayoutInflater.from(mContext).inflate(R.layout.item,parent,false));
    }

    @Override
    public void onBindViewHolder(MsgAdapter.MsgHolder holder, final int position) {
       holder.nameTv.setText(msgList.get(position));
    }

    @Override
    public int getItemCount() {
        return msgList.size();
    }

    public void addMessage(String msg) {
        msgList.add(msg);
        notifyItemInserted(msgList.size()-1);
    }

    public void clearMsgList(){
        msgList.clear();
        notifyDataSetChanged();
    }

    public interface OnItemClickListener{
        void onClick(BluetoothDevice device);
    }

    class MsgHolder extends RecyclerView.ViewHolder{
        private TextView nameTv;
        public MsgHolder(View itemView) {
            super(itemView);
            nameTv = itemView.findViewById(R.id.name);
        }
    }
}

裝置列表(有個點選回撥):
package cn.small_qi.bluetoothtest;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by small_qi on 2017/9/13.
 */

public class RvAdapter extends RecyclerView.Adapter<RvAdapter.RvHolder>{
    private Context mContext;
    private List<BluetoothDevice> mDevices;
    private OnItemClickListener onItemClickListener;

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    public RvAdapter(Context mContext) {
        this.mContext = mContext;
        mDevices = new ArrayList<>();
    }

    @Override
    public RvHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new RvHolder(LayoutInflater.from(mContext).inflate(R.layout.item,parent,false));
    }

    @Override
    public void onBindViewHolder(RvHolder holder, final int position) {
        holder.nameTv.setText(mDevices.get(position).getName()+":"+mDevices.get(position).getAddress());
        //點選事件 點選配對
        if (onItemClickListener!=null) {
            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    onItemClickListener.onClick(mDevices.get(position));
                }
            });
        }
    }

    @Override
    public int getItemCount() {
        return mDevices.size();
    }

    public void addDevice(BluetoothDevice device) {
        mDevices.add(device);
        notifyItemInserted(mDevices.size()-1);
    }

    public void clearDevices(){
        mDevices.clear();
        notifyDataSetChanged();
    }

    public interface OnItemClickListener{
        void onClick(BluetoothDevice device);
    }

    class RvHolder extends RecyclerView.ViewHolder{
        private TextView nameTv;
        public RvHolder(View itemView) {
            super(itemView);
            nameTv = itemView.findViewById(R.id.name);
        }
    }
}

列表Item的佈局,兩個用的同一個佈局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="8dp"
    >
    <TextView
        android:id="@+id/name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

全部程式碼就這些了。主要展示的是藍芽的基本使用流程,要達到標準的使用,還需要做大量優化和修改~這就要根據業務需求來了。就像我這裡,用手機實時獲取同步並記錄萬用表的測量資料,如果不用BLE,其實就是萬用表充當服務端,我一旦連線,就給我傳送約定好的資料格式,我在客戶端進行解析就可以了。這樣的功能這個例子就可以完全模擬出來了~~

非常感謝您能看到最後~這Demo實現了,兩個裝置相互聊天。其實獲取到socekt後,就不關藍芽什麼事了,主要是考察對socket通訊的使用了。例子中只實現了1V1聊天,至於一對多還有檔案傳輸,可以看看我之前的Socket通訊的部落格哦~部落格中的WIFI通訊相關中也有類似的功能,大家有興趣可以看看,雖然不是什麼深度優秀文章。

demo就是demo各種處理都不完善,而且我只是個小白白,有問題在所難免,大家見諒。有問題評論中指出~謝謝 have a nice day

相關推薦

Android開發BlueTooth--簡單Andorid傳統通訊Demo

又到了Android小白的開發之路上寫筆記的時間了~~ 開篇都不知道說什麼好... ...前兩個月寫了WIFI,後來也想寫一下藍芽的,可惜公司產品不給力,出現了很多BUG,一直在修啊修,最近終於有點空閒時間了。那就來簡單的嘗試一下 關於藍芽開發,優秀部落格一大片,你能來看我

Android開發實現簡單酷炫的3D圖片瀏覽效果(一)

一、原理 整體實現是以手機螢幕的正中間位置為對稱軸,位於正中間的圖片顯示最大,也最亮,同時左右兩邊的圖片以最中間位置為對稱軸,分別旋轉對應的角度,同時亮度調整為適當的比例,已達到對稱的效果。具體的3D瀏覽圖片效果,我是通過自定義Gallery來實現的,建立一個類Galler

Android開發實現簡單酷炫的3D圖片瀏覽效果(二)

          (一)截圖                      (二)實現關鍵:           1、改寫Gallery,實現圖片的層疊和透明度漸變。 主要是改寫getChildStaticTransformation方法           2、對圖片進行

Android開發漫漫長途 Ⅷ——Android Binder(也許是容易理解的)

pct med ctf 共享 抽象 fin 進程的地址空間 源碼 instance 該文章是一個系列文章,是本人在Android開發的漫漫長途上的一點感想和記錄,我會盡量按照先易後難的順序進行編寫該系列。該系列引用了《Android開發藝術探索》以及《深入理解Android

Android開發百度地圖定位以及簡單覆蓋物的實現

直接上程式碼: 先看下效果圖: 我這裡主要做了三個功能: 1.一秒鐘實時定位功能; 2.新增任意經緯度地點到地圖上; 3.判斷朝陽門是否在本人定位範圍1000米內; 百度地圖初始化方法: DemoApplication.java package com

【 Visual C 】遊戲開發筆記簡單的DirectX vc視窗的編寫

                筆記一中我們介紹瞭如何用程式碼建立空的win32視窗,然而建立空的win32視窗只完成了一半的工作,接下來要做的工作是設定Direct3D,從而可以在螢幕上渲染圖形。Direct3D要呼叫很多函式才能成功設定API。一旦完成設定,並且設定成功,就可以向螢幕上渲染圖形。下面是函式

Android開發Android studio中的Theme Editor的簡單使用

本文章可以看到: 1. Android Studio 中 Theme 的編輯神器 如下圖: 主要功能:主題風格編輯 可以看到,裡面有外面熟悉的colorPrimary、colorPrimaryD

Android開發(Bluetooth)操作(二)--修改本機裝置的可見性,並掃描周圍可用的裝置

一. 修改本機藍芽裝置的可見性 二. 掃描周圍可用的藍芽裝置 Eg: 一.  清單檔案AdroidManifest.xml: <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android=

Android開發實現簡單的極光推送

public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); JPushInterface.setDebugMode(t

android應用開發Activity的簡單佈局切換

Activity到底是什麼怎麼用,到現在感覺心裡還是沒底的,說不太清楚,看文件的時候發現一大堆方法,這裡我只寫了簡單佈局的切換,至於以後Activity的用法方法,我會慢慢學習的。 1、什麼是activity Activity是一種互動的介面,一個介面

Android開發getX,getRawX,getWidth,getTranslationX等的區別

save string hlist getwidth sta 是我 touch 項目 寬度 轉載請註明出處:http://blog.csdn.net/dmk877/article/details/51550031 好久沒寫博客了,最近工作確實挺忙的,剛剛結束了一個

Android 開發Windows環境下Android Studio安裝和使用教程(圖文詳細步驟)

9.png 虛擬機 jdk版本 編寫 clip 開發平臺 集成開發 arc 電腦安裝 鑒於谷歌最新推出的Android Studio備受開發者的推崇,所以也跟著體驗一下。 一、介紹Android Studio Android Studio 是一個Android

Android開發AudioManager(音頻管理器)具體解釋

應該 數量 service eth out 開發 要求 type 路由 AudioManager簡單介紹: AudioManager類提供了訪問音量和振鈴器mode控制。使用Context.getSystemService(Context.AUDIO_SERVICE)

【入門篇】ANDROID開發BUG專講

world 自然 執行 類型 效率 str 積累 全部 href 話說諸葛亮是一個優秀的程序員,每個錦囊都是應對不同的case而編寫的。可是優秀的程序員也敵只是更優秀的bug。六出祈山。七進中原,鞠躬盡瘁,死而後已的諸葛亮僅僅由於有一

android開發merge結合include優化布局

ted com match clas you title example ews 文件的 merge結合include優化android布局,效果不知道。個人感覺使用上也有非常大的局限。只是還是了解一下。記錄下來。 布局文件都要有根節點,但androi

Android開發增量更新

avt exp chm 這一 font ams extern city ron 一、使用場景 apk升級,節省服務器和用戶的流量 二、原理 自從 Android 4.1 開始, Google Play 引入了應用程序的增量更新功能,App使用該升級方式,可節省約2/3

Android開發布局文件裏實現OnClick事件關聯處理方法

intent dsm nbsp ext 關聯 you vertica findview 時間 一般監聽OnClickListener事件,我們都是通過Button button = (Button)findViewById(....); button.se

Android 開發 ---- bootloader (LK)

ttl tab 不同的 opera 指定 isa system void mem LK是什麽 LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代碼 ,little kernel

Android開發牙連接打印機

cep sdi tco disable ner gis util receiver count 代碼很簡單,直接一個布局文件和一個activity。需要的朋友可以直接將這兩部分粘貼復制到項目中即可。 Activity部分: package com.anhua.bluet

Android開發新建項目報錯的問題

instr rul txt gin 通過 ini .com org top 通過android studio新建一個空項目。在新建完項目之後,gradle編譯會報錯。 發生問題的原因是build.gradle(Project:TopDialog)中: allproject