美文网首页
android-标准蓝牙开发与蓝牙低功耗开发

android-标准蓝牙开发与蓝牙低功耗开发

作者: MrWu_ | 来源:发表于2017-05-03 20:35 被阅读641次

    做蓝牙开发,先认识几个蓝牙的几个要点:

    1、UUID:通用唯一识别码(英语:Universally Unique Identifier,简称UUID)

    2、GATT:通用属性描述Generic Attribute Profile-GATT是为通过BLE链接发送和接收的短数据(属性值)而做的一个规范描述。* 属性协议Attribute Protocol-GATT是建立在ATT之上的。它们一起指的是GATT/ATT(类似于TCP/IP的概念)。* ATT对在BLE设备上的运行进行优化。为此,它使用尽可能少的字节。每个属性都是由一个全局唯一标识符唯一地标识(UUID),这是一个字符串ID,* 一个标准的128位格式,用于唯一标识信息。这里的attributes(属性)的传输被ATT格式化为characteristics和services。* 特征[Characteristic]-一个characteristic包含一个唯一的值和0到n个描述数来描述characteristic的值。一个characteristic可以被看作一个数据类型,或者一个类。* 描述符[Descriptor]-Descriptor被定义为一些属性用来描述一个characteristic的值。例如,一个描述符可能特指为一个只有人才可以读的描述,* characteristic的值的一个可接受的范围。或者是characteristic的一个测量单元

    标准蓝牙开发流程

    1、开启权限
    2、开启蓝牙
    3、蓝牙搜寻广播
    4、连接与配对

    简要介绍Bluetooth开发要使用的类

    1、BluetoothAdapter 蓝牙适配器,可判断蓝牙设备是否可用等功能
    有几个重要的方法:
    cancelDiscovery() ,取消搜索过程,在进行蓝牙设备搜索时,如果调用该方法会停止搜索。(搜索过程会持续12秒)
    disable()关闭蓝牙,也就是我们常说的禁用蓝牙。
    enable()打开蓝牙,这个方法打开蓝牙但不会弹出提示,正常流程操作下,我们会让系统提示用户是否打开蓝牙设备。如下两行代码可轻松搞定。

    2.BluetoothDevice看名字就知道,这个类描述了一个蓝牙设备createRfcommSocketToServiceRecord(UUIDuuid)根据UUID创建并返回一个BluetoothSocket* 这个方法也是我们获取BluetoothDevice的目的——创建BluetoothSocket* 这个类其他的方法,如getAddress(),getName(),同BluetoothAdapter* 这个类有几个隐藏方法,涉及到蓝牙的自动配对,setPin,createBond,cancelPairingUserInput,等方法(需要通过java的反射,调用这几个隐藏方法)

    下面通过案例说明:

    public class ClientThread extends Thread {
    
        private BluetoothSocket socket;
        private BluetoothDevice device;
        private Context context;
        public ClientThread(BluetoothDevice device, Context context) {
            this.socket = socket;
            this.context= context;
        }
    
        @Override
        public void run() {
            try {
                socket=device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
                socket.connect();
    
                if(socket==null){
                    Toast.makeText(context, "本设备没有蓝牙模块", Toast.LENGTH_SHORT).show();
                    return;
                }
                System.out.println("hasnheng client");
                while (true){
                    System.out.println("hansheng client is in");
                    String msg = "hello everybody I am client";
                    OutputStream os = socket.getOutputStream();
                    os.write(msg.getBytes());
    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    }
    
    public class ServerThread extends Thread {
    
    
        public BluetoothServerSocket serverSocket;
        private BluetoothAdapter bluetoothAdapter;
        private BluetoothSocket socket;
        private Context context;
        public ServerThread(BluetoothAdapter bluetoothAdapter, Context context) {
            this.bluetoothAdapter = bluetoothAdapter;
            this.context = context;
        }
    
        @Override
        public void run() {
            try {
                serverSocket=bluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(Bluetoothprotocol.PROTOCOL_SCHEME_RFCOMM,
                        UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
                socket=serverSocket.accept();
                byte[] buffer=new byte[1024];
                int bytes=1;
    
                InputStream inputStream=null;
                inputStream=socket.getInputStream();
    
                while (true){
                    if((bytes=inputStream.read(buffer))>0){
                        byte[] buf=new byte[bytes];
                        for(int i=0;i<bytes;i++){
                            buf[i]=buffer[i];
                        }
                        String s = new String(buf);
                        System.out.println(s+"hansheng server is in");
                    }
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    public class BluetoothActivity1 extends AppCompatActivity {
    
        public Button searchBtn;//搜索蓝牙设备
        public Button exitBtn;//退出应用
        public Button discoverBtn;//设置可被发现
        public ToggleButton openBtn;//开关蓝牙设备
        public Button serverbtn;
        public ListView listView;//蓝牙设备清单
        public ArrayAdapter<String> adapter;
        public ArrayList<String> list = new ArrayList<String>();
        private BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        Set<BluetoothDevice> bondDevices;
        public Context context;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.blue_layoutmain);
            searchBtn = (Button) findViewById(R.id.btnSearch);
            exitBtn = (Button) findViewById(R.id.btnExit);
            discoverBtn = (Button) findViewById(R.id.btnDis);
            openBtn = (ToggleButton) findViewById(R.id.tbtnSwitch);
            serverbtn = (Button) findViewById(R.id.btnserver);
            listView = (ListView) findViewById(R.id.lvDevices);
            context = getApplicationContext();
            adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
            listView.setAdapter(adapter);
            openBtn.setChecked(false);
            //注册广播接收信号
            IntentFilter intent = new IntentFilter();
            intent.addAction(BluetoothDevice.ACTION_FOUND);// 用BroadcastReceiver来取得搜索结果
            intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); //每当扫描模式变化的时候,应用程序可以为通过ACTION_SCAN_MODE_CHANGED值来监听全局的消息通知。比如,当设备停止被搜寻以后,该消息可以被系统通知給应用程序。
            intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); //每当蓝牙模块被打开或者关闭,应用程序可以为通过ACTION_STATE_CHANGED值来监听全局的消息通知。
            registerReceiver(searchReceiver, intent);
    
            searchBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (bluetoothAdapter.isDiscovering()) {
                        bluetoothAdapter.cancelDiscovery();
                    }
                    list.clear();
                    bondDevices = bluetoothAdapter.getBondedDevices();
    
                    for (BluetoothDevice device : bondDevices) {
                        String str = "  已配对完成   " + device.getName() + "    " + device.getAddress();
                        list.add(str);
                        adapter.notifyDataSetChanged();
                    }
                    bluetoothAdapter.startDiscovery();
                }
            });
            //退出应用
            exitBtn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    BluetoothActivity1.this.finish();
                }
            });
            //设置蓝牙设备可发现
            discoverBtn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    Intent discoverIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                    discoverIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                    startActivity(discoverIntent);
                }
            });
            //开关蓝牙设备
            openBtn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    if(openBtn.isChecked() == true){
                        bluetoothAdapter.disable();
                    }
                    else if(openBtn.isChecked() == false){
                        bluetoothAdapter.enable();
                    }
                }
            });
            //作为服务端开启
            serverbtn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    ServerThread serverThread = new ServerThread(bluetoothAdapter, context);
                    Toast.makeText(context, "server 端启动", Toast.LENGTH_SHORT).show();
                    serverThread.start();
                }
            });
            listView.setOnItemClickListener(new ItemClickListener());
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (searchReceiver != null) {
                unregisterReceiver(searchReceiver);
            }
    
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            // If BT is not on, request that it be enabled.
            if (bluetoothAdapter == null) {
                Toast.makeText(context, "蓝牙设备不可用", Toast.LENGTH_SHORT).show();
            }
            if (!bluetoothAdapter.isEnabled()) {
                Intent enableIntent = new Intent(
                        BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableIntent, 3);
            }
        }
    
        private final BroadcastReceiver searchReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String action = intent.getAction();
                BluetoothDevice device = null;
                if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                    device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                    if (device.getBondState() == BluetoothDevice.BOND_BONDED) {
                        Toast.makeText(context, device.getName() + "", Toast.LENGTH_SHORT).show();
                        String str = "  未配对完成   " + device.getName() + "    " + device.getAddress();
                        if (list.indexOf(str) == -1)// 防止重复添加
                            list.add(str);
                    }
                    adapter.notifyDataSetChanged();
                }
            }
        };
    
        private class ItemClickListener implements AdapterView.OnItemClickListener {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                if(bluetoothAdapter.isDiscovering())
                    bluetoothAdapter.cancelDiscovery();
                String str = list.get(position);
                String address = str.substring(str.length() - 17);
                BluetoothDevice btDev = bluetoothAdapter.getRemoteDevice(address);
                ClientThread clientThread = new ClientThread(btDev, context);
                clientThread.start();
            }
        }
    }
    
    

    蓝牙低功耗开发

    区别:

    标准蓝牙的的开发和BLE不同。标准蓝牙连接里有两个角色一个是客户端一个是服务器,当客户端搜索到蓝牙服务器后并与之配对后, 才能通过UUID(这个是唯一的,服务器端必须与客户端一致)建立socket,然后使用流像文件读写和网络通信那样传输数据就行了。

    ** 在BLE里,变成了中心设备(central)和外围设备(peripheral),中心设备就是你的手机,外围设备就是智能手环一类的东西。开发BLE的应用都得遵守Generic Attribute Profile (GATT),一个BLE蓝牙设备包含多个service,每个service 又包含多个characteristic。每个characteristic有一个value和多个descriptor,通过characteristic中心设备与外围设备进行通信。** descriptor顾名思义,包含了BLE设备的一些信息。不同service、characteristic和descriptor都有各自己唯一的UUID。想要跟BLE设备通信, 首先通过UUID获取目标服务,然后再通过UUID获取characteristic,charateristic起着载体的作用,通过writeCharacteristic()和readCharacteristic(),可以写入和读出信息。*每个characteristic都有一些自己的属性,其中在property里,说明了该characteristic的属性 ,例如READ|WRITE|WRITE_NO_RESPONSE|NOTIFY。

    当连接上BLE设备后,调用discoveryServices()发现服务,通过SERVICE_UUID获取目标service,如果service不为空,* 再通过CHARACTERISTIC_UUID获取characteristic,借助characteristic写入指定值与BLE设备进行通信。

    这里要注意的是characteristic接收的是一个byte数组, 而且读写的方法都是异步的。调用bluetoothGatt.readCharacteristic(characteristic)可读取BLE设备返回的值。bluetoothGatt的writeCharacteristic()方法会触发BluetoothGattCallback里的onCharacteristicWrite(),相应的,bluetoothGatt的readCharacteristic()方法会触发onCharacteristicRead()。对中心设备与外围设备的传输数据的处理发生在onCharacteristicChanged()里,当characteristic写入了正确的数值后,会激活BLE设备,不时地返回数据,当不知道BLE设备的service和characteristic的对应的UUID时,可以在回调函数里的onServicesDiscovered()方法里, 通过BluetoothGatt的getServices()获得该设备所有的service,然后再调用service的getUuid()得到其UUID,同理通过service的getCharacteristics()可以得到每个service所有的characteristic和其UUID。然后一个个不厌其烦的尝试,找到正确的service和characteristic 接收数据:

    现在,本地的代理组件知道了传感器所提供的服务,我们可以开始使用这些服务了。为了使用它们,我们首先需要获得服务,然后是该服务所包含的特征,最后是特征的描述符。

    一个GATT服务表现为一个BluetoothGattService 对象,我们需要通过适当的UUID从 BluetoothGatt 实例中获得;
    一个GATT特征表示为一个 BluetoothGattCharacteristic 对象,我们可以通过适当的UUID从BluetoothGattService 中得到;
    一个GATT描述符表现为一个 BluetoothGattDescriptor 对象,我们可以通过适当的UUID从BluetoothGattCharacteristic对象中获得:

    蓝牙低功耗开发步骤:
    1、使用BluetoothAdapter.startLeScan来扫描低功耗蓝牙设备
    2、在扫描到设备的回调函数中会得到BluetoothDevice对象,并使用BluetoothAdapter.stopLeScan停止扫描
    3、使用BluetoothDevice.connectGatt来获取到BluetoothGatt对象
    4、执行BluetoothGatt.discoverServices,这个方法是异步操作,在回调函数onServicesDiscovered中得到status, 通过判断status是否等于BluetoothGatt.GATT_SUCCESS来判断查找Service是否成功
    5、如果成功了,则通过BluetoothGatt.getService来获取BluetoothGattService
    6、接着通过BluetoothGattService.getCharacteristic获取BluetoothGattCharacteristic
    7、然后通过BluetoothGattCharacteristic.getDescriptor获取BluetoothGattDescriptor

    通过案例:

    public class DeviceScanActivity extends ListActivity {
        private LeDeviceListAdapter mLeDeviceListAdapter;
        private BluetoothAdapter mBluetoothAdapter;
        private boolean mScanning;
        private Handler mHandler;
    
        private static final int REQUEST_ENABLE_BT = 1;
        // Stops scanning after 10 seconds.
        private static final long SCAN_PERIOD = 10000;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            getActionBar().setTitle(R.string.title_devices);
            mHandler = new Handler();
    
            // Use this check to determine whether BLE is supported on the device.  Then you can
            // selectively disable BLE-related features.
            if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
                finish();
            }
    
            // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
            // BluetoothAdapter through BluetoothManager.
            final BluetoothManager bluetoothManager =
                    (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            mBluetoothAdapter = bluetoothManager.getAdapter();
    
            // Checks if Bluetooth is supported on the device.
            if (mBluetoothAdapter == null) {
                Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
                finish();
                return;
            }
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.main, menu);
            if (!mScanning) {
                menu.findItem(R.id.menu_stop).setVisible(false);
                menu.findItem(R.id.menu_scan).setVisible(true);
                menu.findItem(R.id.menu_refresh).setActionView(null);
            } else {
                menu.findItem(R.id.menu_stop).setVisible(true);
                menu.findItem(R.id.menu_scan).setVisible(false);
                menu.findItem(R.id.menu_refresh).setActionView(
                        R.layout.actionbar_indeterminate_progress);
            }
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch (item.getItemId()) {
                case R.id.menu_scan:
                    mLeDeviceListAdapter.clear();
                    scanLeDevice(true);
                    break;
                case R.id.menu_stop:
                    scanLeDevice(false);
                    break;
            }
            return true;
        }
    
        @Override
        protected void onResume() {
            super.onResume();
    
            // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
            // fire an intent to display a dialog asking the user to grant permission to enable it.
            if (!mBluetoothAdapter.isEnabled()) {
                if (!mBluetoothAdapter.isEnabled()) {
                    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
                }
            }
    
            // Initializes list view adapter.
            mLeDeviceListAdapter = new LeDeviceListAdapter();
            setListAdapter(mLeDeviceListAdapter);
            scanLeDevice(true);
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            // User chose not to enable Bluetooth.
            if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
                finish();
                return;
            }
            super.onActivityResult(requestCode, resultCode, data);
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            scanLeDevice(false);
            mLeDeviceListAdapter.clear();
        }
    
        @Override
        protected void onListItemClick(ListView l, View v, int position, long id) {
            final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
            if (device == null) return;
            final Intent intent = new Intent(this, DeviceControlActivity.class);
            intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
            intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
            if (mScanning) {
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
                mScanning = false;
            }
            startActivity(intent);
        }
    
        //    如果只是要扫描到特定类型的设备,则使用接口 startLeScan(UUID[], BluetoothAdapter.LeScanCallback),通过UUID来查找设备。
        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);
                        invalidateOptionsMenu();
                    }
                }, SCAN_PERIOD);
    
                mScanning = true;
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
            invalidateOptionsMenu();
        }
    
        // Adapter for holding devices found through scanning.
        private class LeDeviceListAdapter extends BaseAdapter {
            private ArrayList<BluetoothDevice> mLeDevices;
            private LayoutInflater mInflator;
    
            public LeDeviceListAdapter() {
                super();
                mLeDevices = new ArrayList<BluetoothDevice>();
                mInflator = DeviceScanActivity.this.getLayoutInflater();
            }
    
            public void addDevice(BluetoothDevice device) {
                if (!mLeDevices.contains(device)) {
                    mLeDevices.add(device);
                }
            }
    
            public BluetoothDevice getDevice(int position) {
                return mLeDevices.get(position);
            }
    
            public void clear() {
                mLeDevices.clear();
            }
    
            @Override
            public int getCount() {
                return mLeDevices.size();
            }
    
            @Override
            public Object getItem(int i) {
                return mLeDevices.get(i);
            }
    
            @Override
            public long getItemId(int i) {
                return i;
            }
    
            @Override
            public View getView(int i, View view, ViewGroup viewGroup) {
                ViewHolder viewHolder;
                // General ListView optimization code.
                if (view == null) {
                    view = mInflator.inflate(R.layout.listitem_device, null);
                    viewHolder = new ViewHolder();
                    viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
                    viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
                    view.setTag(viewHolder);
                } else {
                    viewHolder = (ViewHolder) view.getTag();
                }
    
                BluetoothDevice device = mLeDevices.get(i);
                final String deviceName = device.getName();
                if (deviceName != null && deviceName.length() > 0)
                    viewHolder.deviceName.setText(deviceName);
                else
                    viewHolder.deviceName.setText(R.string.unknown_device);
                viewHolder.deviceAddress.setText(device.getAddress());
    
                return view;
            }
        }
    
        // Device scan callback.
        private BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
    
                    @Override
                    public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                mLeDeviceListAdapter.addDevice(device);
                                mLeDeviceListAdapter.notifyDataSetChanged();
                            }
                        });
                    }
                };
    
        static class ViewHolder {
            TextView deviceName;
            TextView deviceAddress;
        }
    
    }
    public class DeviceControlActivity extends AppCompatActivity {
    
    
        private final static String TAG = DeviceControlActivity.class.getSimpleName();
    
        public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
        public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";
    
        private TextView mConnectionState;
        private TextView mDataField;
        private String mDeviceName;
        private String mDeviceAddress;
        private ExpandableListView mGattServicesList;
        private BluetoothLeService mBluetoothLeService;
        private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics =
                new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
        private boolean mConnected = false;
        private BluetoothGattCharacteristic mNotifyCharacteristic;
    
        private final String LIST_NAME = "NAME";
        private final String LIST_UUID = "UUID";
    
        // Code to manage Service lifecycle.
        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();
                }
                // Automatically connects to the device upon successful start-up initialization.
                mBluetoothLeService.connect(mDeviceAddress);
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                mBluetoothLeService = null;
            }
        };
    
        // Handles various events fired by the Service.
        // ACTION_GATT_CONNECTED: connected to a GATT server.
        // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
        // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
        // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
        //                        or notification operations.
        private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final String action = intent.getAction();
                if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                    mConnected = true;
                    updateConnectionState(R.string.connected);
                    invalidateOptionsMenu();
                } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                    mConnected = false;
                    updateConnectionState(R.string.disconnected);
                    invalidateOptionsMenu();
                    clearUI();
                } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                    // Show all the supported services and characteristics on the user interface.
                    displayGattServices(mBluetoothLeService.getSupportedGattServices());
                } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                    displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
                }
            }
        };
    
        // If a given GATT characteristic is selected, check for supported features.  This sample
        // demonstrates 'Read' and 'Notify' features.  See
        // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
        // list of supported characteristic features.
        private final ExpandableListView.OnChildClickListener servicesListClickListner =
                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);
                            final int charaProp = characteristic.getProperties();
                            if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
                                // If there is an active notification on a characteristic, clear
                                // it first so it doesn't update the data field on the user interface.
                                if (mNotifyCharacteristic != null) {
                                    mBluetoothLeService.setCharacteristicNotification(
                                            mNotifyCharacteristic, false);
                                    mNotifyCharacteristic = null;
                                }
                                mBluetoothLeService.readCharacteristic(characteristic);
                            }
                            if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
                                mNotifyCharacteristic = characteristic;
                                mBluetoothLeService.setCharacteristicNotification(
                                        characteristic, true);
                            }
                            return true;
                        }
                        return false;
                    }
                };
    
        private void clearUI() {
            mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
            mDataField.setText(R.string.no_data);
        }
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.gatt_services_characteristics);
    
            final Intent intent = getIntent();
            mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
            mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);
    
            // Sets up UI references.
            ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
            mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
            mGattServicesList.setOnChildClickListener(servicesListClickListner);
            mConnectionState = (TextView) findViewById(R.id.connection_state);
            mDataField = (TextView) findViewById(R.id.data_value);
    
    //        getActionBar().setTitle(mDeviceName);
    //        getActionBar().setDisplayHomeAsUpEnabled(true);
            Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
            bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
            if (mBluetoothLeService != null) {
                final boolean result = mBluetoothLeService.connect(mDeviceAddress);
                Log.d(TAG, "Connect request result=" + result);
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            unregisterReceiver(mGattUpdateReceiver);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(mServiceConnection);
            mBluetoothLeService = null;
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.gatt_services, menu);
            if (mConnected) {
                menu.findItem(R.id.menu_connect).setVisible(false);
                menu.findItem(R.id.menu_disconnect).setVisible(true);
            } else {
                menu.findItem(R.id.menu_connect).setVisible(true);
                menu.findItem(R.id.menu_disconnect).setVisible(false);
            }
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            switch(item.getItemId()) {
                case R.id.menu_connect:
                    mBluetoothLeService.connect(mDeviceAddress);
                    return true;
                case R.id.menu_disconnect:
                    mBluetoothLeService.disconnect();
                    return true;
                case android.R.id.home:
                    onBackPressed();
                    return true;
            }
            return super.onOptionsItemSelected(item);
        }
    
        private void updateConnectionState(final int resourceId) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mConnectionState.setText(resourceId);
                }
            });
        }
    
        private void displayData(String data) {
            if (data != null) {
                mDataField.setText(data);
            }
        }
    
        // Demonstrates how to iterate through the supported GATT Services/Characteristics.
        // In this sample, we populate the data structure that is bound to the ExpandableListView
        // on the UI.
        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>>();
    
            // Loops through available GATT Services.
            for (BluetoothGattService gattService : gattServices) {
                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);
                gattServiceData.add(currentServiceData);
    
                ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                        new ArrayList<HashMap<String, String>>();
                List<BluetoothGattCharacteristic> gattCharacteristics =
                        gattService.getCharacteristics();
                ArrayList<BluetoothGattCharacteristic> charas =
                        new ArrayList<BluetoothGattCharacteristic>();
    
                // Loops through available Characteristics.
                for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
                    charas.add(gattCharacteristic);
                    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);
                    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);
        }
    
        private static IntentFilter makeGattUpdateIntentFilter() {
            final IntentFilter intentFilter = new IntentFilter();
            intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
            intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
            intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
            intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
            return intentFilter;
        }
    }
    public class BluetoothLeService extends Service {
        private final static String TAG = BluetoothLeService.class.getSimpleName();
    
        private BluetoothManager mBluetoothManager;
        private BluetoothAdapter mBluetoothAdapter;
        private String mBluetoothDeviceAddress;
        private BluetoothGatt mBluetoothGatt;
        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.example.bluetooth.le.ACTION_GATT_CONNECTED";
        public final static String ACTION_GATT_DISCONNECTED =
                "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
        public final static String ACTION_GATT_SERVICES_DISCOVERED =
                "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
        public final static String ACTION_DATA_AVAILABLE =
                "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
        public final static String EXTRA_DATA =
                "com.example.bluetooth.le.EXTRA_DATA";
    
        public final static UUID UUID_HEART_RATE_MEASUREMENT =
                UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
    
        // 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());
    
                } 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);
            }
        };
    
        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);
    
            // This is special handling for the Heart Rate Measurement profile.  Data parsing is
            // carried out as per profile specifications:
            // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
            if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
                int flag = characteristic.getProperties();
                int format = -1;
                if ((flag & 0x01) != 0) {
                    format = BluetoothGattCharacteristic.FORMAT_UINT16;
                    Log.d(TAG, "Heart rate format UINT16.");
                } else {
                    format = BluetoothGattCharacteristic.FORMAT_UINT8;
                    Log.d(TAG, "Heart rate format UINT8.");
                }
                final int heartRate = characteristic.getIntValue(format, 1);
                Log.d(TAG, String.format("Received heart rate: %d", heartRate));
                intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
            } else {
                // For all other profiles, writes the data formatted in HEX.
                final byte[] data = characteristic.getValue();
                if (data != null && data.length > 0) {
                    final StringBuilder stringBuilder = new StringBuilder(data.length);
                    for (byte byteChar : data)
                        stringBuilder.append(String.format("%02X ", byteChar));
                    intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
                }
            }
            sendBroadcast(intent);
        }
    
        public class LocalBinder extends Binder {
            BluetoothLeService getService() {
                return BluetoothLeService.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;
            }
    
            return true;
        }
    
        /**
         * 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) {
            if (mBluetoothAdapter == null || address == null) {
                Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
                return false;
            }
    
            // Previously connected device.  Try to reconnect.
            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;
            }
            // 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;
        }
    
        /**
         * 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) {
            if (mBluetoothAdapter == null || mBluetoothGatt == null) {
                Log.w(TAG, "BluetoothAdapter not initialized");
                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.w(TAG, "BluetoothAdapter not initialized");
                return;
            }
            mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    
            // This is specific to Heart Rate Measurement.
            if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
                BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
                        UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
                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();
        }
    }
    

    具体代码:
    Bluetooth

    相关文章

      网友评论

          本文标题:android-标准蓝牙开发与蓝牙低功耗开发

          本文链接:https://www.haomeiwen.com/subject/obsltxtx.html