传统蓝牙通讯实例

蓝牙通讯:本质是通过socket做虚拟串口通讯。

关键字:

  • RFCOMM(串口电缆仿真协议)
  • socket通讯
  • 可被发现
  • 扫描设备
  • 配对
  • UUID (客户端与服务端UUID要相同)

关键步骤点:

  • 确定终端支持蓝牙
  • 确认并请求开启蓝牙
  • 扫描发现已配对设备
  • 扫描发现可配对设备
  • 连接设备(发起配对)
  • 配对连接后,Socket通讯

以上关键步骤有些可选,我们扫描及配对等步骤通过终端系统先操作,编码先实现连接设备及socket通讯。其他功能根据需要添加,后面也会一一介绍。

实现说明:

服务端:

  1. 获取蓝牙设备对象
  2. 建立服务端监听socket对象
  3. 启动socket对象的accept()方法监听客户端的连接
  4. 管理socket输入输出流进行通讯
package com.bluetooth.sevice;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {
    private static final String NAME_SECURE = "BluetoothChatSecure";
    private static final UUID SPP_MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private BluetoothAdapter mBluetoothAdapter;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constants.MESSAGE_READ:
                    Toast.makeText(MainActivity.this,"数据接收成功",Toast.LENGTH_LONG).show();
                    parseReceiveData((byte[]) msg.obj);
                    break;
            }
            super.handleMessage(msg);
        }
    };
    private ConnectedThread connectedThread;
    private AcceptThread acceptThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //1、获取蓝牙设备对象,mBluetoothAdapter如果为空表示终端不存在蓝牙设备
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        //isEnabled判断蓝牙开关是否开启
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, 99);
        } else {
            acceptThread = new AcceptThread();
            acceptThread.start();
        }
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            acceptThread = new AcceptThread();
            acceptThread.start();
        }
    }
    private void parseReceiveData(byte[] obj) {
        Log.i("接收到数据:", "长度:"+obj.length+"数据:"+HexUtils.bcd2str(obj));
    }

    private class AcceptThread extends Thread {
        private final BluetoothServerSocket mmServerSocket;

        public AcceptThread() {
            // Use a temporary object that is later assigned to mmServerSocket,
            // because mmServerSocket is final
            BluetoothServerSocket tmp = null;
            try {
                //2.建立服务端监听socket对象
                tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,SPP_MY_UUID_SECURE);
            } catch (IOException e) { }
            mmServerSocket = tmp;
        }

        @Override
        public void run() {
            BluetoothSocket socket = null;
            // Keep listening until exception occurs or a socket is returned
            while (true) {
                try {
                    //3.启动socket对象的accept()方法监听客户端的连接
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    break;
                }
                // If a connection was accepted
                if (socket != null) {
                    //4.管理socket输入输出流进行通讯
                    connectedThread = new ConnectedThread(socket);
                    connectedThread.start();
                    try {
                        mmServerSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
        }

        /** Will cancel the listening socket, and cause the thread to finish */
        public void cancel() {
            try {
                mmServerSocket.close();
            } catch (IOException e) { }
        }
    }


    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the input and output streams, using temp objects because
            // member streams are final
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) { }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];  // buffer store for the stream
            int bytes; // bytes returned from read()

            while (true) {
                try {
                    bytes = mmInStream.read(buffer);
                    byte[] outBuf = new byte[bytes];
                    System.arraycopy(buffer,0,outBuf,0,bytes);
                    mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, outBuf)
                            .sendToTarget();
                } catch (IOException e) {
                    break;
                }
            }
        }

        /* Call this from the main activity to send data to the remote device */
        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);
            } catch (IOException e) { }
        }

        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != connectedThread) {
            connectedThread.cancel();
        }
        if (null != acceptThread) {
            acceptThread.cancel();
        }
    }
}

客户端:

  1. 获取蓝牙设备对象
  2. 通过服务端mac发起配对
  3. 建立客户端socket对象
  4. 通过socket的connect()方法与服务端建立连接
  5. 管理socket输入输出流进行通讯
package com.bluetooth.client;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {
    private final String macAddress = "48:E6:C0:2D:CA:C5";
    private ConnectThread connectThread;
    private ConnectedThread connectedThread;
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constants.MESSAGE_READ:
                    Toast.makeText(MainActivity.this,"数据接收成功",Toast.LENGTH_LONG).show();
                    parseReceiveData((byte[]) msg.obj);
                    break;
            }
            super.handleMessage(msg);
        }
    };
    private BluetoothAdapter mBluetoothAdapter;

    private void parseReceiveData(byte[] obj) {
        Log.i("接收到数据:", "长度:"+obj.length+"数据:"+HexUtils.bcd2str(obj));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                JSONObject req = new JSONObject();
                byte[] bytes = null;
                try {
                    req.put("type", "face");
                    bytes = req.toString().getBytes("GBK");
                } catch (JSONException e) {
                    e.printStackTrace();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                if (null != bytes && null != connectedThread) {
                    connectedThread.write(bytes);
                }
            }
        });
        //1、获取蓝牙设备对象mBluetoothAdapter如果为空表示终端不存在蓝牙设备
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (null != mBluetoothAdapter) {
            //isEnabled判断蓝牙开关是否开启
            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, 99);
            } else {
                //2.通过服务端mac发起配对
                BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress);
                connectThread = new ConnectThread(device);
                connectThread.start();
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_OK) {
            BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(macAddress);
            connectThread = new ConnectThread(device);
            connectThread.start();
        }
    }

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;
        private final UUID MY_UUID_SECURE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
        public ConnectThread(BluetoothDevice device) {
            BluetoothSocket tmp = null;
            mmDevice = device;

            try {
                //3.建立客户端socket对象
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE);
            } catch (IOException e) { }
            mmSocket = tmp;
        }

        @Override
        public void run() {

            try {
                //4.通过socket的connect()方法与服务端建立连接
                mmSocket.connect();
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) { }
                return;
            }
            //5.管理socket输入输出流进行通讯
            connectedThread = new ConnectedThread(mmSocket);
            connectedThread.start();
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) { }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];  // buffer store for the stream
            int bytes; // bytes returned from read()

            while (true) {
                try {
                    bytes = mmInStream.read(buffer);
                    byte[] outBuf = new byte[bytes];
                    System.arraycopy(buffer,0,outBuf,0,bytes);
                    mHandler.obtainMessage(Constants.MESSAGE_READ, bytes, -1, outBuf)
                            .sendToTarget();
                } catch (IOException e) {
                    break;
                }
            }
        }

        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);
            } catch (IOException e) { }
        }

        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        connectedThread.cancel();
        connectThread.cancel();
    }
}

以上代码已经可以完成双方数据的收发,接着可以学习其他实用功能:

  • 扫描发现已配对设备
    //获取已经配对的设备
        Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            for (BluetoothDevice device : pairedDevices) {
                Log.i("已经配对的设备:",device.getName() + "|" + device.getAddress());
            }
        }
  • 扫描发现可配对设备(主要就是保存可配对设备的mac值,通过mac值进行配对连接)

调用方法mBluetoothAdapter.startDiscovery();开始扫描设备。这里需要声明权限

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

扫描获取到设备是异步的,所以用广播的方式。扫描的结果需要用广播接收:

 //扫描获取设备的广播接收器
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // When discovery finds a device
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // Get the BluetoothDevice object from the Intent
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                Log.i("扫描到新设备:",device.getName() + "|" + device.getAddress());
            }
        }
    };
 //广播过滤并注册
        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        registerReceiver(mReceiver, filter);

扫描并配对连接的动作通常由客户端操作,优化客户端的代码,最终的效果如图:

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值