Bootstrap

Android BLE设备操作之FastBle的使用

开源地址:FastBle

权限申请

在文件【AndroidManifest.xml】中添加权限:蓝牙权限位置权限

<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" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

build.gradle配置

在文件【build.gradle】中引入第三方框架:FastBle

dependencies {
    api 'com.clj.fastble:FastBleLib:2.2.4'
}

代码嵌入

myBleDevice

创建蓝牙设备类 myBleDevice.java

import android.app.Activity;
import android.bluetooth.BluetoothGatt;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;

import com.Interface.myBleCommInterface;
import com.Utils.myUtils;
import com.clj.fastble.BleManager;
import com.clj.fastble.callback.BleMtuChangedCallback;
import com.clj.fastble.callback.BleNotifyCallback;
import com.clj.fastble.callback.BleScanAndConnectCallback;
import com.clj.fastble.callback.BleWriteCallback;
import com.clj.fastble.data.BleDevice;
import com.clj.fastble.exception.BleException;
import com.clj.fastble.scan.BleScanRuleConfig;
import com.others.MainApp;

import java.util.ArrayList;
import java.util.Arrays;

import static com.others.MainApp.NOTIFY_UUID;
import static com.others.MainApp.SERVICE_UUID;
import static java.lang.Thread.sleep;

public class myBleDevice extends Activity {
    private BleDevice bleDeviceTemp = null; //蓝牙设备
    private Context context = null;
    private String BleNameConnected = null;
    private Handler handlerDefineMessage = null;
    private myBleCommInterface mybleCommInterface = null; //接口
    private myUtils myutils;
    private Handler mHandler = null;
    private int RxdOutTimer = 0;
    private ArrayList<byte[]> rd = new ArrayList<byte[]>();
    //ArrayList<byte[]> 转 byte[]
    private static byte[] byteMerger(ArrayList<byte[]> bytes) {
        //需要处理的是一个ArrayList<byte[]> bytes
        int lengthByte = 0;
        for(byte[] by : bytes){
            lengthByte +=by.length;
        }
        byte[] allbyte = new byte[lengthByte];
        int countLength=0;
        for(byte[] by : bytes){
            System.arraycopy(by,0,allbyte,countLength,by.length);
            countLength+=by.length;
        }
        return allbyte;
    }
    /***********************************************************************************************
     * 构造函数
     * @param context
     * @param mainHandler
     * @param bleName
     */
    public myBleDevice(Context context, Handler mainHandler, final myBleCommInterface mybleCommInterface, String bleName) {
        this.context = context;
        BleNameConnected = bleName;
        handlerDefineMessage = mainHandler;
        this.mybleCommInterface = mybleCommInterface;
        myutils = new myUtils();

        mHandler = new Handler();
        Runnable r = new Runnable() {
            @Override
            public void run() {
            //由于BLE设备的20字节的首发限制,此处代码主要完成的任务是,当接受的字节流大于20字节时,
            //从机BLE设备可能会进行分包发送。比如modbus的03读取的寄存器数量过多,
            //导致响应的字节流超过了20字节,则从机设备就会进行分包发送,每包不超过20字节,
            //而接收设备则需要进行收包并进行拼接。此处代买就是实现此功能。
                if(RxdOutTimer++ > 1){ //RxdOutTimer就是接收包之间的超时计数
                    if(!rd.isEmpty()){
                        byte[] rxdBytes = byteMerger(rd);
                        //Log.d("myDebug", Arrays.toString(rxdBytes));
                        //myutils.printHexString(rxdBytes);
                        //接受到通知消息 这里需要自己创建个回调接口,或者自己定义其他的数据处理方式。
                        //把接收到的数据发送处理进行解析处理。
                        mybleCommInterface.UpdateBleReceiveData(rxdBytes);
                        rd.clear();
                    }
                    RxdOutTimer = 0;
                }
                mHandler.postDelayed(this, 100);
            }
        };

        mHandler.postDelayed(r, 100);//延时100毫秒
    }

    /***********************************************************************************************
     * 获取Ble的设备
     * @return
     */
    public BleDevice getBleDevice() {
        return bleDeviceTemp;
    }

    /***********************************************************************************************
     * 扫描和连接蓝牙
     */
    public void scanAndConnect() {
        Log.e("myDebug", "BLE开始连接");
        BleManager.getInstance().scanAndConnect(new BleScanAndConnectCallback() {
            @Override
            public void onScanStarted(boolean success) {
                // 开始扫描(主线程)
                Log.e("myDebug", "BLE开始扫描");
            }

            @Override
            public void onScanFinished(BleDevice scanResult) {
                // 扫描结束,结果即为扫描到的第一个符合扫描规则的BLE设备,如果为空表示未搜索到(主线程)
                Log.e("myDebug", "BLE扫描结束");
                if (scanResult != null) {
                    Log.e("myDebug", "BLE扫描到了");
                } else {
                    Log.e("myDebug", "BLE没扫描到");
                    Toast.makeText(context, "BLE没扫描到", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onStartConnect() {
                Log.e("myDebug", "BLE开始连接");
                // 开始连接(主线程)
            }

            @Override
            public void onConnectFail(BleException exception) {
                // 连接失败(主线程)
                Toast.makeText(context, "BLE连接失败", Toast.LENGTH_SHORT).show();
                Log.e("myDebug", "BLE连接失败");
            }

            @Override
            public void onConnectSuccess(BleDevice bleDevice, BluetoothGatt gatt, int status) {
                // 连接成功,BleDevice即为所连接的BLE设备(主线程)
                Toast.makeText(context, "BLE连接成功", Toast.LENGTH_SHORT).show();
                Log.e("myDebug", "BLE连接成功");
                Log.e("myDebug", BleManager.getInstance().getAllConnectedDevice().size() + "");
                sendMessage(101);
                bleDeviceTemp = bleDevice;
                //设置MTU
                BleManager.getInstance().setMtu(bleDeviceTemp, 512, new BleMtuChangedCallback() {
                    @Override
                    public void onSetMTUFailure(BleException exception) {
                        // 设置MTU失败
                        Log.e("myDebug", "BLE设置MTU失败");
                    }

                    @Override
                    public void onMtuChanged(int mtu) {
                        // 设置MTU成功,并获得当前设备传输支持的MTU值
                        Log.e("myDebug", "BLE设置MTU成功,并获得当前设备传输支持的MTU值  " + mtu);
                        //启动蓝牙接收消息
                        startNotify(bleDeviceTemp);
                    }
                });
            }

            @Override
            public void onDisConnected(boolean isActiveDisConnected, BleDevice device, BluetoothGatt gatt, int status) {
                // 连接断开,isActiveDisConnected是主动断开还是被动断开(主线程)
                Toast.makeText(context, "BLE连接断开", Toast.LENGTH_SHORT).show();
                Log.e("myDebug", "BLE连接断开");
                Log.e("myDebug", isActiveDisConnected + "");
                sendMessage(102);
            }
        });
    }

    /***********************************************************************************************
     *  蓝牙连接
     */
    public void BleConnectAction() {
        BleScanRuleConfig scanRuleConfig = new BleScanRuleConfig.Builder()
//                        .setServiceUuids(serviceUuids)      // 只扫描指定的服务的设备,可选
                .setDeviceName(true, BleNameConnected)   // 只扫描指定广播名的设备,可选
//                .setDeviceMac(mac)                  // 只扫描指定mac的设备,可选
//                .setAutoConnect(isAutoConnect)      // 连接时的autoConnect参数,可选,默认false
                .setScanTimeOut(10000)              // 扫描超时时间,可选,默认10秒
                .build();
        BleManager.getInstance().initScanRule(scanRuleConfig);
    }

    /***********************************************************************************************
     * 发送数据函数
     * @param WriteBytes
     */
    public void SendData(int[] WriteBytes) {
        int len = WriteBytes.length;
        byte[] outData = new byte[len];


        for (int i = 0; i < len; i++) {
            if ((WriteBytes[i] >= 128) && (WriteBytes[i] <= 255)) {
                outData[i] = (byte) ((WriteBytes[i] - 256) & 0xFF);
            } else {
                outData[i] = (byte) (WriteBytes[i] & 0xFF);
            }
        }

        BleManager.getInstance().write(
                bleDeviceTemp,
                SERVICE_UUID,
                MainApp.WRITE_UUID,
                outData,
                new BleWriteCallback() {
                    @Override
                    public void onWriteSuccess(final int current, final int total, final byte[] justWrite) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //Toast.makeText(context, "写入成功", Toast.LENGTH_SHORT).show();
                                //Log.d("myDebug", "写入成功");
                            }
                        });
                    }

                    @Override
                    public void onWriteFailure(final BleException exception) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(context, "写入失败", Toast.LENGTH_SHORT).show();
                                Log.e("myDebug", "写入失败");
                                Log.e("myDebug", String.valueOf(exception));
                            }
                        });
                    }
                });
    }

    /***********************************************************************************************
     * 发送数据函数 注意BLE发送的20字节限制,不同设备可能限制的最大发送字节数不一样,建议发送数据
     * 时自行分包发送,这样可适用于所有BLE设备。
     * @param outData
     */
    public void SendData(byte[] outData) {
        BleManager.getInstance().write(
                bleDeviceTemp,
                SERVICE_UUID,
                MainApp.WRITE_UUID,
                outData,
                new BleWriteCallback() {
                    @Override
                    public void onWriteSuccess(final int current, final int total, final byte[] justWrite) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //Toast.makeText(context, "写入成功", Toast.LENGTH_SHORT).show();
                                //Log.d("myDebug", "写入成功");
                            }
                        });
                    }

                    @Override
                    public void onWriteFailure(final BleException exception) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(context, "写入失败", Toast.LENGTH_SHORT).show();
                                Log.e("myDebug", "写入失败");
                                Log.e("myDebug", String.valueOf(exception));
                            }
                        });
                    }
                });
    }

    /***********************************************************************************************
     * 蓝牙接收数据
     * @param bleDevice
     */
    private void startNotify(BleDevice bleDevice) {
        BleManager.getInstance().notify(
                bleDevice,
                SERVICE_UUID,
                NOTIFY_UUID,
                new BleNotifyCallback() {

                    @Override
                    public void onNotifySuccess() {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //打开通知成功
                            }
                        });
                    }

                    @Override
                    public void onNotifyFailure(final BleException exception) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                //打开通知失败
                            }
                        });
                    }

                    /*******************************************************************************
                     * 数据接收处理
                     * @param data
                     */
                    @Override
                    public void onCharacteristicChanged(final byte[] data) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                rd.add(data); //追加方式接收代码
                                RxdOutTimer = 0;
                            }
                        });
                    }
                });
    }
}

MainActivity

在MainActivity中调用

 protected void onCreate(Bundle savedInstanceState) {
	    super.onCreate(savedInstanceState);
        //setTheme(R.style.AppTheme_Launcher);
        setContentView(R.layout.activity_main);
		// 获取本地蓝牙适配器
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        // 如果没有蓝牙适配器,则不支持
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "蓝牙不可用", Toast.LENGTH_LONG).show();
            finish();
            return;
        }
        BleManager.getInstance().init(getApplication());
        BleManager.getInstance()
                .enableLog(true)
                .setMaxConnectCount(1)
                .setOperateTimeout(5000);
	//BleNameConnected 需要连接的蓝牙名称,本程序需求是连接指定蓝牙名称的的设备,其他应用可根据具体需求自行修改代码。
        mybleDevice = new myBleDevice(MainActivity.this, handlerDefineMessage, this, BleNameConnected);
        mybleDevice.BleConnectAction();
        checkPermissions(); //权限处理
}
 /***********************************************************************************************
     * 检查权限
     */
    private void checkPermissions() {
        BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (!bluetoothAdapter.isEnabled()) {
            Toast.makeText(this, getString(R.string.please_open_blue), Toast.LENGTH_LONG).show();
            return;
        }

        String[] permissions = {
                Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA,
                Manifest.permission.ACCESS_WIFI_STATE,
                Manifest.permission.CHANGE_WIFI_STATE,
                Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS,
                Manifest.permission.WRITE_SETTINGS};
        List<String> permissionDeniedList = new ArrayList<>();

        for (String permission : permissions) {
            int permissionCheck = checkSelfPermission(permission);
            if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
                onPermissionGranted(permission);
            } else {
                permissionDeniedList.add(permission);
            }
        }
        if (!permissionDeniedList.isEmpty()) {
            String[] deniedPermissions = permissionDeniedList.toArray(new String[permissionDeniedList.size()]);
            requestPermissions(deniedPermissions, REQUEST_CODE_PERMISSION_LOCATION);
        }
    }
;