Android功能记录(六) ------ Android手机通过蓝牙(BLE)与硬件进行通信

  • 参考1:http://www.jcodecraeer.com/a/opensource/2017/1106/8705.html
  • 参考2:https://blog.csdn.net/qq_30552993/article/details/51822726

好不容易折腾出来了,一把辛酸泪啊,参考2方法试了不行,但是我参照参考2的结构写的,参考1是可以用的,包括连接/发送/接收,在此把完整代码放出来,其中掺杂我自己项目要用的代码

主要工具类BleUtil (基本都注释了):

package ylcx.com.blue;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

import com.vise.baseble.ViseBle;
import com.vise.baseble.callback.IBleCallback;
import com.vise.baseble.callback.IConnectCallback;
import com.vise.baseble.callback.scan.IScanCallback;
import com.vise.baseble.callback.scan.ScanCallback;
import com.vise.baseble.common.PropertyType;
import com.vise.baseble.core.BluetoothGattChannel;
import com.vise.baseble.core.DeviceMirror;
import com.vise.baseble.core.DeviceMirrorPool;
import com.vise.baseble.exception.BleException;
import com.vise.baseble.model.BluetoothLeDevice;
import com.vise.baseble.model.BluetoothLeDeviceStore;
import com.vise.baseble.model.resolver.GattAttributeResolver;
import com.vise.baseble.utils.HexUtil;
import com.vise.log.ViseLog;
import com.vise.xsnow.cache.SpCache;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;

import static com.vise.utils.handler.HandlerUtil.runOnUiThread;

public class BleUtil {

    private static final String TAG = "BleUtil";

    public static String WriteUUID = "00008877-0000-1000-8000-00805f9b34fb"; //APP发送命令
    public static String ReadUUID = "00008888-0000-1000-8000-00805f9b34fb"; //BLE用于回复命令
    private static final String LIST_NAME = "NAME";
    private static final String LIST_UUID = "UUID";
    public static final String WRITE_CHARACTERISTI_UUID_KEY = "write_uuid_key";
    public static final String WRITE_DATA_KEY = "write_data_key";

    private Context mContext;
    private static BleUtil mInstance;

    private DeviceMirror deviceMirror;
    private SpCache mSpCache;
    private BTUtilListener mListener;
    private BluetoothLeDevice mCurDevice;
    private List<BluetoothLeDevice> listDevice;
    private List<BluetoothGattService> mGattServices;//服务
    // 设备特征值集合
    private List<List<BluetoothGattCharacteristic>> mGattCharacteristics = new ArrayList<>();
    public boolean isConnect = false;
    private DeviceMirrorPool mDeviceMirrorPool;
    private  DataManage dataManage;
    public static synchronized BleUtil getInstance() {
        if (mInstance == null) {
            mInstance = new BleUtil();
        }
        return mInstance;
    }
    //初始化
    public void setContext(Context context) {
        mContext = context;
        init();
    }
    public void init() {
        listDevice = new ArrayList<>();
        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            showToast("BLE不支持此设备!");
            ((Activity) mContext).finish();
        }
        if (mContext == null) {
            return;
        }
        //蓝牙相关配置修改
        ViseBle.config()
                .setScanTimeout(-1)//扫描超时时间,这里设置为永久扫描
                //.setScanRepeatInterval(5 * 1000)//扫描间隔5秒
                .setConnectTimeout(10 * 1000)//连接超时时间
                .setOperateTimeout(5 * 1000)//设置数据操作超时时间
                .setConnectRetryCount(3)//设置连接失败重试次数
                .setConnectRetryInterval(1000)//设置连接失败重试间隔时间
                .setOperateRetryCount(3)//设置数据操作失败重试次数
                .setOperateRetryInterval(1000)//设置数据操作失败重试间隔时间
                .setMaxConnectCount(3);//设置最大连接设备数量
        
        //蓝牙信息初始化,全局唯一,必须在应用初始化时调用
        ViseBle.getInstance().init(mContext.getApplicationContext());
        mDeviceMirrorPool = ViseBle.getInstance().getDeviceMirrorPool();
        mSpCache = new SpCache(mContext);
        scanLeDevice(true);
    }
    private ScanCallback mLeScanCallback = new ScanCallback(new IScanCallback() {
        @Override
        //发现设备
        public void onDeviceFound(final BluetoothLeDeviceStore bluetoothLeDeviceStore) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    //扫描到后与Rockrt进行比较
                    List<BluetoothLeDevice> BlueList = bluetoothLeDeviceStore.getDeviceList();
                    for (int i = 0; i < BlueList.size(); i++) {
                        if (!listDevice.contains(BlueList.get(i))) {
                            //不重复添加
                            listDevice.add(BlueList.get(i));
                        }
                    }
                    mListener.onLeScanDevices(listDevice);
                }
            });
        }
        @Override
        public void onScanFinish(BluetoothLeDeviceStore bluetoothLeDeviceStore) {
            ViseLog.i("scan finish " + bluetoothLeDeviceStore);
        }
        @Override
        public void onScanTimeout() {
            ViseLog.i("scan timeout");
        }
    });

    //扫描/停止扫描设备
    public void scanLeDevice(final boolean enable) {
        if (enable) {
            startScan();
        } else {
            stopScan();
        }
    }

    //开始扫描BLE设备
    private void startScan() {
        ViseBle.getInstance().startScan(mLeScanCallback);
        mListener.onLeScanStart();
    }

    //停止扫描BLE设备
    private void stopScan() {
        ViseBle.getInstance().stopScan(mLeScanCallback);
        mListener.onLeScanStop();
    }

    /**
     * 连接回调
     */
    private IConnectCallback mGattCallback = new IConnectCallback() {

        @Override
        public void onConnectSuccess(final DeviceMirror devicemirror) {
            deviceMirror = devicemirror;
            ConnectResult(true);
            mListener.onConnected(mCurDevice);
        }

        @Override
        public void onConnectFailure(BleException exception) {
            showToast("连接失败");
            ConnectResult(false);

        }

        @Override
        public void onDisconnect(boolean isActive) {
            showToast("断开连接");
            ConnectResult(false);
            mListener.onDisConnected(mCurDevice);
        }
    };
    /**
     * 操作数据回调,下面读写操作绑定的回调就是这个
     */
    private IBleCallback bleCallback = new IBleCallback() {
        @Override
        public void onSuccess(final byte[] data, BluetoothGattChannel bluetoothGattInfo, BluetoothLeDevice bluetoothLeDevice) {
            if (data == null) {
                return;
            }
            if(bluetoothGattInfo != null && (bluetoothGattInfo.getPropertyType() == PropertyType.PROPERTY_READ))
            {
                 Log.d(TAG, "Data: " +HexUtil.encodeHexStr(data) );
                receiveData(HexUtil.encodeHexStr(data));
            }
            mCurDevice = bluetoothLeDevice;
            if(bluetoothGattInfo != null && (bluetoothGattInfo.getPropertyType() == PropertyType.PROPERTY_WRITE))
            {
                BindReadChannel(); //因为我这里是根据发送的命令返回,所以当写成功时重新绑定读通道
            }
        }
        @Override
        public void onFailure(BleException exception) {
            if (exception == null) {
                return;
            }
            Log.d(TAG,"callback fail:" + exception.getDescription());
        }
    };
    //连接后的处理
    public void ConnectResult(boolean isSuccess) {
        if (isSuccess) {
               Log.d(TAG, "连接成功");
               if(deviceMirror != null && deviceMirror.getBluetoothGatt() != null)
               {
                   isConnect = true;
                   GetServicesAndCharacteristic(deviceMirror.getBluetoothGatt().getServices()); //获取设备根据GATT服务显示该服务下的所有特征值
                   BindWriteChannel();
                   BindReadChannel();
               }
        }else{
            isConnect = false;
            //重新发起连接
            Connect();
        }
    }
    //绑定通道
    public void BindWriteChannel()
    {
        if(deviceMirror != null && deviceMirror.getBluetoothGatt() != null)
        {
            setPro(WriteUUID);
            GattServicesW();
        }
    }
    public void BindReadChannel(){
        if(deviceMirror != null && deviceMirror.getBluetoothGatt() != null)
        {
            setPro(ReadUUID);                                                  //定位该特征为在所有特征值里面的绝对位置
            GattServicesR();
        }
    }
    //获取设备根据GATT服务显示该服务下的所有特征值
    private void GetServicesAndCharacteristic(final List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;

        String uuid;
        final String unknownServiceString = "unknown_service";
        final String unknownCharaString = "unknown_characteristic";
        final List<Map<String, String>> gattServiceData = new ArrayList<>();
        final List<List<Map<String, String>>> gattCharacteristicData = new ArrayList<>();
        mGattServices = new ArrayList<>();
        mGattCharacteristics = new ArrayList<>();
        // 循环遍历services
        for (final BluetoothGattService gattService : gattServices) {
            final Map<String, String> currentServiceData = new HashMap<>();
            uuid = gattService.getUuid().toString();

            currentServiceData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);

            gattServiceData.add(currentServiceData);
            final List<Map<String, String>> gattCharacteristicGroupData = new ArrayList<>();
            final List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
            final List<BluetoothGattCharacteristic> charas = new ArrayList<>();
            // Loops through available Characteristics.
            for (final BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                charas.add(gattCharacteristic);
                final Map<String, String> currentCharaData = new HashMap<>();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(LIST_NAME, GattAttributeResolver.getAttributeName(uuid, unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);
                gattCharacteristicGroupData.add(currentCharaData);
            }
            mGattServices.add(gattService);
            mGattCharacteristics.add(charas);
            gattCharacteristicData.add(gattCharacteristicGroupData);
        }
    }
    //定位读写特征值的坐标
    private int prox=0;         //定位读写操作的坐标
    private int proy=0;         //定位读写操作的坐标
    private void setPro(String uuid){
        int x=0; int y=0;
        //BluetoothGattService是GATT数据的大集合,
        for (BluetoothGattService goo : mGattServices) {
            //List是BluetoothGattService里面的子集,包含一部分数据
            List<BluetoothGattCharacteristic> lt2 = goo.getCharacteristics();
            for (BluetoothGattCharacteristic goo2 : goo.getCharacteristics()) {
                if (goo2.getUuid().toString().equals(uuid)) {
                    prox = x;   //BluetoothGattService第一层数据
                    proy = y;  //BluetoothGattService第二层数据
                }
                y++;
            }
            x++; y=0;
        }
    }
    //绑定通道
    private void GattServicesW() {
        //获取特定的服务
        final BluetoothGattService service = mGattServices.get(prox);
        //获取特定的特征
        final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(prox).get(proy);
        //负责设置操作
        mSpCache.put(WRITE_CHARACTERISTI_UUID_KEY + mCurDevice.getAddress(), characteristic.getUuid().toString());
        DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(mCurDevice);
        if (deviceMirror != null) {
            BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
                    .setBluetoothGatt(deviceMirror.getBluetoothGatt())
                    .setPropertyType(PropertyType.PROPERTY_WRITE)
                    .setServiceUUID(service.getUuid())
                    .setCharacteristicUUID(characteristic.getUuid())
                    .setDescriptorUUID(null)
                    .builder();
            deviceMirror.bindChannel(bleCallback, bluetoothGattChannel);
        }
    }
    private void GattServicesR() {
        //获取特定的服务
        final BluetoothGattService service = mGattServices.get(prox);
        //获取特定的特征
        final BluetoothGattCharacteristic characteristic = mGattCharacteristics.get(prox).get(proy);
        final int charaProp = characteristic.getProperties();
        DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(mCurDevice);
       if ((charaProp & BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
            BluetoothGattChannel bluetoothGattChannel = new BluetoothGattChannel.Builder()
                    .setBluetoothGatt(deviceMirror.getBluetoothGatt())
                    .setPropertyType(PropertyType.PROPERTY_READ)
                    .setServiceUUID(service.getUuid())
                    .setCharacteristicUUID(characteristic.getUuid())
                    .setDescriptorUUID(null)
                    .builder();
            deviceMirror.bindChannel(bleCallback, bluetoothGattChannel);
            deviceMirror.readData();
        }
    }

    //接收数据,对其进行处理
    private void receiveData( String data) {
        dataManage.addMessageAndShow(data,"read");
    }

    //连接设备
    public void connectLeDevice(int devicePos) {
        scanLeDevice(false);  //停止扫描
        mCurDevice = listDevice.get(devicePos);
        Connect();
    }
    //连接/重连设备
    private void Connect() {
        if (!ViseBle.getInstance().isConnect(mCurDevice)) {
            Log.d(TAG, "开始连接:" + mCurDevice);
            ViseBle.getInstance().connect(mCurDevice, mGattCallback); //开始连接
            mListener.onConnecting(mCurDevice);
        }
    }
    //  断开设备连接
    private void disConnGatt() {
        if (ViseBle.getInstance().isConnect(mCurDevice)) {
            ViseBle.getInstance().disconnect(mCurDevice);
            listDevice = new ArrayList<>();
            mListener.onDisConnected(mCurDevice);
        }
    }
    private Queue<byte[]> dataInfoQueue = new LinkedList<>();
    //发送数据
    public void sendMsg(String s) {
        if(isConnect)
        {
            mSpCache.put(WRITE_DATA_KEY + mCurDevice.getAddress(), s);  //尚不清楚,反正就有

            if (dataInfoQueue != null) {
                dataInfoQueue.clear();
                dataInfoQueue = splitPacketFor20Byte(HexUtil.decodeHex(s.toCharArray()));
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        send(mCurDevice);
                    }
                });
            }
        }
    }
    private void send(final BluetoothLeDevice bluetoothLeDevice) {
        if (dataInfoQueue != null && !dataInfoQueue.isEmpty()) {
            DeviceMirror deviceMirror = mDeviceMirrorPool.getDeviceMirror(bluetoothLeDevice);
            if (dataInfoQueue.peek() != null && deviceMirror != null) {
                deviceMirror.writeData(dataInfoQueue.poll());
            }
            if (dataInfoQueue.peek() != null) {
                new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        send(bluetoothLeDevice);
                    }
                }, 100);
            }
        }
    }
    //数据分包
    private Queue<byte[]> splitPacketFor20Byte(byte[] data) {
        Queue<byte[]> dataInfoQueue = new LinkedList<>();
        if (data != null) {
            int index = 0;
            do {
                byte[] surplusData = new byte[data.length - index];
                byte[] currentData;
                System.arraycopy(data, index, surplusData, 0, data.length - index);
                if (surplusData.length <= 20) {
                    currentData = new byte[surplusData.length];
                    System.arraycopy(surplusData, 0, currentData, 0, surplusData.length);
                    index += surplusData.length;
                } else {
                    currentData = new byte[20];
                    System.arraycopy(data, index, currentData, 0, 20);
                    index += 20;
                }
                dataInfoQueue.offer(currentData);
            } while (index < data.length);
        }
        return dataInfoQueue;
    }

    private void showToast(String message) {
        Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
    }

    public void setBTUtilListener(BTUtilListener listener) {
        mListener = listener;
    }
    public void setDataManage(DataManage dataMan)
    {
        dataManage = dataMan;
    }
    public interface BTUtilListener {
        String bluName = "Rocket";

        void onLeScanStart(); // 扫描开始

        void onLeScanStop();  // 扫描停止

        void onLeScanDevices(List<BluetoothLeDevice> listDevice); //扫描得到的设备

        void onConnected(BluetoothLeDevice mCurDevice); //设备的连接

        void onDisConnected(BluetoothLeDevice mCurDevice); //设备断开连接

        void onConnecting(BluetoothLeDevice mCurDevice); //设备连接中

        void onDisConnecting(BluetoothLeDevice mCurDevice); //设备连接失败
    }
}

BluListen 类:

package ylcx.com.blue;


import com.vise.baseble.model.BluetoothLeDevice;

import java.util.List;

public class BluListen implements BleUtil.BTUtilListener {
    public String TAG = "BluListen";
    @Override
    public void onLeScanStart() {

    }

    @Override
    public void onLeScanStop() {

    }

    @Override
    public void onLeScanDevices(List<BluetoothLeDevice> listDevice) {
           for(int i = 0;i<listDevice.size();i++)
           {
               if(listDevice.get(i).getName() != null && listDevice.get(i).getName().equals(bluName))
               {
                     BleUtil.getInstance().connectLeDevice(i);
               }
           }
    }

    @Override
    public void onConnected(BluetoothLeDevice mCurDevice) {

    }

    @Override
    public void onDisConnected(BluetoothLeDevice mCurDevice) {

    }

    @Override
    public void onConnecting(BluetoothLeDevice mCurDevice) {

    }

    @Override
    public void onDisConnecting(BluetoothLeDevice mCurDevice) {

    }

}

接收数据处理类DataManage:

package ylcx.com.blue;

import android.content.Context;
import android.os.Handler;

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

public class DataManage {
    public MainActivity mainActivity;
    public Context mContext;
    public List<String> message;
    public void setMainActivity(Context context,MainActivity activity)
    {
        mContext = context;
        message = new ArrayList<>();
        mainActivity = activity;
    }
    public void addMessageAndShow(final String msg, final String isSendOrWrite)
    {
       message.add(msg);
        new Handler(mContext.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                mainActivity.setDataText(msg,isSendOrWrite);
            }
        });

    }
}

MainActivity调用:

package ylcx.com.blue;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    public Context mContext;
    public Button send;
    public EditText str;
    public TextView receive;

    public Button sendRegister;
    public Button sendLight;
    public Button sendClock;
    public String TAG ="MainActivity";
    public  DataManage dataManage;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;

        send = findViewById(R.id.send);
        str = findViewById(R.id.sendStr);
        receive = findViewById(R.id.receive);
        receive.setMovementMethod(ScrollingMovementMethod.getInstance());//滚动

        turnOnBlueTooth(this,0);
        BleUtil.getInstance().setBTUtilListener(new BluListen());
        BleUtil.getInstance().setContext(mContext);

        dataManage = new DataManage();
        dataManage.setMainActivity(this,this);
        BleUtil.getInstance().setDataManage(dataManage);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    /**
     * 打开蓝牙
     */
    public void turnOnBlueTooth(Activity activity, int requestCode) {
        //通过Intent获取蓝牙
        Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        //获取返回结果
        activity.startActivityForResult(intent, requestCode);
    }

    public void setDataText(String msg,String isSendOrWrite)
    {
        String str = receive.getText().toString();
        str += "\n";
        str += isSendOrWrite+":"+msg;
        receive.setText(str);
    }

}

AndroidManifest权限申请:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ylcx.com.blue">

    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission-sdk-23 android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

猜你喜欢

转载自blog.csdn.net/dengshunhao/article/details/80880926