Android 藍牙連接與通訊,BLE藍牙和經典藍牙一起的藍牙連接與通訊案例


Android 藍牙有兩種,一種是BLE藍牙,另外一種是經典藍牙。

BLE藍牙連接與通訊使用的是 BluetoothKit 框架,BluetoothKit 框架源碼地址與說明:

https://gitee.com/www163/Android-BluetoothKit

BluetoothKit 的弊端經典藍牙連接失敗

所以經典藍牙,自己寫了連接和通訊方式。

對於通訊來講,分為服務端BtServer與客戶端BtClient,一般是安裝app方為服務端,主要是本身的藍牙mac是固定,所以作為服務器端,被連接方為客戶端。

對BLE的連接與通訊,大家可以看 BluetoothKit 框架詳細說明,這里就不累贅。

下面主要是介紹經典藍牙的連接和通訊。

藍牙連接是通過UUID來進行監聽連接的,就是服務器端與客戶端的UUID要一致,這樣才能連接成功。

ps:在藍牙連接通訊時,前提先要配對才能正常通訊。

最下面有項目源碼下載。

在經典藍牙連接時,經常出現“run: read failed, socket might closed or timeout, read ret: -1”

主要原因是UUID的錯誤。

非手機終端的UUID:00001101-0000-1000-8000-00805f9B34FB

手機終端的UUID:00001105-0000-1000-8000-00805f9b34fb

1、AndroidManifest.xml 清單設置藍牙權限

 <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

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

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

2、藍牙通訊BtBase 基類:

package com.inuker.bluetooth; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.os.Environment; import android.util.Log; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.UUID; public class BtBase { //00001101-0000-1000-8000-00805F9B34FB - 00001101-0000-1000-8000-00805f9b34fb //00001106-0000-1000-8000-00805F9B34FB - 00001105-0000-1000-8000-00805f9b34fb //00001105-0000-1000-8000-00805f9B34FB - 00001105-0000-1000-8000-00805f9b34fb //非手機終端的UUID
    public static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9B34FB"); //手機終端的UUID
    public static final UUID MOB_UUID = UUID.fromString("00001105-0000-1000-8000-00805f9B34FB"); private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bluetooth/"; private static final int FLAG_MSG = 0;  //消息標記
    private static final int FLAG_FILE = 1; //文件標記

    private BluetoothSocket mSocket; private DataOutputStream mOut; private Listener mListener; public boolean isRead; public boolean isSending; BtBase(Listener listener) { mListener = listener; } /** * 循環讀取對方數據(若沒有數據,則阻塞等待) */
    void loopRead(BluetoothSocket socket) { mSocket = socket; try { Log.i("test","---------loopRead111--------"); if (!mSocket.isConnected()) mSocket.connect(); Log.i("test","---------loopRead2222--------"); notifyUI(Listener.CONNECTED, mSocket.getRemoteDevice()); mOut = new DataOutputStream(mSocket.getOutputStream()); DataInputStream in = new DataInputStream(mSocket.getInputStream()); isRead = true; while (isRead) { //死循環讀取
                Log.i("Test","------------------server in.readInt="+in.readInt()); switch (in.readInt()) { case FLAG_MSG: //讀取短消息
                        String msg = in.readUTF(); notifyUI(Listener.MSG, "接收短消息:" + msg); break; case FLAG_FILE: //讀取文件
 Util.mkdirs(FILE_PATH); String fileName = in.readUTF(); //文件名
                        long fileLen = in.readLong(); //文件長度 // 讀取文件內容
                        long len = 0; int r; byte[] b = new byte[4 * 1024]; FileOutputStream out = new FileOutputStream(FILE_PATH + fileName); notifyUI(Listener.MSG, "正在接收文件(" + fileName + "),請稍后..."); while ((r = in.read(b)) != -1) { out.write(b, 0, r); len += r; if (len >= fileLen) break; } notifyUI(Listener.MSG, "文件接收完成(存放在:" + FILE_PATH + ")"); break; } } } catch (Throwable e) { e.printStackTrace(); Log.i("Test","-------------BtBase close-----------------"); close(); } } void loopRead2(BluetoothSocket socket) { mSocket = socket; try { Log.i("test","---------loopRead111--------"); if (!mSocket.isConnected()) mSocket.connect(); Log.i("test","---------loopRead2222--------"); notifyUI(Listener.CONNECTED, mSocket.getRemoteDevice()); } catch (Throwable e) { e.printStackTrace(); Log.i("Test","-------------BtBase close-----------------"); close(); } } /** * 循環讀取對方數據(若沒有數據,則阻塞等待) */
    void loopReadMsg() { try { // notifyUI(Listener.CONNECTED, mSocket.getRemoteDevice());
            Log.i("Test","-----------BtBase-loopReadMsg2---------"); // mOut = new DataOutputStream(mSocket.getOutputStream());
            DataInputStream in = new DataInputStream(mSocket.getInputStream()); isRead = true; Log.i("Test","-----------BtBase-loopReadMsg3---------"); while (isRead) { //死循環讀取
                String msg = in.readUTF(); Log.i("Test","-----------BtBase-loopReadMsg4---------"); notifyUI(Listener.MSG, "接收短消息:" + msg); } } catch (Throwable e) { e.printStackTrace(); close(); } } /** * 發送短消息 */
    public void sendMsg2(String msg) { Log.i("Test","-----------sendMsg3----2-----"); if (checkSend()) return; isSending = true; Log.i("Test","-----------sendMsg3----3-----"); try { Log.i("Test","----------sendMsg2--mSocket="+mSocket); OutputStream out = mSocket.getOutputStream(); Log.i("Test","----------sendMsg2--out="+out); mOut = new DataOutputStream(out); mOut.writeInt(FLAG_MSG); //消息標記
 mOut.writeUTF(msg); mOut.flush(); notifyUI(Listener.MSG, "發送短消息:" + msg); } catch (Throwable e) { e.printStackTrace(); // close();
 } isSending = false; } /** * 發送短消息 */
    public void sendMsg(String msg) { Log.i("Test","-----------sendMsg3----2-----"); if (checkSend()) return; isSending = true; Log.i("Test","-----------sendMsg3----3-----"); try { mOut = new DataOutputStream(mSocket.getOutputStream()); mOut.writeInt(FLAG_MSG); //消息標記
 mOut.writeUTF(msg); mOut.flush(); notifyUI(Listener.MSG, "發送短消息:" + msg); } catch (Throwable e) { e.printStackTrace(); // close();
 } isSending = false; } /** * 發送文件 */
    public void sendFile(final String filePath) { if (checkSend()) return; isSending = true; Util.EXECUTOR.execute(new Runnable() { @Override public void run() { try { FileInputStream in = new FileInputStream(filePath); File file = new File(filePath); mOut.writeInt(FLAG_FILE); //文件標記
                    mOut.writeUTF(file.getName()); //文件名
                    mOut.writeLong(file.length()); //文件長度
                    int r; byte[] b = new byte[4 * 1024]; notifyUI(Listener.MSG, "正在發送文件(" + filePath + "),請稍后..."); while ((r = in.read(b)) != -1) mOut.write(b, 0, r); mOut.flush(); notifyUI(Listener.MSG, "文件發送完成."); } catch (Throwable e) { close(); } isSending = false; } }); } /** * 釋放監聽引用(例如釋放對Activity引用,避免內存泄漏) */
    public void unListener() { mListener = null; } /** * 關閉Socket連接 */
    public void close() { try { Log.i("Test","---------關閉Socket連接-----------"); isRead = false; if(mSocket!=null){ mSocket.close(); notifyUI(Listener.DISCONNECTED, null); } // notifyUI(Listener.DISCONNECTED, null);
        } catch (Throwable e) { e.printStackTrace(); } } /** * 當前設備與指定設備是否連接 */
    public boolean isConnected(BluetoothDevice dev) { boolean connected = (mSocket != null && mSocket.isConnected()); if (dev == null) return connected; return connected && mSocket.getRemoteDevice().equals(dev); } // ============================================通知UI===========================================================
    public boolean checkSend() { if (isSending) { MyApplication.toast("正在發送其它數據,請稍后再發...", 0); return true; } return false; } public void notifyUI(final int state, final Object obj) { MyApplication.runUi(new Runnable() { @Override public void run() { try { if (mListener != null) mListener.socketNotify(state, obj); } catch (Throwable e) { e.printStackTrace(); } } }); } public interface Listener { int DISCONNECTED = 0; int CONNECTED = 1; int MSG = 2; void socketNotify(int state, Object obj); } }

3、服務器端BtServer 代碼:

package com.inuker.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothServerSocket; import android.bluetooth.BluetoothSocket; import android.util.Log; import java.lang.reflect.Method; import java.util.UUID; public class BtServer extends BtBase{ private static final String TAG = BtServer.class.getSimpleName(); private BluetoothServerSocket mSSocket; private BluetoothSocket socket; BtServer(Listener listener) { super(listener); // listen();
 } /** * 監聽客戶端發起的連接 */
    public void listen() { try {  BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
            mSSocket = adapter.listenUsingInsecureRfcommWithServiceRecord(TAG, MOB_UUID); //明文傳輸(不安全),無需配對 // 開啟子線程
            Util.EXECUTOR.execute(new Runnable() { @Override public void run() { try { socket = mSSocket.accept(); // 監聽連接 // mSSocket.close(); // 關閉監聽,只連接一個設備
                        loopRead(socket); // 循環讀取
                    } catch (Throwable e) { close(); } } }); } catch (Throwable e) { close(); } } public void listen(UUID uuid) { try {
            Log.i("Test","--------------------uuid="+uuid); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
            mSSocket = adapter.listenUsingInsecureRfcommWithServiceRecord(TAG, uuid); //明文傳輸(不安全),無需配對 // 開啟子線程
            Util.EXECUTOR.execute(new Runnable() { @Override public void run() { try { socket = mSSocket.accept(); // 監聽連接 // mSSocket.close(); // 關閉監聽,只連接一個設備
                        loopRead(socket); // 循環讀取
                    } catch (Throwable e) { close(); } } }); } catch (Throwable e) { close(); } } @Override public void close() { super.close(); try { if(socket!=null){ socket.close(); } if(mSSocket!=null){ mSSocket.close(); } } catch (Throwable e) { e.printStackTrace(); } } }

4、客戶端BtClient 代碼:

package com.inuker.bluetooth; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothSocket; import android.os.ParcelUuid; import android.util.Log; import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.InvocationTargetException; import java.util.UUID; public class BtClient extends BtBase { private BluetoothSocket mSocket; boolean flag = false; BtClient(Listener listener) { super(listener); } /** * 與遠端設備建立長連接 * * @param dev 遠端設備 */
    public void connect(BluetoothDevice dev) { close(); try { 
            final BluetoothSocket socket = dev.createInsecureRfcommSocketToServiceRecord(SPP_UUID); //明文傳輸(不安全),無需配對
            Log.i("Test","------------------socket="+socket); // 開啟子線程
            Util.EXECUTOR.execute(new Runnable() { @Override public void run() { Log.i("Test","-----------connect-------"); loopRead(socket); //循環讀取
 } }); } catch (Throwable e) { Log.i("Test","-------------BtClient close-----------------"); close(); } } public void connect2(BluetoothDevice dev,UUID uuid) { close(); try { 
            final BluetoothSocket socket = dev.createInsecureRfcommSocketToServiceRecord(uuid); //明文傳輸(不安全),無需配對
            Log.i("Test","------------------socket="+socket); // 開啟子線程
            new Thread(new Runnable() { @Override public void run() { try { boolean isCon = socket.isConnected(); Log.i("Test","------------------isCon="+isCon); socket.connect(); loopRead2(socket); //循環讀取
 } catch (IOException e) { e.printStackTrace(); } } }).start(); } catch (Throwable e) { Log.i("Test","-------------BtClient close-----------------"); close(); } } public void connect3(BluetoothDevice dev) { close(); try { 
            final BluetoothSocket socket = dev.createInsecureRfcommSocketToServiceRecord(SPP_UUID); //明文傳輸(不安全),無需配對
            Log.i("Test","------------------socket="+socket); // 開啟子線程
            new Thread(new Runnable() { @Override public void run() { try { boolean isCon = socket.isConnected(); Log.i("Test","------------------isCon="+isCon); socket.connect(); loopRead2(socket); //循環讀取
 } catch (IOException e) { e.printStackTrace(); } } }).start(); } catch (Throwable e) { Log.i("Test","-------------BtClient close-----------------"); close(); } } public void loopReadMsg(BluetoothDevice dev) { // close();
        try { Log.i("Test","-----------loopReadMsg1---------");// 開啟子線程
            Util.EXECUTOR.execute(new Runnable() { @Override public void run() { Log.i("Test","-----------loopReadMsg2---------"); loopReadMsg(); //循環讀取
 } }); } catch (Throwable e) { e.printStackTrace(); Log.i("Test","-----------loopReadMsg3---------ex="+e.getMessage()); close(); } } public void clientSendMsg(String msg) { Log.i("Test","-----------sendMsg3----2-----"); if (checkSend()) return; isSending = true; Log.i("Test","-----------sendMsg3----3-----"); try { Log.i("Test","----------sendMsg2--mSocket="+mSocket); OutputStream out = mSocket.getOutputStream(); Log.i("Test","----------sendMsg2--out="+out); DataOutputStream mOut = new DataOutputStream(out); mOut.writeInt(Listener.MSG); //消息標記
 mOut.writeUTF(msg); mOut.flush(); notifyUI(Listener.MSG, "發送短消息:" + msg); } catch (Throwable e) { e.printStackTrace(); // close();
 } isSending = false; } }

5、Activity 中調用案例:

package com.inuker.bluetooth; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.ParcelUuid; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.inuker.bluetooth.library.connect.listener.BluetoothStateListener; import com.inuker.bluetooth.library.search.SearchResult; import com.inuker.bluetooth.library.utils.BluetoothLog; import com.inuker.bluetooth.view.PullRefreshListView; import com.inuker.bluetooth.view.PullToRefreshFrameLayout; import java.io.File; import java.util.ArrayList; import java.util.UUID; public class CommonFragment extends Fragment implements BtBase.Listener, BtReceiver.Listener, BtDevAdapter.Listener{ private TextView mTips; private EditText mInputMsg; private EditText mInputFile; private TextView mLogs; private BtReceiver mBtReceiver; private BtDevAdapter mBtDevAdapter; private BtClient mClient; private View view; private Context context; private Button scanBtn,sendMsgBtn,sendFileBtn,listenTerBtn,listenMobBtn; private BluetoothDevice remoteDev; private boolean isLoop = true; private BtServer mServer; BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); private boolean isListen = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_common, container, false); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); view = getView(); context = view.getContext(); mBtDevAdapter = new BtDevAdapter(this); mClient = new BtClient(this); mServer = new BtServer(this); listenTerBtn = view.findViewById(R.id.listen_ter); listenMobBtn = view.findViewById(R.id.listen_mob); RecyclerView rv = view.findViewById(R.id.rv_bt); rv.setLayoutManager(new LinearLayoutManager(context)); rv.setAdapter(mBtDevAdapter); mTips = view.findViewById(R.id.tv_tips); mInputMsg = view.findViewById(R.id.input_msg); mInputFile = view.findViewById(R.id.input_file); mLogs = view.findViewById(R.id.tv_log); scanBtn = view.findViewById(R.id.button2); sendMsgBtn = view.findViewById(R.id.btn_sendMsg); sendFileBtn = view.findViewById(R.id.btn_sendFile); mBtReceiver = new BtReceiver(context, this);//注冊藍牙廣播
 BluetoothAdapter.getDefaultAdapter().startDiscovery(); scanBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { reScan(); } }); sendMsgBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String txt = sendMsgBtn.getText().toString(); Log.i("Test","-----is="+"發送信息".equals(txt)); if("發送信息".equals(txt)){
                    mLogs.setText(""); sendMsgBtn.setText("關閉");  loopSendMsg(); }else{ 
                    handler3.sendEmptyMessage(1); } } }); sendFileBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { sendFile(); } }); listenTerBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mServer.close(); mServer.listen(BtBase.SPP_UUID); isListen = true; } }); listenMobBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mServer.close(); mServer.listen(BtBase.MOB_UUID); isListen = true; } }); } @Override public void onDestroy() { super.onDestroy(); context.unregisterReceiver(mBtReceiver); mClient.unListener(); mClient.close(); mServer.unListener(); mServer.close(); } @Override public void socketNotify(int state, Object obj) {
        String msg = null; switch (state) { case BtBase.Listener.CONNECTED: BluetoothDevice dev = (BluetoothDevice) obj; msg = String.format("與%s(%s)連接成功", dev.getName(), dev.getAddress()); remoteDev = dev; mTips.setText(msg); break; case BtBase.Listener.DISCONNECTED: msg = "連接斷開"; mTips.setText(msg); break; case BtBase.Listener.MSG: msg = String.format("\n%s", obj); mLogs.append(msg); break; } MyApplication.toast(msg, 0); } @Override public void onItemClick(BluetoothDevice dev) { mServer.close(); mBluetoothAdapter.cancelDiscovery(); Log.i("Test","------name="+dev.getName()+","+dev.getAddress()); if (mClient.isConnected(dev)) { remoteDev = dev; MyApplication.toast("已經連接了", 0); return; } int state = dev.getBondState(); if(state != BluetoothDevice.BOND_BONDED){ boolean isPair = BluetoothUtils.pair(dev.getAddress(),""); //             Log.i("Test","-------------------isPair="+isPair); MyApplication.toast("請先配對藍牙", 0); reScan(); return; } ParcelUuid[] parcel = dev.getUuids(); boolean isContain = isContainUUID(dev); if(isContain){ mServer.listen(BtBase.SPP_UUID); mClient.connect2(dev,BtBase.SPP_UUID); }else{ mServer.listen(BtBase.MOB_UUID); mClient.connect2(dev,BtBase.MOB_UUID); } MyApplication.toast("正在連接...", 0); mTips.setText("正在連接..."); } @Override public void foundDev(BluetoothDevice dev) { mBtDevAdapter.add(dev); } // 重新掃描
    public void reScan() { mBtDevAdapter.reScan(); } //發送信息
    public void sendMsg2() { if (mClient.isConnected(null)) { String msg = mInputMsg.getText().toString(); if (TextUtils.isEmpty(msg)) MyApplication.toast("消息不能空", 0); else mClient.sendMsg(msg); } else MyApplication.toast("沒有連接", 0); } public void sendMsgByClient() { mClient.sendMsg("發送成功"); } //發送信息
    public void loopReadMsg() { if (mClient.isConnected(null)) { String msg = "發送成功......"; mClient.loopReadMsg(); } else MyApplication.toast("沒有連接", 0); } public void sendMsg() { Log.i("Test","-----------sendMsg1---------"); if(remoteDev == null){ MyApplication.toast("請選連接藍牙", 0); return; } Log.i("Test","-----------sendMsg2---------"); if (!mClient.isConnected(remoteDev)) { MyApplication.toast("請選連接藍牙", 0); return; } Log.i("Test","-----------sendMsg3---------"); mClient.loopReadMsg(remoteDev); } //發送文件
    public void sendFile() { if (mClient.isConnected(null)) { String filePath = mInputFile.getText().toString(); if (TextUtils.isEmpty(filePath) || !new File(filePath).isFile()) MyApplication.toast("文件無效", 0); else mClient.sendFile(filePath); } else MyApplication.toast("沒有連接", 0); } private void loopRead(){ new Thread(new Runnable() { @Override public void run() { while (isLoop){ 
 loopReadMsg(); } } }).start(); } private Handler handler = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); String txt = sendMsgBtn.getText().toString(); if("發送信息".equals(txt)){ // loopRead(); // loopRead();
                if (mClient.isConnected(null)) { // String msg = "發送成功......";
                    sendMsgBtn.setText("關閉"); mClient.loopReadMsg(); isLoop = true; } else MyApplication.toast("沒有連接", 0); }else{ isLoop = false; sendMsgBtn.setText("發送信息"); } } }; public void serverSendMsg() { if (mServer.isConnected(null)) { 
                mServer.sendMsg("測試。。。。"); } else MyApplication.toast("沒有連接", 0); } private boolean isContainUUID(BluetoothDevice dev){ ParcelUuid[] pars = dev.getUuids(); for(ParcelUuid p : pars){ UUID u = p.getUuid(); if(BtBase.SPP_UUID.equals(u)){ return true; } } return false; } private Handler handler3 = new Handler(){ @Override public void handleMessage(@NonNull Message msg) { super.handleMessage(msg); switch (msg.what){ case 0: // sendMsgBtn.setText("關閉");
 sendMsgByClient(); isLoop = true; break; case 1: isLoop = false; sendMsgBtn.setText("發送信息"); break; } } }; private void loopSendMsg(){ new Thread(new Runnable() { @Override public void run() { while(isLoop){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } handler3.sendEmptyMessage(0); } } }).start(); } }

6、效果如下:

 

 

 

 XT300 是打印機終端:

 

 K5為手機藍牙連接如下:

 

7、項目源碼:

https://github.com/hongchuanfeng/Android-Bluetooth-Demo

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM