精华内容
下载资源
问答
  • Android 低功耗蓝牙

    2017-11-24 15:46:58
    安卓低功耗蓝牙与硬件设备扫描,连接,收发数据代码。由于不同设备的UUID不同,使用时需根据实际情况,修改DeviceControlActivity 143行,151行 处的 UUID的值。(该值一般由硬件工程师给出)
  • Android低功耗蓝牙

    2018-12-03 16:11:33
    花了半个多月,写Android BLE,看了github上很多源码。   import android.annotation.SuppressLint; import android.app.Service; import android.bluetooth.BluetoothAdapter; import android.bluetooth....

    花了半个多月,写Android BLE,看了github上很多源码。

     

    
    import android.annotation.SuppressLint;
    import android.app.Service;
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothGatt;
    import android.bluetooth.BluetoothGattCallback;
    import android.bluetooth.BluetoothGattCharacteristic;
    import android.bluetooth.BluetoothGattDescriptor;
    import android.bluetooth.BluetoothGattService;
    import android.bluetooth.BluetoothManager;
    import android.bluetooth.BluetoothProfile;
    import android.content.Context;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.os.Binder;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.util.Log;
    import java.util.List;
    import java.util.Timer;
    import java.util.TimerTask;
    import java.util.UUID;
    import android.app.Activity;
    
    import btlescan.ui.control.DeviceControlActivity;
    
    
    //creator by zhangkun on 2018-12-1
    @SuppressLint("NewApi")
    public class PkeService extends Service {
    
        private final static String TAG = PkeService.class.getSimpleName();
    
        private BluetoothManager mBluetoothManager;
        private BluetoothAdapter mBluetoothAdapter;
        private String mBluetoothDeviceAddress;
        private BluetoothGatt mBluetoothGatt;
    
        BluetoothGattService linkLossService_pke;
        BluetoothGattCharacteristic alertLevel_pke;
        private int mConnectionState = STATE_DISCONNECTED;
    
        private static final int STATE_DISCONNECTED = 0;
        private static final int STATE_CONNECTING = 1;
        private static final int STATE_CONNECTED = 2;
    
    
        public final static String ACTION_GATT_CONNECTED = "com.choicemmed.bledemo.ACTION_GATT_CONNECTED";
        public final static String ACTION_GATT_DISCONNECTED = "com.choicemmed.bledemo.ACTION_GATT_DISCONNECTED";
        public final static String ACTION_GATT_SERVICES_DISCOVERED = "com.choicemmed.bledemo.ACTION_GATT_SERVICES_DISCOVERED";
        public final static String ACTION_DATA_AVAILABLE = "com.choicemmed.bledemo.ACTION_DATA_AVAILABLE";
        public final static String EXTRA_DATA = "com.choicemmed.bledemo.EXTRA_DATA";
    
        public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
    
        //zhangkun add 2018-11-15
        public SharedPreferences pkeUserSettings = null;
        private String PkeMacAddr = "";
        private boolean mScanning;
        private static BluetoothDevice PkeDevice;
        private int PkeRssiQualified = 0;
        // Implements callback methods for GATT events that the app cares about. For
        // example,
        // connection change and services discovered.
        private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                                int newState) {
                String intentAction;
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    intentAction = ACTION_GATT_CONNECTED;
                    mConnectionState = STATE_CONNECTED;
            //        broadcastUpdate(intentAction);
                    Log.i(TAG, "Connected to GATT server.");
                    // Attempts to discover services after successful connection.
            //        Log.i(TAG, "Attempting to start service discovery:"
            //                + mBluetoothGatt.discoverServices());
    
                    Log.e("onConnec中中中", "连接成功:");
                    gatt.discoverServices();//必须有,可以让onServicesDiscovered显示所有Services
    
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    intentAction = ACTION_GATT_DISCONNECTED;
                    mConnectionState = STATE_DISCONNECTED;
                    Log.i(TAG, "Disconnected from GATT server.");
                //    broadcastUpdate(intentAction);
                }
            }
    
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
            //        broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
                    List<BluetoothGattService> list = mBluetoothGatt.getServices();
                    for (BluetoothGattService bluetoothGattService:list){
                        String str = bluetoothGattService.getUuid().toString();
                        Log.e("onServicesDisc中中中", " :" + str);
                        List<BluetoothGattCharacteristic> gattCharacteristics = bluetoothGattService
                                .getCharacteristics();
                        for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                            Log.e("onServicesDisc中中中", " :" + gattCharacteristic.getUuid());
                            if("0000ffe1-0000-1000-8000-00805f9b34fb".equals(gattCharacteristic.getUuid().toString())){
                                linkLossService_pke=bluetoothGattService;
                                alertLevel_pke=gattCharacteristic;
                                Log.e("daole",alertLevel_pke.getUuid().toString());
                            }
                        }
    
                    }
                    enableNotification(true,gatt,alertLevel_pke);//必须要有,否则接收不到数据
                } else {
                    Log.w(TAG, "onServicesDiscovered received: " + status);
                }
            }
    
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic characteristic, int status) {
                Log.d(TAG, "onCharacteristicRead:" +status);
                Log.e("onCharacteristicRead中", "数据接收了哦"+bytesToHexString(characteristic.getValue()));
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                }
            }
    
            @Override
            public void onCharacteristicWrite(BluetoothGatt gatt,
                                              BluetoothGattCharacteristic characteristic, int status) {
                System.out.println("--------write success----- status:" + status);
                Log.e("onCharacteristicWrite中", "数据发送了哦");
                Log.e("onCharacteristicWrite中", bytesToHexString(characteristic.getValue()));
                if(status == BluetoothGatt.GATT_SUCCESS){//写入成功
                    Log.e("onCharacteristicWrite中", "写入成功");
                    //              tx_display.append("写入成功");
                }else if (status == BluetoothGatt.GATT_FAILURE){
                    Log.e("onCharacteristicWrite中", "写入失败");
                }else if (status == BluetoothGatt.GATT_WRITE_NOT_PERMITTED){
                    Log.e("onCharacteristicWrite中", "没权限");
                }
            };
    
            /*
             * when connected successfully will callback this method
             * this method can dealwith send password or data analyze
             *
             * */
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt,
                                                BluetoothGattCharacteristic characteristic) {
                Log.e("CharacteristicChanged中", "数据接收了哦"+bytesToHexString(characteristic.getValue()));
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
    
            @Override
            public void onDescriptorWrite(BluetoothGatt gatt,
                                          BluetoothGattDescriptor descriptor, int status) {
                UUID uuid = descriptor.getCharacteristic().getUuid();
            }
    
            @Override
            public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
                System.out.println("rssi = " + rssi);
            }
        };
    
        private void broadcastUpdate(final String action) {
            final Intent intent = new Intent(action);
            sendBroadcast(intent);
        }
    
        private void broadcastUpdate(final String action,
                                     final BluetoothGattCharacteristic characteristic) {
            final Intent intent = new Intent(action);
    
            byte[] heartRate = characteristic.getValue();
            String s = DeviceControlActivity.byte2HexStr(heartRate);
            String data = DeviceControlActivity.print10(s);
            if (data != null ) {
                intent.putExtra(EXTRA_DATA, data);
            }
            sendBroadcast(intent);
        }
        public class LocalBinder extends Binder {
            public PkeService getService() {
                return PkeService.this;
            }
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            // After using a given device, you should make sure that
            // BluetoothGatt.close() is called
            // such that resources are cleaned up properly. In this particular
            // example, close() is
            // invoked when the UI is disconnected from the Service.
            close();
            return super.onUnbind(intent);
        }
    
        private final IBinder mBinder = new LocalBinder();
    
        /**
         * Initializes a reference to the local Bluetooth adapter.
         *
         * @return Return true if the initialization is successful.
         */
        public boolean initialize() {
            // For API level 18 and above, get a reference to BluetoothAdapter
            // through
            // BluetoothManager.
            if (mBluetoothManager == null) {
                mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
                if (mBluetoothManager == null) {
                    Log.e(TAG, "Unable to initialize BluetoothManager.");
                    return false;
                }
            }
    
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
                return false;
            }else
            {
                ReadPkeStatus();
    
            }
    
            return true;
        }
    
        public void closePke()
        {
            timerPke.cancel();
            mBluetoothGatt.close();
        }
    
        /**
         * Connects to the GATT server hosted on the Bluetooth LE device.
         *
         * @param address
         *            The device address of the destination device.
         *
         * @return Return true if the connection is initiated successfully. The
         *         connection result is reported asynchronously through the
         *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
         *         callback.
         */
        public boolean connect(final String address, final BluetoothDevice device ) {
            if (mBluetoothAdapter == null || address == null) {
                Log.w(TAG,
                        "BluetoothAdapter not initialized or unspecified address.");
                return false;
            }
    
            if (device == null) {
                Log.d(TAG, "没有设备");
                return false;
            }
            // We want to directly connect to the device, so we are setting the
            // autoConnect
            // parameter to false.
            mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
            Log.d(TAG, "Trying to create a new connection.");
            mBluetoothDeviceAddress = address;
            mConnectionState = STATE_CONNECTING;
            return true;
        }
    
        /**
         * Disconnects an existing connection or cancel a pending connection. The
         * disconnection result is reported asynchronously through the
         * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
         * callback.
         */
        public void disconnect() {
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
            mBluetoothGatt.disconnect();
        }
    
        /**
         * After using a given BLE device, the app must call this method to ensure
         * resources are released properly.
         */
        public void close() {
            if (mBluetoothGatt == null) {
                return;
            }
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
    
        public void wirteCharacteristic(BluetoothGattCharacteristic characteristic) {
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
            mBluetoothGatt.writeCharacteristic(characteristic);
    
        }
    
        /**
         * Request a read on a given {@code BluetoothGattCharacteristic}. The read
         * result is reported asynchronously through the
         * {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
         * callback.
         *
         * @param characteristic
         *            The characteristic to read from.
         */
        public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
            Log.d(TAG, "readCharacteristic: "+characteristic.getProperties());
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                Log.d(TAG, "BluetoothAdapter为空");
                return;
            }
            mBluetoothGatt.readCharacteristic(characteristic);
        }
    
        /**
         * Enables or disables notification on a give characteristic.
         *
         * @param characteristic
         *            Characteristic to act on.
         * @param enabled
         *            If true, enable notification. False otherwise.
         */
        public void setCharacteristicNotification(
                BluetoothGattCharacteristic characteristic, boolean enabled) {
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                Log.d(TAG, "BluetoothAdapter为空");
                return;
            }
            mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID
                    .fromString(CLIENT_CHARACTERISTIC_CONFIG));
            if (descriptor != null) {
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                mBluetoothGatt.writeDescriptor(descriptor);
            }
        }
    
        /**
         * Retrieves a list of supported GATT services on the connected device. This
         * should be invoked only after {@code BluetoothGatt#discoverServices()}
         * completes successfully.
         *
         * @return A {@code List} of supported services.
         */
        public List<BluetoothGattService> getSupportedGattServices() {
            if (mBluetoothGatt == null)
                return null;
    
            return mBluetoothGatt.getServices();
        }
    
        /**
         * Read the RSSI for a connected remote device.
         * */
        public boolean getRssiVal() {
            if (mBluetoothGatt == null)
                return false;
    
            return mBluetoothGatt.readRemoteRssi();
        }
    
    
        //  Bluetooth PKE scan device callback function by Zhangkun on 2018-11-26
        private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
                Log.e("LeScanCallback-onLeScan", " "+device.toString()+ "rssi:"+rssi);
                if(device.toString().contains(PkeMacAddr))
                {
                    //    Log.e("LeScanCallback-onLeScan", " "+device.toString()+ rssi + "uuids "+ device.getUuids().toString());
                    if(rssi > -90)
                    {
                        Log.e(" pke onLeScan","rssi > 90");
                        PkeRssiQualified++;
                        PkeDevice = device;
                    }
    /*
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            updateItemCount(rssi);
                        }
                    });             */
                }
    
            }
        };
        //update rssi TextView by ZhangKun 2018-11-26
        private void updateItemCount(final int count) {
            /*
            mTvRssiShow.setText(
                    getString(
                            R.string.formatter_db,
                            String.valueOf(count)));
            */
        }
    
        public void ReadPkeStatus()
        {
            //
            pkeUserSettings = getSharedPreferences("PkeSetting", 0);
            PkeMacAddr = pkeUserSettings.getString("MacAddr",null);
            Log.e("ReadPkeStatus", "pkeUserSettings...  addr="+ PkeMacAddr);
            if(PkeMacAddr != null)
            {
                boolean pkeSwitch = pkeUserSettings.getBoolean("pkeSwitch", false);
                Log.e("ReadPkeStatus", "pkeSwitch "+ pkeSwitch);
                if(pkeSwitch == true)
                {
                    //auto open door of car
                    //1. start scan bluetooth
                    mBluetoothAdapter.startLeScan(mLeScanCallback);
                    mScanning = true;
                    //2. check rssi > -90 then stop bluetooth scan,and try connect
                    timerPke.schedule(timerTaskPke,200,1000);//延时1s,每隔500毫秒执行一次run方法
    
                    Log.e("ReadPkeStatus", "Start scan...");
                    boolean pkeUnlock = pkeUserSettings.getBoolean("pkeUnlock", false);
                    boolean pkeLock = pkeUserSettings.getBoolean("pkeLock", false);
                }
            }
        }
    
        public static final String bytesToHexString(byte[] bArray) {
            StringBuffer sb = new StringBuffer(bArray.length);
            String sTemp;
            for (int i = 0; i < bArray.length; i++) {
                sTemp = Integer.toHexString(0xFF & bArray[i]);
                if (sTemp.length() < 2)
                    sb.append(0);
                sb.append(sTemp.toUpperCase());
            }
            return sb.toString();
        }
    
    
        private void enableNotification(boolean enable, BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            if (gatt == null || characteristic == null)
                return; //这一步必须要有 否则收不到通知 gatt.setCharacteristicNotification(characteristic, enable); BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUIDUtils.CCC); if (enable) { clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); } else { clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); } //准备数据 BleWriteData bData = new BleWriteData(); bData.write_type = BleWriteData.DESCRIP_WRITE;//数据种类 bData.object = clientConfig; //将数据加入队列 mWriteQueue.add(bData); }
            //这一步必须要有 否则收不到通知 gatt.setCharacteristicNotification(characteristic, enable); BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUIDUtils.CCC); if (enable) { clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); } else { clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); } //准备数据 BleWriteData bData = new BleWriteData(); bData.write_type = BleWriteData.DESCRIP_WRITE;//数据种类 bData.object = clientConfig; //将数据加入队列 mWriteQueue.add(bData);
            gatt.setCharacteristicNotification(characteristic, enable);
        }
    
        /**
         * 向蓝牙发送数据
         */
        public void dataSend(){
            alertLevel_pke.setValue(getCmd80_new((byte)0x00));
            boolean status = mBluetoothGatt.writeCharacteristic(alertLevel_pke);
            try{
                Thread.sleep(200);
            }catch (Exception e){}
            alertLevel_pke.setValue("xxxxxx");
            mBluetoothGatt.writeCharacteristic(alertLevel_pke);
            Log.e("dataSend", status+"");
        }
    
    
    
        Timer timerPke = new Timer();
        TimerTask timerTaskPke = new TimerTask() {
            @Override
            public void run() {
                Message message = new Message();
                message.what = 1;
                handlerPke.sendMessage(message);
            }
        };
        Handler handlerPke = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == 1){
                    //do something
                    if(mScanning)
                    {
                        //the status is Scanning,check rssi > 90
                        //signal over 3 times
                        if(PkeRssiQualified > 3)
                        {
                            //Stop Scan and Connect PKE
                            mScanning = false;
                            mBluetoothAdapter.stopLeScan(mLeScanCallback);
                            connect(PkeMacAddr, PkeDevice);
                        }
    
                    }else
                    {
                        if(mConnectionState == STATE_CONNECTED)
                        {
                            dataSend();
                        }else
                        {
                            if(!mScanning)
                            {
                                mBluetoothAdapter.startLeScan(mLeScanCallback);     //start scan
                                PkeRssiQualified = 0;
                            }
    
                        }
                    }
    
    
                    Log.e("readCharacteristic", "handleMessage");
                }
                super.handleMessage(msg);
            }
        };
    
    
    }
    

     

    调用部分:

    Intent gattServiceIntentPke = new Intent(this, PkeService.class);
            boolean bll = bindService(gattServiceIntentPke, mServiceConnectionPkeService,
                    BIND_AUTO_CREATE);
            if (bll) {
                System.out.println("---------------");
            } else {
                System.out.println("===============");
            }
    
        private final ServiceConnection mServiceConnectionPkeService = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName componentName,
                                           IBinder service) {
                mPkeService = ((PkeService.LocalBinder) service).getService();
                if (!mPkeService.initialize()) {
                    Log.e(TAG, "Unable to initialize Bluetooth");
                    finish();
                }
                // Automatically connects to the device upon successful start-up
                // initialization.
                //mBluetoothLeService.connect(mDeviceAddress);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                mPkeService.closePke();
                mPkeService = null;
            }
        };

     

     

    设备扫描并将蓝牙Mac地址存储到本地,

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="xxxxxx.BindBluethothDevice2">
    
        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".BindBluetoothDevice">
    
            <LinearLayout
                android:id="@+id/BigLine"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@mipmap/bluebackground"
                android:orientation="vertical"
                app:layout_constraintBottom_toBottomOf="parent">
    
                <LinearLayout
                    android:id="@+id/firstLineLayout"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="top"
                    android:orientation="horizontal">
    
    
                    <ImageView
                        android:id="@+id/imageViewBcak"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:background="@mipmap/title_back" />
    
                    <TextView
                        android:id="@+id/textView45"
                        android:layout_width="wrap_content"
                        android:layout_height="44dp"
                        android:layout_weight="1"
                        android:text="蓝牙硬件设备号"
                        android:textColor="@color/white"
                        tools:layout_width="wrap_content" />
    
                    <TextView
                        android:id="@+id/mtvCommit"
                        android:layout_width="wrap_content"
                        android:layout_height="44dp"
                        android:layout_weight="1"
                        android:text="提交"
                        android:textColor="@color/white" />
    
                </LinearLayout>
    
                <LinearLayout
                    android:id="@+id/secondLine"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:orientation="vertical"
                    android:visibility="visible">
    
                    <LinearLayout
                        android:id="@+id/second2"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:orientation="horizontal"
                        android:visibility="visible">
    
                        <TextView
                            android:id="@+id/textView85"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_weight="1"
                            android:text="蓝牙硬件号"
                            android:textColor="@color/white"
                            android:visibility="visible" />
    
                        <EditText
                            android:id="@+id/editTextUUID"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_weight="1"
                            android:ems="10"
                            android:inputType="textPersonName"
                            android:text="Name"
                            android:textColor="@color/white"
                            tools:text="请手输或扫码输入硬件设备号" />
    
                        <ImageView
                            android:id="@+id/imageViewScan"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_weight="1"
                            app:srcCompat="@android:drawable/btn_star" />
                    </LinearLayout>
    
                </LinearLayout>
    
                <LinearLayout
                    android:id="@+id/infoContainer"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_alignParentBottom="true"
                    android:orientation="vertical">
    
                    <View
                        android:id="@+id/lowersep"
                        android:layout_width="match_parent"
                        android:layout_height="1dp"
                        android:background="#000000" />
    
                    <TextView
                        android:id="@+id/tvItemCount"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="formatter_item_count"
                        android:textColor="@color/color2" />
                </LinearLayout>
    
                <LinearLayout
                    android:id="@+id/listContainer"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_above="@id/infoContainer"
                    android:layout_alignParentTop="true"
                    android:orientation="vertical">
    
                    <ListView
                        android:id="@+id/bluetoothListView"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent" />
                </LinearLayout>
    
            </LinearLayout>
    
        </android.support.constraint.ConstraintLayout>
    </android.support.constraint.ConstraintLayout>
    
    
    import android.bluetooth.BluetoothAdapter;
    import android.bluetooth.BluetoothDevice;
    import android.bluetooth.BluetoothManager;
    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.EditText;
    import android.widget.ListView;
    import android.widget.SimpleAdapter;
    import android.widget.TextView;
    import android.widget.Toast;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    
    public class BindBluethothDevice2 extends AppCompatActivity  implements AdapterView.OnItemClickListener {
        //打开Preferences,名称为setting,如果存在则打开它,否则创建新的Preferences
        public SharedPreferences pkeUserSettings = null;
        @BindView(R.id.tvItemCount)
        protected TextView mTvItemCount;
        @BindView(R.id.mtvCommit)
        public TextView tvCommit;
        @BindView(R.id.editTextUUID)
        public EditText editInput;
        @BindView(R.id.bluetoothListView)
        public ListView mlstView;
        private final static String TAG = BindBluethothDevice2.class.getSimpleName();
        private BluetoothManager mBluetoothManager;
        private BluetoothAdapter mBluetoothAdapter;
        private String mBluetoothDeviceAddress;
        public final static int REQUEST_ENABLE_BT = 2001;
    
        List<HashMap<String,String>> data = new ArrayList<>();
        SimpleAdapter simpleAdapter;
    
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Log.e("onItemClick", data.get(position).get("text1") +"  "+ data.get(position).get("text2"));
            mBluetoothDeviceAddress = data.get(position).get("text2");
            editInput.setText(mBluetoothDeviceAddress);
        }
    
        //  Bluetooth PKE scan device callback function by Zhangkun on 2018-12-3
        private final BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {
            //    Log.e("LeScanCallback-onLeScan", " "+device.toString()+ "rssi:"+rssi);
    
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        updateItemCount(rssi);
    
                        HashMap<String,String> map = new HashMap<>();
                        map.put("text1", device.getName());
                        map.put("text2", device.getAddress());
                        if(data.contains(map))
                        {
                            for(HashMap mhash : data)
                            {
                                if(mhash.equals(map))
                                {
                                    //
                                }
                            }
                        }else
                        {
                            if(device.getName().isEmpty() || device.getAddress().isEmpty())
                            {
                                //do nothing
                            }else
                            {
                                data.add(map);
                            }
                        }
    
                    }
                });
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_bind_bluethoth_device2);
            ButterKnife.bind(this);
            pkeUserSettings = getSharedPreferences("PkeSetting", 0);
            mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
            //判断是否支持蓝牙
            if (mBluetoothAdapter == null) {
                //不支持
                //    Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
                finish();
                return;
            }else
            {
                //打开蓝牙
                if (!mBluetoothAdapter.isEnabled()) {//判断是否已经打开
                    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
                }
            }
            simpleAdapter = new SimpleAdapter(BindBluethothDevice2.this,
                    data,
                    android.R.layout.simple_list_item_2, new String[]{"text1","text2"},
                    new int[]{android.R.id.text1,android.R.id.text2});
            mlstView.setAdapter(simpleAdapter);
            mlstView.setOnItemClickListener(this);
    
        }
        //update rssi TextView by ZhangKun 2018-11-26
        private void updateItemCount(final int count) {
            mTvItemCount.setText(
                    getString(
                            R.string.formatter_db,
                            String.valueOf(count)));
        }
        @Override
        protected void onResume()
        {
            super.onResume();
            mBluetoothAdapter.startLeScan(mLeScanCallback);
            Log.e(TAG, "onResume+ startLeScan");
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    
        @OnClick({R.id.infoContainer,R.id.tvItemCount, R.id.listContainer,
                R.id.imageViewBcak, R.id.mtvCommit})
        public  void OnClick(View v)
        {
            switch (v.getId())
            {
                case R.id.imageViewBcak:
                    this.finish();
                    break;
                case R.id.mtvCommit:
                    SharedPreferences.Editor editor = pkeUserSettings.edit();
                    Log.e("PkeMacAddr",  editInput.getText().toString());
                    editor.putString("MacAddr", editInput.getText().toString());
                    editor.commit();
                    Toast.makeText(getApplicationContext(),"commit...",Toast.LENGTH_SHORT).show();
                    try{Thread.sleep(500);}catch (Exception e){}
                    this.finish();
                    default:
                    break;
            }
        }
    
    }

     

    展开全文
  • Android低功耗蓝牙demo

    2017-06-07 16:28:16
    方便实用的安卓蓝牙源码,Android低功耗蓝牙demo
  • android 低功耗蓝牙

    2017-12-07 18:33:03
    谷歌官方文档: https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le.html 谷歌官方demo: https://github.com/googlesamples/android-BluetoothLeGatt/ 参考: ...

    谷歌官方文档:
    https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le.html
    谷歌官方demo:
    https://github.com/googlesamples/android-BluetoothLeGatt/
    参考:
    http://blog.csdn.net/chenfengdejuanlian/article/details/45787123
    http://blog.csdn.net/z957250254/article/details/52411556

    第一次接触蓝牙方面的知识,仅此记录,大家多多交流啊

    先按照下图走一遍流程
    这里写图片描述

    检查、开启权限

    开启蓝牙权限

        <uses-permission android:name="android.permission.BLUETOOTH" />
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
        <!-- 声明此应用仅适用于具有低功耗蓝牙设备,若想适用于不支持BLE的设备,required设置为false -->
        <uses-feature
            android:name="android.hardware.bluetooth_le"
            android:required="true" />

    检查设备是否支持低功耗蓝牙

    if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, "此设备不支持BLE(低功耗蓝牙)", Toast.LENGTH_LONG).show();
                finish();
            }

    需要注意的是还要开启位置权限
    这里写图片描述
    在manifest添加

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

    并在activity中动态申请权限(本例使用RxPermission请求权限)

        /**
         * 请求定位权限
         */
        private void checkPermission() {
            new RxPermissions((Activity) mContext).request(Manifest.permission.ACCESS_COARSE_LOCATION)
                    .subscribe(new Observer<Boolean>() {
                        @Override
                        public void onSubscribe(Disposable d) {
    
                        }
    
                        @Override
                        public void onNext(Boolean granted) {
                            if (granted) {
                                initBlueTooth();
                            } else {
                                // 未获取权限
                                Toast.makeText(mContext, "未获取定位权限", Toast.LENGTH_LONG).show();
                            }
                        }
    
                        @Override
                        public void onError(Throwable e) {
    
                        }
    
                        @Override
                        public void onComplete() {
    
                        }
                    });
        }

    搜索蓝牙设备

    搜索蓝牙设备是需要一个BluetoothAdapter来扫描设备的,这里又要进行设备是否支持蓝牙功能的判断和是否开启蓝牙(若未开启,提示用户或强制开启蓝牙)

    private void initBlueTooth() {
            //BluetoothAdapter代表设备自己的蓝牙适配器(蓝牙无线电)
            BluetoothManager bluetoothManager =
                    (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
    
            //设备是否支持蓝牙
            if (mBluetoothAdapter == null) {
                Toast.makeText(mContext, "该设备不支持蓝牙功能", Toast.LENGTH_LONG).show();
                finish();
            }
    
            //蓝牙是否启用,若未启用,请求用户启用
            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
                return;
            }
    
            //若开启蓝牙,直接扫描低功耗蓝牙设备;若未开启,提示用户开启,用户开启后在onActivityResult开启扫描
            scanLeDevice(true);
        }

    扫描或关闭扫描

    注意:扫描不能一直进行,本例SCAN_PERIOD=10000毫秒后停止扫描

         /**
         * 扫描低功耗蓝牙设备
         * 您只能扫描蓝牙LE设备或扫描经典蓝牙设备,如蓝牙中所述  您无法同时扫描Bluetooth LE和传统设备。
         *
         * @param enable
         */
        private void scanLeDevice(final boolean enable) {
            if (enable) {
                //Because scanning is battery-intensive, you should observe the following guidelines:
                //As soon as you find the desired device, stop scanning.找到所需设备停止扫描
                //Never scan on a loop, and set a time limit on your scan.切勿扫描循环,并在扫描上加上时间限制
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mScanning = false;
                        mBluetoothAdapter.stopLeScan(mLeScanCallback);
                        mTextView.setText("扫描结束");
                        invalidateOptionsMenu();
                    }
                }, SCAN_PERIOD);
    
                mScanning = true;
                //如果只想扫描特定类型的外设,则可以改为调用startLeScan(UUID [],BluetoothAdapter.LeScanCallback)
                //提供指定您的应用程序支持的GATT服务的UUID对象数组
                mBluetoothAdapter.startLeScan(mLeScanCallback);
                mTextView.setText("扫描中...");
            } else {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
                mTextView.setText("扫描结束");
            }
            invalidateOptionsMenu();
        }

    mBluetoothAdapter.startLeScan()的参数mLeScanCallback是扫描结果的回调

    mLeScanCallback =
                    new BluetoothAdapter.LeScanCallback() {
                        @Override
                        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    if (device != null) {
                                        //过滤同一设备
                                        if (!mDevices.contains(device)) {
                                            //将设备添加到列表中
                                            mDeviceListAdapter.addData(device);
                                        }
                                    }
                                }
                            });
                        }
                    };

    其中mDeviceListAdapter为列表适配器,mDevices为列表适配器中的数据,若不进行同一设备的过滤,会返回很多相同设备

    连接ble设备

    本demo是基于谷歌的示例代码,就直接将谷歌连接设备的相关类拷过来了,这里直接对其分析

    在activity中绑定一个BluetoothLeService服务,此服务是用于对指定蓝牙设备进行连接或数据通信的服务

            Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
            bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

    其中mServiceConnection管理BluetoothLeService服务的生命周期

        /**
         * 管理蓝牙服务的生命周期
         */
        private final ServiceConnection mServiceConnection = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder service) {
                mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
                if (!mBluetoothLeService.initialize()) {
                    Log.e(TAG, "Unable to initialize Bluetooth");
                    finish();
                }
                //成功启动初始化后自动连接到设备
                mBluetoothLeService.connect(mDeviceAddress);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                mBluetoothLeService = null;
            }
        };

    在连接设备之前,要判断设备是否支持低功耗蓝牙

    public boolean initialize() {
            // For API level 18 and above, get a reference to BluetoothAdapter through
            // BluetoothManager.
            if (mBluetoothManager == null) {
                mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
                if (mBluetoothManager == null) {
                    Log.e(TAG, "Unable to initialize BluetoothManager.");
                    return false;
                }
            }
    
            mBluetoothAdapter = mBluetoothManager.getAdapter();
            if (mBluetoothAdapter == null) {
                Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
                return false;
            }
    
            return true;
        }

    BluetoothLeService绑定成功后,将要连接的设备地址传入,在service中连接设备

        /**
         * 连接到Bluetooth LE设备上托管的GATT服务器
         *
         * @param address 设备地址
         * @return 返回是否连接是否成功启动(注意:是启动,并不是连接结果)
         * 连接结果是在BluetoothGattCallback onConnectionStateChange异步返回的
         */
        public boolean connect(final String address) {
            if (mBluetoothAdapter == null || address == null) {
                Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
                return false;
            }
    
            // 以前连接的设备,尝试重连
            if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                    && mBluetoothGatt != null) {
                Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
                if (mBluetoothGatt.connect()) {
                    mConnectionState = STATE_CONNECTING;
                    return true;
                } else {
                    return false;
                }
            }
            //通过传入的设备地址获取设备
            final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
            if (device == null) {
                Log.w(TAG, "Device not found.  Unable to connect.");
                return false;
            }
            // 我们要直接连接到设备,所以我们正在设置autoConnect参数为false (此处autoConnect为true可能不是立即连接)
            mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
            Log.d(TAG, "Trying to create a new connection.");
            mBluetoothDeviceAddress = address;
            mConnectionState = STATE_CONNECTING;
            return true;
        }

    其中device.connectGatt()的第三个参数mGattCallback为蓝牙设备与GATT服务端连接和数据通信的监听,此方法的返回值mBluetoothGatt 稍后介绍

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
            //回调指示何时GATT客户端连接到远程GATT服务器/从远程GATT服务器断开连接
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
                String intentAction;
                //连接成功
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    intentAction = ACTION_GATT_CONNECTED;
                    mConnectionState = STATE_CONNECTED;
                    broadcastUpdate(intentAction);
                    Log.i(TAG, "Connected to GATT server.");
                    // 连接成功后尝试发现服务
                    Log.i(TAG, "Attempting to start service discovery:" +
                            mBluetoothGatt.discoverServices());
    
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    intentAction = ACTION_GATT_DISCONNECTED;
                    mConnectionState = STATE_DISCONNECTED;
                    Log.i(TAG, "Disconnected from GATT server.");
                    broadcastUpdate(intentAction);
                }
            }
    
            //发现新服务时调用回调
            @Override
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
                } else {
                    Log.w(TAG, "onServicesDiscovered received: " + status);
                }
            }
    
            //回调报告特征性读取操作的结果
            @Override
            public void onCharacteristicRead(BluetoothGatt gatt,
                                             BluetoothGattCharacteristic characteristic,
                                             int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                }
            }
    
            //由于远程特征通知而触发的回调
            @Override
            public void onCharacteristicChanged(BluetoothGatt gatt,
                                                BluetoothGattCharacteristic characteristic) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        };

    其中,连接成功、断开或发现服务或数据通信的回调均发送了广播,在activity中接收广播进行管理,这里先不关注每一个方法,尽量从一个流程分析

    看到onConnectionStateChange回调中,判断状态为连接成功,调用了如下代码发现服务

    mBluetoothGatt.discoverServices()

    注意:

    上文中mBluetoothGatt相当于手机与设备通信的管道
    通过BluetoothGatt

    • 发现服务(discoverServices)
    • 获取服务(getServices)
    • 开启指定指定特征的通知(setCharacteristicNotification)
    • 对指定特征写入数据(writeCharacteristic)
    • 把相应地属性返回到BluetoothGattCallback

    demo中发现服务后发送广播到activity中,在activity中接收广播调用service(此service为BluetoothLeService,是自己写的对指定蓝牙设备进行连接或数据通信的服务)的获取服务(此处的服务为**蓝牙设备所拥有的服务**BluetoothGattService)方法getServices,然后在activity中对服务进行遍历,并存储每个服务下的特征值

    此处分析下流程:调用mBluetoothGatt.discoverServices方法后,发现服务后会回调给BluetoothGattCallback的onServicesDiscovered,在调用mBluetoothGatt.getServices()返回一个包含BluetoothGattService服务的集合,再对集合进行遍历,获取每一个BluetoothGattService服务的特征

    注意:(这里有点绕)

    • BluetoothLeService 自己写的对指定蓝牙设备进行连接或数据通信的服务
    • BluetoothGattService 蓝牙设备所拥有的服务
    • BluetoothGattCharacteristic 某一BluetoothGattService的某一特征
    • BluetoothGattDescriptor 某一BluetoothGattCharacteristic下的属性,用来描述characteristic变量的属性

    下面这张图为BluetoothGattService 和 BluetoothGattCharacteristic 和 BluetoothGattDescriptor的关系

    这里写图片描述

    遍历服务(这里尤其绕)使用了ExpandableListView展示服务和服务下的特征

    private void displayGattServices(List<BluetoothGattService> gattServices) {
            if (gattServices == null) return;
            String uuid = null;
            String unknownServiceString = getResources().getString(R.string.unknown_service);
            String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
            ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
            ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                    = new ArrayList<ArrayList<HashMap<String, String>>>();
            mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
    
            //遍历所有的服务
            for (BluetoothGattService gattService : gattServices) {
                //每一个服务的name 和 UUID保存在map中
                HashMap<String, String> currentServiceData = new HashMap<String, String>();
                uuid = gattService.getUuid().toString();
                currentServiceData.put(
                        LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
                currentServiceData.put(LIST_UUID, uuid);
                //将 每一个 包含服务信息的map 都添加到集合中
                gattServiceData.add(currentServiceData);
    
                ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                        new ArrayList<HashMap<String, String>>();
                List<BluetoothGattCharacteristic> gattCharacteristics =
                        gattService.getCharacteristics();
                ArrayList<BluetoothGattCharacteristic> charas =
                        new ArrayList<BluetoothGattCharacteristic>();
    
                //遍历服务中的特征
                for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                    //将此服务中的特征添加到集合中
                    charas.add(gattCharacteristic);
    
                    //将此特征的UUID和name保存在map中
                    HashMap<String, String> currentCharaData = new HashMap<String, String>();
                    uuid = gattCharacteristic.getUuid().toString();
                    currentCharaData.put(
                            LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
                    currentCharaData.put(LIST_UUID, uuid);
    
                    //将 每一个 包含特征信息的map 都添加到集合中
                    gattCharacteristicGroupData.add(currentCharaData);
                }
                //将  每个服务中包含的所有特征的集合  添加到总集合中
                mGattCharacteristics.add(charas);
                //将  每个服务中包含的所有特征信息的集合  添加到总集合中
                gattCharacteristicData.add(gattCharacteristicGroupData);
            }
    
            SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
                    this,
                    gattServiceData,
                    android.R.layout.simple_expandable_list_item_2,
                    new String[]{LIST_NAME, LIST_UUID},
                    new int[]{android.R.id.text1, android.R.id.text2},
                    gattCharacteristicData,
                    android.R.layout.simple_expandable_list_item_2,
                    new String[]{LIST_NAME, LIST_UUID},
                    new int[]{android.R.id.text1, android.R.id.text2}
            );
            mGattServicesList.setAdapter(gattServiceAdapter);
        }

    上面的代码如果不太明白可以慢慢看的,先看下面的
    我们只需要知道,点击ExpandableListView group为此设备下的所有service,点击group会展开此service下的所有特征

    /**
         * ExpandableListView 子条目点击监听 点击条目,会把正在通信的特征关闭通知,再把点击条目的特征打开通知
         */
        private final ExpandableListView.OnChildClickListener servicesListClickListener =
                new ExpandableListView.OnChildClickListener() {
                    @Override
                    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
                                                int childPosition, long id) {
                        if (mGattCharacteristics != null) {
                            final BluetoothGattCharacteristic characteristic =
                                    mGattCharacteristics.get(groupPosition).get(childPosition);
                            /**
                             * 正常流程
                             * 打开  指定接收特征  (指定UUID)通知
                             * 在  指定写入特征  写入数据
                             */
                            //从指定UUID的特征打开接收(测试用)
                            if (characteristic.getUuid().toString().equals("0000ff02-0000-1000-8000-00805f9b34fb")) {
                                mBluetoothLeService.setCharacteristicNotification(
                                        characteristic, true);
    
                            }
    //                        //从指定UUID的特征写入(测试用)
                            if (characteristic.getUuid().toString().equals("0000ff01-0000-1000-8000-00805f9b34fb")) {
                                mBluetoothLeService.write(characteristic);
                            }
                            return true;
                        }
                        return false;
                    }
                };

    本例中
    UUID为0000ff02-0000-1000-8000-00805f9b34fb的特征为读取特征
    UUID为0000ff01-0000-1000-8000-00805f9b34fb的特征为写入特征

    点击0000ff02-0000-1000-8000-00805f9b34fb条目打开读取通知

    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

    点击0000ff01-0000-1000-8000-00805f9b34fb条目写入数据

    //写入数据(测试用)
        public void write(BluetoothGattCharacteristic characteristic) {
            //握手
            characteristic.setValue(hexStringToByteArray("A2"));
            mBluetoothGatt.writeCharacteristic(characteristic);
            //握手和命令不能超过4秒,但又不能马上
            SystemClock.sleep(500);
            //命令
            characteristic.setValue("SINSAM");
            mBluetoothGatt.writeCharacteristic(characteristic);
        }

    数据写入后,就会在mGattCallback的onCharacteristicRead回调里通过characteristic.getValue()拿到数据了
    ok基本总结完了

    源码点我

    最后一张美图镇楼。。(按照自己对低功耗蓝牙的理解画的图)
    这里写图片描述

    展开全文
  • Android低功耗蓝牙透传模块,对谷歌官方的低功耗蓝牙例进行精简适合
  • Android低功耗蓝牙开发官方示例Demo

    千次下载 热门讨论 2015-12-17 09:41:44
    谷歌官方提供的Android平台上的关于低功耗蓝牙开发的示例代码,该示例包含了Android低功耗蓝牙开发的完整过程:(低功耗蓝牙可简称“BLE”) 1、声明蓝牙权限 2、设置BLE 3、扫描BLE 4、连接到GATT服务器(即低...
  • Android低功耗蓝牙数据透传发送和接受demo,通过扫描连接自动 监听接受数据,通过连接获取到UUID
  • android低功耗蓝牙源码

    2014-11-04 09:43:55
    实现手机与低功耗蓝牙设备之间的连接以及通信。
  • Android 低功耗蓝牙开发

    千次阅读 2019-07-04 14:12:26
    Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序通过这些 API 扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等操作...

    初识低功耗蓝牙

    Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的 API, 应用程序通过这些 API 扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等操作。

    Android BLE 使用的蓝牙协议是 GATT 协议,有关该协议的详细内容可以参见蓝牙官方文档。以下我引用一张官网的图来大概说明 Android 开发中我们需要了解的一些 Bluetooth Low Energy 的专业术语。

    Service

    一个低功耗蓝牙设备可以定义许多 Service, Service 可以理解为一个功能的集合。设备中每一个不同的 Service 都有一个 128 bit 的 UUID 作为这个 Service 的独立标志。蓝牙核心规范制定了两种不同的UUID,一种是基本的UUID,一种是代替基本UUID的16位UUID。所有的蓝牙技术联盟定义UUID共用了一个基本的UUID:
    0x0000xxxx-0000-1000-8000-00805F9B34FB
    为了进一步简化基本UUID,每一个蓝牙技术联盟定义的属性有一个唯一的16位UUID,以代替上面的基本UUID的‘x’部分。例如,心率测量特性使用0X2A37作为它的16位UUID,因此它完整的128位UUID为:
    0x00002A37-0000-1000-8000-00805F9B34FB

    Characteristic

    在 Service 下面,又包括了许多的独立数据项,我们把这些独立的数据项称作 Characteristic。同样的,每一个 Characteristic 也有一个唯一的 UUID 作为标识符。在 Android 开发中,建立蓝牙连接后,我们说的通过蓝牙发送数据给外围设备就是往这些 Characteristic 中的 Value 字段写入数据;外围设备发送数据给手机就是监听这些 Charateristic 中的 Value 字段有没有变化,如果发生了变化,手机的 BLE API 就会收到一个监听的回调。

    更详细的内容可以参见
    GATT Profile 简介
    通用属性配置文件(GATT)及其服务,特性与属性介绍
    GATT specification
    GATT Services
    蓝牙【GATT】协议介绍


    Android BLE API 简介

    BluetoothAdapter
    BluetoothAdapter 拥有基本的蓝牙操作,例如开启蓝牙扫描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)实例化一个 BluetoothDevice 用于连接蓝牙设备的操作等等。

    BluetoothDevice
    代表一个远程蓝牙设备。这个类可以让你连接所代表的蓝牙设备或者获取一些有关它的信息,例如它的名字,地址和绑定状态等等。

    BluetoothGatt
    这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。

    BluetoothGattService
    这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。

    BluetoothGattCharacteristic
    这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。


    Android 蓝牙开发示例

    第一步:声明所需要的权限

    <uses-permission android:name="android.permission.BLUETOOTH"/> 使用蓝牙所需要的权限
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 使用扫描和设置蓝牙的权限(申明这一个权限必须申明上面一个权限)
    

    在Android5.0之前,是默认申请GPS硬件功能的。而在Android 5.0 之后,需要在manifest 中申明GPS硬件模块功能的使用。

        <!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
        <uses-feature android:name="android.hardware.location.gps" />
    

    在 Android 6.0 及以上,还需要打开位置权限。如果应用没有位置权限,蓝牙扫描功能不能使用(其它蓝牙操作例如连接蓝牙设备和写入数据不受影响)。

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

    第二步:连接蓝牙前的初始化工作

    在建立蓝牙连接之前,需要确认设备支持 BLE。如果支持,再确认蓝牙是否开启。如果蓝牙没有开启,可以使用 BLuetoothAdapter 类来开启蓝牙。

    1. 获取 BluetoothAdapter
        private BluetoothAdapter mBluetoothAdapter;
            
        // Initializes Bluetooth adapter.
        final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();
    
    1. 如果检测到蓝牙没有开启,尝试开启蓝牙
        // Ensures Bluetooth is available on the device and it is enabled. If not,
        // displays a dialog requesting user permission to enable Bluetooth.
        if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }
    

    第三步:扫描蓝牙设备

    外围设备开启蓝牙后,会广播出许多的关于该设备的数据信息,例如 mac 地址,uuid 等等。通过这些数据我们可以筛选出需要的设备。

    在 BluetoothAdapter 中,我们可以看到有两个扫描蓝牙的方法。第一个方法可以指定只扫描含有特定 UUID Service 的蓝牙设备,第二个方法则是扫描全部蓝牙设备。

    boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
    
    boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
    

    开启蓝牙扫描

    final BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            bluetoothDeviceArrayList.add(device);
            Log.d(TAG, "run: scanning...");
        }
    };
    
    mBluetoothAdapter.startLeScan(callback);
    

    在 LeScanCallback 回调的方法中,第一个参数是代表蓝牙设备的类,可以通过这个类建立蓝牙连接获取关于这一个设备的一系列详细的参数,例如名字,MAC 地址等等;第二个参数是蓝牙的信号强弱指标,通过蓝牙的信号指标,我们可以大概计算出蓝牙设备离手机的距离。计算公式为:d = 10^((abs(RSSI) - A) / (10 * n));第三个参数是蓝牙广播出来的广告数据。
    当执行上面的代码之后,一旦发现蓝牙设备,LeScanCallback 就会被回调,直到 stopLeScan 被调用。出现在回调中的设备会重复出现,所以如果我们需要通过 BluetoothDevice 获取外围设备的地址手动过滤掉已经发现的外围设备。

    停止蓝牙扫描

    void    stopLeScan(BluetoothAdapter.LeScanCallback callback)
    

    通过调用 BluetoothAdapter#stopLeScan 可以停止正在进行的蓝牙扫描。这里需要注意的是,传入的回调必须是开启蓝牙扫描时传入的回调,否则蓝牙扫描不会停止。

    由于蓝牙扫描的操作比较消耗手机的能量。所以我们不能一直开着蓝牙,必须设置一段时间之后关闭蓝牙扫描。示例代码如下:

    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            // 预先定义停止蓝牙扫描的时间(因为蓝牙扫描需要消耗较多的电量)
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);
            mScanning = true;
    
            // 定义一个回调接口供扫描结束处理
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }
    

    第四步:连接蓝牙设备

    连接蓝牙设备可以通过 BluetoothDevice#ConnectGatt 方法连接,也可以通过 BluetoothGatt#connect 方法进行重新连接。以下分别是两个方法的官方说明:

    BluetoothDevice#connectGatt
    BluetoothGatt   connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
    

    第二个参数表示是否需要自动连接。如果设置为 true, 表示如果设备断开了,会不断的尝试自动连接。设置为 false 表示只进行一次连接尝试。
    第三个参数是连接后进行的一系列操作的回调,例如连接和断开连接的回调,发现服务的回调,成功写入数据,成功读取数据的回调等等。

    BluetoothGatt#connect
    boolean connect()
    

    调用这一个方法相当与调用 BluetoothDevice#connectGatt 且第二个参数 autoConnect 设置为 true。

    当调用蓝牙的连接方法之后,蓝牙会异步执行蓝牙连接的操作,如果连接成功会回调 BluetoothGattCalbackl#onConnectionStateChange 方法。这个方法运行的线程是一个 Binder 线程,所以不建议直接在这个线程处理耗时的任务,因为这可能导致蓝牙相关的线程被阻塞。

    void    onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
    

    这一个方法有三个参数,第一个就蓝牙设备的 Gatt 服务连接类。
    第二个参数代表是否成功执行了连接操作,如果为 BluetoothGatt.GATT_SUCCESS 表示成功执行连接操作,第三个参数才有效,否则说明这次连接尝试不成功。有时候,我们会遇到 status == 133 的情况,根据网上大部分人的说法,这是因为 Android 最多支持连接 6 到 7 个左右的蓝牙设备,如果超出了这个数量就无法再连接了。所以当我们断开蓝牙设备的连接时,还必须调用 BluetoothGatt#close 方法释放连接资源。否则,在多次尝试连接蓝牙设备之后很快就会超出这一个限制,导致出现这一个错误再也无法连接蓝牙设备。
    第三个参数代表当前设备的连接状态,如果 newState == BluetoothProfile.STATE_CONNECTED 说明设备已经连接,可以进行下一步的操作了(发现蓝牙服务,也就是 Service)。当蓝牙设备断开连接时,这一个方法也会被回调,其中的 newState == BluetoothProfile.STATE_DISCONNECTED。

    第五步:发现服务

    在成功连接到蓝牙设备之后才能进行这一个步骤,也就是说在 BluetoothGattCalbackl#onConnectionStateChang 方法被成功回调且表示成功连接之后调用 BluetoothGatt#discoverService 这一个方法。当这一个方法被调用之后,系统会异步执行发现服务的过程,直到 BluetoothGattCallback#onServicesDiscovered 被系统回调之后,手机设备和蓝牙设备才算是真正建立了可通信的连接。

    到这一步,我们已经成功和蓝牙设备建立了可通信的连接,接下来就可以执行相应的蓝牙通信操作了,例如写入数据,读取蓝牙设备的数据等等。

    读取数据

    当我们发现服务之后就可以通过 BluetoothGatt#getService 获取 BluetoothGattService,接着通过 BluetoothGattService#getCharactristic 获取 BluetoothGattCharactristic。
    通过 BluetoothGattCharactristic#readCharacteristic 方法可以通知系统去读取特定的数据。如果系统读取到了蓝牙设备发送过来的数据就会调用 BluetoothGattCallback#onCharacteristicRead 方法。通过 BluetoothGattCharacteristic#getValue 可以读取到蓝牙设备的数据。以下是代码示例:

    @Override
    public void onCharacteristicRead(final BluetoothGatt gatt,
                                        final BluetoothGattCharacteristic characteristic,
                                        final int status) {
    
        Log.d(TAG, "callback characteristic read status " + status
                + " in thread " + Thread.currentThread());
        if (status == BluetoothGatt.GATT_SUCCESS) {
            Log.d(TAG, "read value: " + characteristic.getValue());
        }
    
    }
    
    
    // 读取数据
    BluetoothGattService service = gattt.getService(SERVICE_UUID);
    BluetoothGattCharacteristic characteristic = gatt.getCharacteristic(CHARACTER_UUID);
    gatt.readCharacteristic();
    

    写入数据

    和读取数据一样,在执行写入数据前需要获取到 BluetoothGattCharactristic。接着执行一下步骤:

    1. 调用 BluetoothGattCharactristic#setValue 传入需要写入的数据(蓝牙最多单次1支持 20 个字节数据的传输,如果需要传输的数据大于这一个字节则需要分包传输)。
    2. 调用 BluetoothGattCharactristic#writeCharacteristic 方法通知系统异步往设备写入数据。
    3. 系统回调 BluetoothGattCallback#onCharacteristicWrite 方法通知数据已经完成写入。此时,我们需要执行 BluetoothGattCharactristic#getValue 方法检查一下写入的数据是否我们需要发送的数据,如果不是按照项目的需要判断是否需要重发。
      以下是示例代码:
    @Override
    public void onCharacteristicWrite(final BluetoothGatt gatt,
                                        final BluetoothGattCharacteristic characteristic,
                                        final int status) {
        Log.d(TAG, "callback characteristic write in thread " + Thread.currentThread());
        if(!characteristic.getValue().equal(sendValue)) {
            // 执行重发策略
            gatt.writeCharacteristic(characteristic);
        }
    }
    
    //往蓝牙数据通道的写入数据
    BluetoothGattService service = gattt.getService(SERVICE_UUID);
    BluetoothGattCharacteristic characteristic = gatt.getCharacteristic(CHARACTER_UUID);
    characteristic.setValue(sendValue);
    gatt.writeCharacteristic(characteristic);
    

    向蓝牙设备注册监听实现实时读取蓝牙设备的数据

    BLE app通常需要获取设备中characteristic 变化的通知。下面的代码演示了怎么为一个Characteristic 设置一个监听。

    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
            UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
    

    值得注意的是,除了通过 BluetoothGatt#setCharacteristicNotification 开启 Android 端接收通知的开关,还需要往 Characteristic 的 Descriptor 属性写入开启通知的数据开关使得当硬件的数据改变时,主动往手机发送数据。

    最后一步:断开连接

    当我们连接蓝牙设备完成一系列的蓝牙操作之后就可以断开蓝牙设备的连接了。通过 BluetoothGatt#disconnect 可以断开正在连接的蓝牙设备。当这一个方法被调用之后,系统会异步回调 BluetoothGattCallback#onConnectionStateChange 方法。通过这个方法的 newState 参数可以判断是连接成功还是断开成功的回调。
    由于 Android 蓝牙连接设备的资源有限,当我们执行断开蓝牙操作之后必须执行 BluetoothGatt#close 方法释放资源。需要注意的是通过 BluetoothGatt#close 方法也可以执行断开蓝牙的操作,不过 BluetoothGattCallback#onConnectionStateChange 将不会收到任何回调。此时如果执行 BluetoothGatt#connect 方法会得到一个蓝牙 API 的空指针异常。所以,我们推荐的写法是当蓝牙成功连接之后,通过 BluetoothGatt#disconnect 断开蓝牙的连接,紧接着在 BluetoothGattCallback#onConnectionStateChange 执行 BluetoothGatt#close 方法释放资源。
    以下是代码示例:

        @Override
        public void onConnectionStateChange(final BluetoothGatt gatt, final int status,
                                        final int newState) {
            Log.d(TAG, "onConnectionStateChange: thread "
                    + Thread.currentThread() + " status " + newState);
    
            if (status != BluetoothGatt.GATT_SUCCESS) {
                String err = "Cannot connect device with error status: " + status;
          // 当尝试连接失败的时候调用 disconnect 方法是不会引起这个方法回调的,所以这里
                    //   直接回调就可以了。
                gatt.close();
                Log.e(TAG, err);
                return;
            }
    
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                gatt.discoverService();
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                gatt.close();
            }
        }
    

    蓝牙操作的注意事项

    1. 蓝牙的写入操作( 包括 Descriptor 的写入操作), 读取操作必须序列化进行. 写入数据和读取数据是不能同时进行的, 如果调用了写入数据的方法, 马上调用又调用写入数据或者读取数据的方法,第二次调用的方法会立即返回 false, 代表当前无法进行操作. 详情可以参考 蓝牙读写操作返回 false,为什么多次读写只有一次回调?
    2. Android 连接外围设备的数量有限,当不需要连接蓝牙设备的时候,必须调用 BluetoothGatt#close 方法释放资源。详细的参考可以看这里 Android BLE 蓝牙开发的各种坑
    3. 蓝牙 API 连接蓝牙设备的超时时间大概在 20s 左右,具体时间看系统实现。有时候某些设备进行蓝牙连接的时间会很长,大概十多秒。如果自己手动设置了连接超时时间(例如通过 Handler#postDelay 设置了 5s 后没有进入 BluetoothGattCallback#onConnectionStateChange 就执行 BluetoothGatt#close 操作强制释放断开连接释放资源)在某些设备上可能会导致接下来几次的连接尝试都会在 BluetoothGattCallback#onConnectionStateChange 返回 state == 133。另外可以参考这篇吐槽 Android 中 BLE 连接出现“BluetoothGatt status 133”的解决方法
    4. 所有的蓝牙操作使用 Handler 固定在一条线程操作,这样能省去很多因为线程不同步导致的麻烦

     

    近段项目涉及蓝牙开发,这篇是逻辑相对比较清晰和全面的博文,感谢作者

    原文地址:https://www.jianshu.com/p/3a372af38103

    展开全文
  • 1.android 手机的低功耗蓝牙,又称BLE ;BLE在andriod 4.3 以上才支持,又称蓝牙4.0,区别于经典蓝牙,BLE 低功耗,手机是否支持低功耗蓝牙,主要取决于手机硬件,所以使用前,需要先进行判断,是否支持低功耗蓝牙 ...

    参考链接:http://blog.csdn.net/xubin341719/article/details/38584469

    1.android 手机的低功耗蓝牙,又称BLE ;BLE在andriod 4.3 以上才支持,又称蓝牙4.0,区别于经典蓝牙,BLE 低功耗,手机是否支持低功耗蓝牙,主要取决于手机硬件,所以使用前,需要先进行判断,是否支持低功耗蓝牙

    2.蓝牙的使用,

      1.判断mobile 是否有低功耗蓝牙,返回值boolean

    mainActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
    

      2.获取bluetooth的adapter,adapter为null,证明设备无蓝牙

     mDefaultAdapter = BluetoothAdapter.getDefaultAdapter();
    

      3.设备是否已经开启蓝牙

    mDefaultAdapter.isEnabled()
    

      4.打开蓝牙,开启蓝牙有两种方式,第一种,通过intent开启,会给用户以提示。

    Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
     mainActivity.startActivityForResult(intent, OPEN_BLUETOOTH_REQUEST_CODE);
    
    //在activity中获取开启蓝牙的反馈
    protected void onActivityResult (int requestCode, int resultCode, Intent data) {
           switch (requestCode){
               case OpenBluetoothPresenter.OPEN_BLUETOOTH_REQUEST_CODE:
                   if(requestCode==RESULT_OK){ //result_ok 是activity中的常量,为-1,说明操作成功了
                       mOpenBluetoothPresenter.scanDevice();
                   }else{
                       ToastUtils.showMessage(this,getString(R.string.connect_device_need_open_bluetooth));
                   }
    
                   break;
           }
    
        }
    

      

      第二种方式,这种方式会直接开启蓝牙,但是不能监听到用户是否真的开启了蓝牙;但是可以通过广播监听蓝牙状态的变化,建议非系统应用,采用第一种开启方式

     mDefaultAdapter.enable();
    

      5.扫描设备,因为我所使用的低功耗设备,而且只需要扫描低功耗设备,我采用的是startLeScan(),改方法过时了,建议采用bluetoothAdapterScanner.startScan,但是这个函数,要求sdk>23,而我需要支持的18,所以仍采用startLeScan

     mDefaultAdapter.startLeScan(this);
    

      6.扫描的结果,回调OnLEScan里面,改方法是实现接口:BluetoothAdapter.LeScanCallback

    @Override
        public void onLeScan (BluetoothDevice device, int rssi, byte[] scanRecord) {
            boolean isDeviceFinded = false;
            String deviceName = device.getName();
            if (deviceName != null && deviceName.length() > 0 /*&& deviceName.startsWith("M")*/) {
                if(devices==null){
                    devices = new ArrayList<>();
                    devices.add(device);
                }else{
                    for (BluetoothDevice bluetoothDevice : devices) {
                        if(bluetoothDevice.getName().equals(deviceName)){
                            isDeviceFinded = true;
                            break;
                        }
                    }
    
                    if(!isDeviceFinded){
                        devices.add(device);
                    }
    
                    openBluetoothView.notifyDeviceList(devices);
                }
            }
        }
    

      7,停止搜索

     mDefaultAdapter.stopLeScan(OpenBluetoothPresenter.this);
    

      8.连接设备,连接设备是通过bluetoothdevice的address进行连接,但是连接过程中,还是要进行一系列的判断,进行优化

    if (mDefaultAdapter == null || address == null) {
                ToastUtils.showMessage(mainActivity,mainActivity.getString(R.string.no_connect_device));
                return;
            }
    
            BluetoothDevice remoteDevice = mDefaultAdapter.getRemoteDevice(address);
            if (remoteDevice == null) {
                ToastUtils.showMessage(mainActivity,mainActivity.getString(R.string.connect_fail_no_device));
                return;
            }
    
            remoteDevice.connectGatt(mainActivity,false,this);
    

      9.连接的结果,会返回到BluetoothGattCallback里面,BluetoothGAttCallback是一个抽象类,里面的函数意义,:

    /**
         * 回调指示,
         *
         * @param gatt     gatt客户端
         * @param status   连接或者断开操作的状态,返回是BlutoothGatt 操作是否成功
         * @param newState 新连接的状态,断开,或者连接bluetprofile state_connect,state_disconnect
         */
        @Override
        public void onConnectionStateChange (BluetoothGatt gatt, int status, int newState) {
            Log.e(TAG, "onConnectionStateChange: status" + status + " status" + BluetoothGatt.GATT_SUCCESS + newState + ">>" + BluetoothProfile.STATE_CONNECTED);
        }
    
        /**
         * 连接设备的services,characteristrics,desriptors 更新的时候调用
         *
         * @param gatt   gatt客户端
         * @param status 连接设备已经被探索成功,是BluetoothGatt.Gatt_successfuly
         */
        @Override
        public void onServicesDiscovered (BluetoothGatt gatt, int status) {
            Log.e(TAG, "onServicesDiscovered: " + status + " sss" + BluetoothGatt.GATT_SUCCESS);
        }
    
        /**
         * 读到特征值得结果
         *
         * @param gatt           客户端
         * @param characteristic 特征值
         * @param status         读的操作完成后,status 是BluetoothGatt.Gatt_Success
         */
        @Override
        public void onCharacteristicRead (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            Log.e(TAG, "onCharacteristicRead: " + status);
        }
    
        /**
         * 设备的特征值变化的时候,该函数被触发
         *
         * @param gatt           客户端
         * @param characteristic 变换后的特征值
         */
        @Override
        public void onCharacteristicChanged (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            Log.e(TAG, "onCharacteristicChanged: 调用了");
        }
    /**
    * 在调用mBluetoothGatt.writeCharacteristic()函数之后,接收该函数操作的结果
    * @param gatt 客户端
    * @param characteristic 特征值,这个特征值是从远程设备返回的之前写入的值,应用程序应该进行对比,如果不正确,进行操作
    * @param status 如果写入成功,这个返回的BluetoothGatt 的Gatt_success
    */
    @Override
    public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
    }

      10.remoteDevice connectGatt()会返回一个GAtt对象,gatt connect方法会重新连接一个已经连接过的连接

     BluetoothGatt mBluetoothGatt = remoteDevice.connectGatt(mainActivity, false, this);
            //如果与远程设备的连接断开,调用该函数会重新进行连接,如果设备不在连接范围内,
            //会等设备出现在连接范围内后,调用一次,进行连接
            mBluetoothGatt.connect();
    

      11.向蓝牙发送指令,通过的是串口服务

     //当write操作完成后,会回调BluetoothGattCallback的onCharacteristicWrite函数,回报操作的结果,
                //返回true,写入成功
                boolean b = mBluetoothGatt.writeCharacteristic(rxServiceChar);
    

      12.开启Characteritics特征

     //开启或者关闭指定的特征值,Boolean 参数即为开启或关闭操作,该函数返回值,是操作是否成功
            //返回的结果会在BluetoothGattCallback里面的onCharacteriticsChang调用
            mBluetoothGatt.setCharacteristicNotification(characteristic,true);
    

      13.获取远程设备的所有服务

    mBluetoothGatt.getServices();
    

      14.断开连接

    public void disconnect(){
            if(mDefaultAdapter==null||mBluetoothGatt==null){
                Log.d(TAG, "disconnect: mDefaultAdapter或者 mBluetoothGatt为null,无法关闭");
                return;
            }
    
            mBluetoothGatt.disconnect();
        }
    

      15.清除对象

     public void close(){
            if(mBluetoothGatt == null){
                Log.d(TAG, "close: mBluetoothGatt 位null,已经close");
                return;
            }
    
            mOldBluetoothAddress = null;
            mBluetoothGatt.close();
            mBluetoothGatt = null;
        }
    

      

    转载于:https://www.cnblogs.com/hechangshou/p/8085766.html

    展开全文
  • 与普通蓝牙相比,低功耗蓝牙显著降低了能量消耗,允许Android应用程序与具有更严格电源要求的BLE设备进行通信,如接近传感器、心率传感器等低功耗设备。 声明蓝牙权限 6.0以上 <uses-permission android:...
  • Android Bluetooth Low Energy(Android低功耗蓝牙) Android 4.3(API Level 18)开始引入Bluetooth Low Energy(BLE,低功耗蓝牙)的核心功能并提供了相应的API,应用程序通过这些api可以扫描设备、查询services...
  • Android低功耗蓝牙(BLE)开发的一点感受(转) 原文:https://www.cnblogs.com/dongweiq/p/5756486.html 最近一段时间,因为产品的需要我做了一个基于低功耗蓝牙设备的Android应用,其中碰到了一些困难,使我深深体会...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,299
精华内容 519
关键字:

android低功耗蓝牙