{"id":1152,"date":"2016-08-26T22:13:00","date_gmt":"2016-08-26T14:13:00","guid":{"rendered":"https:\/\/www.ray650128.com\/wordpress\/?p=1152"},"modified":"2021-08-28T14:53:58","modified_gmt":"2021-08-28T06:53:58","slug":"android-ble","status":"publish","type":"post","link":"https:\/\/blog.ray650128.com\/?p=1152","title":{"rendered":"Android BLE"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">BLE<\/h2>\n\n\n\n<p>BLE\uff08Bluetooth Low Energy\uff0c\u4f4e\u529f\u8017\u85cd\u82bd\uff09\uff0c\u662f\u85cd\u82bd4.0\u7248\u672c\u4e2d\u52a0\u5165\u7684\u4e00\u500b\u7279\u6027\u3002<\/p>\n\n\n\n<p>Android\u57284.3\u7248\uff08API 18\uff09\u6642\uff0c\u958b\u59cb\u652f\u63f4BLE\u3002\u4ee5\u4e0b\u662fBLE\u7684\u4e00\u4e9b\u7279\u6027\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>\u548c\u7d93\u5178\u85cd\u82bd\uff08Bluetooth 2.0\u30013.0\uff09\u76f8\u6bd4\uff0c\u5177\u6709\u7701\u96fb\u7684\u512a\u52e2\u3002<\/li><li>\u9ad4\u7a4d\u5c0f<\/li><li>\u50f9\u683c\u4fbf\u5b9c<\/li><li>iOS\u3001Android\u3001Windows\u90fd\u652f\u63f4<\/li><\/ul>\n\n\n\n<p>\u95dc\u65bcBLE\u66f4\u8a73\u7d30\u7684\u4ecb\u7d39\uff0c\u8acb\u53c3\u8003<a rel=\"noreferrer noopener\" href=\"https:\/\/zh.wikipedia.org\/wiki\/%E8%93%9D%E7%89%99%E4%BD%8E%E5%8A%9F%E8%80%97\" data-type=\"URL\" data-id=\"https:\/\/zh.wikipedia.org\/wiki\/%E8%93%9D%E7%89%99%E4%BD%8E%E5%8A%9F%E8%80%97\" target=\"_blank\">\u7dad\u57fa\u767e\u79d1<\/a>\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u958b\u767cAPP\u90e8\u5206<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">\u524d\u7f6e\u4f5c\u696d<\/h3>\n\n\n\n<p>\u8981\u5728Android APP\u4e0a\u4f7f\u7528BLE\u7684API\uff0c\u5fc5\u9808\u8981\u958b\u555f\u4ee5\u4e0b\u5e7e\u500b\u6b0a\u9650\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>android.permission.BLUETOOTH<\/li><li>android.permission.BLUETOOTH_ADMIN<\/li><li>android.permission.ACCESS_FINE_LOCATION<\/li><\/ul>\n\n\n\n<p>\u9664\u6b64\u4e4b\u5916\uff0c\u9084\u9700\u8981\u5728Android Manifest.xml\u4e2d\u8072\u660e\u4f7f\u7528\u5230\u7684\u88dd\u7f6e\u529f\u80fd\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>android.hardware.bluetooth_le<\/li><\/ul>\n\n\n\n<p>\u7531\u65bc\uff0cACCESS_FINE_LOCATION\u5c6c\u65bc\u5371\u96aa\u6b0a\u9650\u4e4b\u4e00\uff08\u8a73\u7d30\u8acb\u770b\u6211\u7684\u53e6\u4e00\u7bc7\u6587\u7ae0\uff09\uff0c\u56e0\u6b64\u9700\u8981\u5728\u7a0b\u5f0f\u88e1\u52a0\u5165\u52d5\u614b\u7533\u8acb\u6b0a\u9650\u7684\u529f\u80fd\uff0c\u5982\u4e0b\uff1a<\/p>\n\n\n\n<p>\u524d\u7f6e\u4f5c\u696d\u5230\u76ee\u524d\u70ba\u6b62\u90fd\u5df2\u7d93\u5b8c\u6210\uff0c\u63a5\u8457\u5c31\u53ef\u4ee5\u958b\u59cb\u4f7f\u7528BLE\u4e86\u3002<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u641c\u5c0b\u88dd\u7f6e<\/h3>\n\n\n\n<p>\u8981\u548cBLE\u88dd\u7f6e\u9023\u7dda\uff0c\u5fc5\u9808\u8981\u77e5\u9053\u8a72\u88dd\u7f6e\u7684\u786c\u9ad4\u8b58\u5225\u78bc\uff0c\u4e5f\u5c31\u662fMAC Address\u3002<\/p>\n\n\n\n<p>\u7531\u65bc\u6211\u5011\u4e26\u4e0d\u77e5\u9053\u88dd\u7f6e\u7684\u786c\u9ad4\u8b58\u5225\u78bc\u662f\u591a\u5c11\uff0c\u56e0\u6b64\u5fc5\u9808\u8981\u5148\u7d93\u904e\u641c\u5c0b\u7684\u968e\u6bb5\uff0c\u4ee5\u4e0b\u662f\u641c\u5c0b\u4e26\u4ee5\u5217\u8868\u65b9\u5f0f\u5448\u73fe\u88dd\u7f6e\u6e05\u55ae\u7684\u793a\u7bc4\uff1a<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java line-numbers\">public class ScanActivity extends AppCompatActivity {\n\n    private LeDeviceListAdapter mLeDeviceListAdapter;\n    private BluetoothAdapter mBluetoothAdapter;\n    private boolean isScanning;\n    private Handler mHandler;\n\n    private static final int REQUEST_LOCATION = 0x0;\n    private static final int REQUEST_ENABLE_BT = 0x1;\n\n    \/\/ 10 \u79d2\u5f8c\u505c\u6b62\u6383\u63cf\n    private static final long SCAN_INTERVAL = 10000;\n\n    \/\/ UI \u5143\u4ef6\n    private Button btnScan;\n    private ListView listView;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_scan);\n\n        mHandler = new Handler();\n\n        btnScan = (Button) findViewById(R.id.button_scan);\n        listView = (ListView) findViewById(R.id.list_view);\n\n        \/\/ \u6aa2\u67e5\u8a2d\u5099\u662f\u5426\u652f\u63f4 BLE\n        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {\n            Toast.makeText(this, \"\u4f60\u7684\u88dd\u7f6e\u4e0d\u652f\u63f4 BLE\", Toast.LENGTH_SHORT).show();\n            finish();\n        }\n\n        \/\/ \u521d\u59cb\u5316\u85cd\u82bd Adapter\n        final BluetoothManager bluetoothMgr = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);\n        mBluetoothAdapter = bluetoothMgr.getAdapter();\n\n        if (mBluetoothAdapter == null) {\n            Toast.makeText(this, \"\u4f60\u7684\u88dd\u7f6e\u90e8\u652f\u63f4\u85cd\u82bd\", Toast.LENGTH_SHORT).show();\n            finish();\n        }\n\n        \/\/ \u521d\u59cb\u5316\u6383\u63cf\u88dd\u7f6e\u6e05\u55ae\n        mLeDeviceListAdapter = new LeDeviceListAdapter();\n        listView.setAdapter(mLeDeviceListAdapter);\n        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {\n            @Override\n            public void onItemClick(AdapterView&lt;?&gt; adapterView, View view, int position, long l) {\n                final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);\n                if (device == null) return;\n                final Intent intent = new Intent(ScanActivity.this, MainActivity.class);\n                intent.putExtra(MainActivity.EXTRAS_DEVICE_NAME, device.getName());\n                intent.putExtra(MainActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());\n                if (isScanning) {\n                    mBluetoothAdapter.stopLeScan(mLeScanCallback);\n                    isScanning = false;\n                }\n                \/\/startActivity(intent);\n            }\n        });\n\n        \/\/ \u52a0\u5165\u6309\u9215\u4e8b\u4ef6\n        btnScan.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                mLeDeviceListAdapter.clear();\n                scanLeDevice(true);\n            }\n        });\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n\n        \/\/ \u5982\u679c\u85cd\u82bd\u529f\u80fd\u95dc\u9589\uff0c\u5247\u6253\u958b\u85cd\u82bd\u8a2d\u5b9a\n        if (!mBluetoothAdapter.isEnabled()) {\n            if (!mBluetoothAdapter.isEnabled()) {\n                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);\n                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);\n            }\n        }\n\n        checkLocationPermission();\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        \/\/ \u5982\u679c\u4f7f\u7528\u8005\u6c92\u958b\u555f\u85cd\u82bd\uff0c\u5247\u96e2\u958bAPP\n        if (requestCode == REQUEST_ENABLE_BT &amp;&amp; resultCode == Activity.RESULT_CANCELED) {\n            finish();\n            return;\n        }\n        super.onActivityResult(requestCode, resultCode, data);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        scanLeDevice(false);\n        mLeDeviceListAdapter.clear();\n    }\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        switch(requestCode) {\n            case REQUEST_LOCATION:\n                if (grantResults.length &gt; 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED) {\n                    \/\/ \u5df2\u53d6\u5f97\u6b0a\u9650\uff0c\u5728\u6b64\u505a\u5f8c\u7e8c\u8655\u7406\n                    mLeDeviceListAdapter.clear();\n                    scanLeDevice(true);\n                } else {\n                    \/\/ \u4f7f\u7528\u8005\u62d2\u7d55\uff0c\u986f\u793a\u5c0d\u8a71\u6846\u544a\u77e5\n                    new AlertDialog.Builder(this)\n                            .setMessage(\"\u8981\u4f7f\u7528BLE\u529f\u80fd\uff0c\u5fc5\u9808\u5148\u5141\u8a31\u5b9a\u4f4d\u6b0a\u9650\")\n                            .setPositiveButton(\"\u78ba\u5b9a\", null)\n                            .show();\n                }\n                break;\n            default:\n                break;\n        }\n    }\n\n    private void checkLocationPermission() {\n&nbsp;       \/\/ \u52d5\u614b\u7533\u8acb\u6b0a\u9650\n        int permission = ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION);\n        if (permission != PackageManager.PERMISSION_GRANTED) {\n            \/\/ \u672a\u53d6\u5f97\u6b0a\u9650\uff0c\u8a62\u554f\u4f7f\u7528\u8005\u662f\u5426\u6388\u8207\n            ActivityCompat.requestPermissions(\n                    this,\n                    new String[]{ACCESS_FINE_LOCATION},\n                    REQUEST_LOCATION);\n        } else {\n            \/\/ \u5df2\u53d6\u5f97\u6b0a\u9650\uff0c\u5728\u6b64\u505a\u5f8c\u7e8c\u8655\u7406\n            mLeDeviceListAdapter.clear();\n            scanLeDevice(true);\n        }\n    }\n\n    private void scanLeDevice(final boolean enable) {\n        if (enable) {\n            \/\/ \u5982\u679c\u6383\u63cf\u6642\u9593\u5230\uff0c\u505c\u6b62\u6383\u63cf\n            mHandler.postDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    isScanning = false;\n                    mBluetoothAdapter.stopLeScan(mLeScanCallback);\n                    invalidateOptionsMenu();\n                }\n            }, SCAN_INTERVAL);\n\n            isScanning = true;\n            mBluetoothAdapter.startLeScan(mLeScanCallback);\n        } else {\n            isScanning = false;\n            mBluetoothAdapter.stopLeScan(mLeScanCallback);\n        }\n        invalidateOptionsMenu();\n    }\n\n    \/\/ \u88dd\u7f6e\u6e05\u55ae Adapter\n    private class LeDeviceListAdapter extends BaseAdapter {\n        private ArrayList&lt;BluetoothDevice&gt; mLeDevices;\n        private LayoutInflater mInflator;\n\n        public LeDeviceListAdapter() {\n            super();\n            mLeDevices = new ArrayList&lt;&gt;();\n            mInflator = ScanActivity.this.getLayoutInflater();\n        }\n\n        public void addDevice(BluetoothDevice device) {\n            if (!mLeDevices.contains(device)) {\n                mLeDevices.add(device);\n            }\n        }\n\n        public BluetoothDevice getDevice(int position) {\n            return mLeDevices.get(position);\n        }\n\n        public void clear() {\n            mLeDevices.clear();\n        }\n\n        @Override\n        public int getCount() {\n            return mLeDevices.size();\n        }\n\n        @Override\n        public Object getItem(int i) {\n            return mLeDevices.get(i);\n        }\n\n        @Override\n        public long getItemId(int i) {\n            return i;\n        }\n\n        @Override\n        public View getView(int i, View view, ViewGroup viewGroup) {\n            ViewHolder viewHolder;\n            \/\/ General ListView optimization code.\n            if (view == null) {\n                view = mInflator.inflate(R.layout.listitem_device, null);\n                viewHolder = new ViewHolder();\n                viewHolder.deviceAddress = view.findViewById(R.id.device_mac);\n                viewHolder.deviceName = view.findViewById(R.id.device_name);\n                view.setTag(viewHolder);\n            } else {\n                viewHolder = (ViewHolder) view.getTag();\n            }\n\n            BluetoothDevice device = mLeDevices.get(i);\n            final String deviceName = device.getName();\n            if (deviceName != null &amp;&amp; deviceName.length() &gt; 0)\n                viewHolder.deviceName.setText(deviceName);\n            else\n                viewHolder.deviceName.setText(\"\u672a\u77e5\u7684\u88dd\u7f6e\");\n            viewHolder.deviceAddress.setText(device.getAddress());\n\n            return view;\n        }\n    }\n\n    \/\/ \u88dd\u7f6e\u6383\u63cf callback\n    private BluetoothAdapter.LeScanCallback mLeScanCallback =\n            new BluetoothAdapter.LeScanCallback() {\n\n                @Override\n                public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {\n                    runOnUiThread(new Runnable() {\n                        @Override\n                        public void run() {\n                            mLeDeviceListAdapter.addDevice(device);\n                            mLeDeviceListAdapter.notifyDataSetChanged();\n                        }\n                    });\n                }\n            };\n\n    static class ViewHolder {\n        TextView deviceName;\n        TextView deviceAddress;\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u9023\u63a5\u88dd\u7f6e\u4e26\u9032\u884c GATT service \u63a2\u7d22<\/h3>\n\n\n\n<p>\u641c\u5c0b\u5230\u60f3\u8981\u7684\u88dd\u7f6e\u4e4b\u5f8c\uff0c\u63a5\u8457\u5c31\u662f\u9023\u7dda\u7684\u90e8\u5206\u3002\u6211\u5011\u53ef\u4ee5\u900f\u904e\u9ede\u9078\u6e05\u55ae\u4e2d\u7684\u88dd\u7f6e\uff0c\u4e26\u628a\u8a72\u9805\u7684\u88dd\u7f6e\u8cc7\u8a0a\u7528intent\u50b3\u905e\u7d66MainActivity\uff0c\u4e4b\u5f8cMainActivity\u4fbf\u6703\u555f\u52d5BluetoothLeService\u9032\u884c\u9023\u7dda\uff0c\u5982\u4e0b\uff1a<\/p>\n\n\n\n<p><strong>MainActivity<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java line-numbers\">public class MainActivity extends AppCompatActivity {\n    private final static String TAG = MainActivity.class.getSimpleName();\n\n    public static final String EXTRAS_DEVICE_NAME = \"DEVICE_NAME\";\n    public static final String EXTRAS_DEVICE_ADDRESS = \"DEVICE_ADDRESS\";\n\n    private String mDeviceName;\n    private String mDeviceAddress;\n\n    private BluetoothLeService mBluetoothLeService;\n\n    private final ServiceConnection mServiceConnection = new ServiceConnection() {\n\n        @Override\n        public void onServiceConnected(ComponentName componentName, IBinder service) {\n            mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();\n            if (!mBluetoothLeService.initialize()) {\n                Log.e(TAG, \"\u7121\u6cd5\u521d\u59cb\u5316 Bluetooth \u5143\u4ef6\");\n                finish();\n            }\n            \/\/ Automatically connects to the device upon successful start-up initialization.\n            mBluetoothLeService.connect(mDeviceAddress);\n        }\n\n        @Override\n        public void onServiceDisconnected(ComponentName componentName) {\n            mBluetoothLeService = null;\n        }\n    };\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        final Intent intent = getIntent();\n        mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);\n        mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);\n\n        Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);\n        bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><strong>BluetoothLeService<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java line-numbers\">public class BluetoothLeService extends Service {\n    private final static String TAG = BluetoothLeService.class.getSimpleName();\n\n    private BluetoothManager mBluetoothManager;\n    private BluetoothAdapter mBluetoothAdapter;\n    private String mBluetoothDeviceAddress;\n    private BluetoothGatt mBluetoothGatt;\n    private int mConnectionState = STATE_DISCONNECTED;\n\n    private static final int STATE_DISCONNECTED = 0;\n    private static final int STATE_CONNECTING = 1;\n    private static final int STATE_CONNECTED = 2;\n\n    public final static String ACTION_GATT_CONNECTED =\n            \"com.example.bluetooth.le.ACTION_GATT_CONNECTED\";\n    public final static String ACTION_GATT_DISCONNECTED =\n            \"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED\";\n    public final static String ACTION_GATT_SERVICES_DISCOVERED =\n            \"com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED\";\n    public final static String ACTION_DATA_AVAILABLE =\n            \"com.example.bluetooth.le.ACTION_DATA_AVAILABLE\";\n    public final static String EXTRA_DATA =\n            \"com.example.bluetooth.le.EXTRA_DATA\";\n\n    public final static UUID UUID_HEART_RATE_MEASUREMENT =\n            UUID.fromString(GattAttributes.HEART_RATE_MEASUREMENT);\n\n    \/\/ \u5be6\u4f8b\u5316 GATT \u4e8b\u4ef6 callback\n    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {\n        @Override\n        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {\n            String intentAction;\n            if (newState == BluetoothProfile.STATE_CONNECTED) {\n                intentAction = ACTION_GATT_CONNECTED;\n                mConnectionState = STATE_CONNECTED;\n                broadcastUpdate(intentAction);\n                Log.i(TAG, \"\u9023\u7dda\u5230 BLE \u88dd\u7f6e\");\n                \/\/ \u9023\u7dda\u5f8c\u63a2\u7d22 BLE \u88dd\u7f6e\u63d0\u4f9b\u7684\u670d\u52d9\n                Log.i(TAG, \"\u958b\u59cb\u63a2\u7d22:\" +\n                        mBluetoothGatt.discoverServices());\n\n            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {\n                intentAction = ACTION_GATT_DISCONNECTED;\n                mConnectionState = STATE_DISCONNECTED;\n                Log.i(TAG, \"\u5df2\u65b7\u7dda\");\n                broadcastUpdate(intentAction);\n            }\n        }\n\n        @Override\n        public void onServicesDiscovered(BluetoothGatt gatt, int status) {\n            if (status == BluetoothGatt.GATT_SUCCESS) {\n                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);\n            } else {\n                Log.w(TAG, \"\u5df2\u63a2\u7d22\u5230\u670d\u52d9: \" + status);\n            }\n        }\n\n        @Override\n        public void onCharacteristicRead(BluetoothGatt gatt,\n                                         BluetoothGattCharacteristic characteristic,\n                                         int status) {\n            if (status == BluetoothGatt.GATT_SUCCESS) {\n                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);\n            }\n        }\n\n        @Override\n        public void onCharacteristicChanged(BluetoothGatt gatt,\n                                            BluetoothGattCharacteristic characteristic) {\n            broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);\n        }\n    };\n\n    private void broadcastUpdate(final String action) {\n        final Intent intent = new Intent(action);\n        sendBroadcast(intent);\n    }\n\n    private void broadcastUpdate(final String action,\n                                 final BluetoothGattCharacteristic characteristic) {\n        final Intent intent = new Intent(action);\n\n        final byte[] data = characteristic.getValue();\n        if (data != null &amp;&amp; data.length &gt; 0) {\n            final StringBuilder stringBuilder = new StringBuilder(data.length);\n            for(byte byteChar : data)\n                stringBuilder.append(String.format(\"%02X \", byteChar));\n            intent.putExtra(EXTRA_DATA, new String(data) + \"\\n\" + stringBuilder.toString());\n        }\n        sendBroadcast(intent);\n    }\n\n    public class LocalBinder extends Binder {\n        BluetoothLeService getService() {\n            return BluetoothLeService.this;\n        }\n    }\n\n    @Override\n    public IBinder onBind(Intent intent) {\n        return mBinder;\n    }\n\n    @Override\n    public boolean onUnbind(Intent intent) {\n        \/\/ \u7576 Service \u8207 Activity \u89e3\u9664\u7d81\u5b9a\u5f8c\uff0c\u95dc\u9589 BLE \u9023\u7dda\uff0c\u4ee5\u514d\u6d6a\u8cbb\u8cc7\u6e90\n        close();\n        return super.onUnbind(intent);\n    }\n\n    private final IBinder mBinder = new LocalBinder();\n\n    \/**\n     * \u521d\u59cb\u5316 Bluetooth adapter.\n     *\n     * @return \u5982\u679c\u521d\u59cb\u5316\u6210\u529f\uff0c\u5247\u56de\u50b3 true\u3002\n     *\/\n    public boolean initialize() {\n        \/\/ For API level 18 and above, get a reference to BluetoothAdapter through\n        \/\/ BluetoothManager.\n        if (mBluetoothManager == null) {\n            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);\n            if (mBluetoothManager == null) {\n                Log.e(TAG, \"\u7121\u6cd5\u521d\u59cb\u5316 BluetoothManager\");\n                return false;\n            }\n        }\n\n        mBluetoothAdapter = mBluetoothManager.getAdapter();\n        if (mBluetoothAdapter == null) {\n            Log.e(TAG, \"\u7121\u6cd5\u53d6\u5f97 BluetoothAdapter \u5143\u4ef6\");\n            return false;\n        }\n\n        return true;\n    }\n\n    \/**\n     * \u9023\u7dda\u5230 BLE \u88dd\u7f6e\u7684 GATT server\n     *\n     * @param address \u76ee\u7684\u5730\u88dd\u7f6e\u7684 mac address\n     *\n     * @return \u5982\u679c\u9023\u7dda\u6210\u529f\uff0c\u56de\u50b3 true\u3002\n     *         \u9023\u7dda\u7d50\u679c\u900f\u904e {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}\n     *         callback \u56de\u50b3\u3002\n     *\/\n    public boolean connect(final String address) {\n        if (mBluetoothAdapter == null || address == null) {\n            Log.w(TAG, \"BluetoothAdapter \u672a\u521d\u59cb\u5316\u6216\u672a\u6307\u5b9a mac address\");\n            return false;\n        }\n\n        \/\/ Previously connected device.  Try to reconnect.\n        if (mBluetoothDeviceAddress != null &amp;&amp; address.equals(mBluetoothDeviceAddress)\n                &amp;&amp; mBluetoothGatt != null) {\n            Log.d(TAG, \"\u6b63\u5728\u5617\u8a66\u4f7f\u7528\u73fe\u6709\u7684 mBluetoothGatt \u9023\u7dda\");\n            if (mBluetoothGatt.connect()) {\n                mConnectionState = STATE_CONNECTING;\n                return true;\n            } else {\n                return false;\n            }\n        }\n\n        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);\n        if (device == null) {\n            Log.w(TAG, \"\u627e\u4e0d\u5230\u88dd\u7f6e\uff0c\u7121\u6cd5\u9023\u7dda\");\n            return false;\n        }\n        \/\/ We want to directly connect to the device, so we are setting the autoConnect\n        \/\/ parameter to false.\n        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);\n        Log.d(TAG, \"\u6b63\u5728\u5617\u8a66\u5efa\u7acb\u65b0\u7684\u9023\u7dda\");\n        mBluetoothDeviceAddress = address;\n        mConnectionState = STATE_CONNECTING;\n        return true;\n    }\n\n    \/**\n     * \u5c07\u73fe\u6709\u7684\u9023\u7dda\u4e2d\u65b7\uff0c\u65b7\u7dda\u7d50\u679c\u7d93\u7531\uff1a\n     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}\n     * callback \u56de\u50b3\u3002\n     *\/\n    public void disconnect() {\n        if (mBluetoothAdapter == null || mBluetoothGatt == null) {\n            Log.w(TAG, \"BluetoothAdapter \u5c1a\u672a\u88ab\u521d\u59cb\u5316\");\n            return;\n        }\n        mBluetoothGatt.disconnect();\n    }\n\n    \/**\n     * \u91cb\u653e\u85cd\u82bd\u8cc7\u6e90\n     *\/\n    public void close() {\n        if (mBluetoothGatt == null) {\n            return;\n        }\n        mBluetoothGatt.close();\n        mBluetoothGatt = null;\n    }\n\n    \/**\n     * \u8b80\u53d6\u7279\u5fb5\u5c6c\u6027\u7684\u8cc7\u6599\n     *\n     * @param characteristic \u7279\u5fb5\u5c6c\u6027\n     *\/\n    public void readCharacteristic(BluetoothGattCharacteristic characteristic) {\n        if (mBluetoothAdapter == null || mBluetoothGatt == null) {\n            Log.w(TAG, \"BluetoothAdapter \u5c1a\u672a\u88ab\u521d\u59cb\u5316\");\n            return;\n        }\n        mBluetoothGatt.readCharacteristic(characteristic);\n    }\n\n    \/**\n     * \u958b\u555f\u6216\u95dc\u9589\u7279\u5fb5\u5c6c\u6027\u7684\u901a\u77e5\u529f\u80fd\n     *\n     * @param characteristic \u7279\u5fb5\u5c6c\u6027\n     * @param enabled \u958b\u555f\u6216\u95dc\u9589\n     *\/\n    public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,\n                                              boolean enabled) {\n        if (mBluetoothAdapter == null || mBluetoothGatt == null) {\n            Log.w(TAG, \"BluetoothAdapter \u5c1a\u672a\u88ab\u521d\u59cb\u5316\");\n            return;\n        }\n        mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);\n    }\n\n    \/**\n     * \u6aa2\u67e5\u5df2\u9023\u7dda BLE \u88dd\u7f6e\u7684 GATT service\uff0c\n     * \u5fc5\u9808\u5728 {@code BluetoothGatt#discoverServices()} \u5b8c\u6210\u5f8c\u547c\u53eb\u672c\u65b9\u6cd5\n     *\n     * @return \u652f\u63f4\u7684\u670d\u52d9\u6e05\u55ae {@code List}\n     *\/\n    public List&lt;BluetoothGattService&gt; getSupportedGattServices() {\n        if (mBluetoothGatt == null) return null;\n\n        return mBluetoothGatt.getServices();\n    }\n}\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u8b80\u53d6\u7279\u5fb5\u5c6c\u6027\u7684\u8cc7\u6599<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java line-numbers\">public void readCharacteristic(BluetoothGattCharacteristic characteristic) {\n&nbsp;   if (mBluetoothAdapter == null || mBluetoothGatt == null) {\n&nbsp;       Log.w(TAG, \"BluetoothAdapter \u5c1a\u672a\u88ab\u521d\u59cb\u5316\");\n&nbsp;       return;\n&nbsp;   }\n    mBluetoothGatt.readCharacteristic(characteristic);\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u5c07\u8cc7\u6599\u5beb\u5165\u7279\u5fb5\u5c6c\u6027<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java line-numbers\">public void writeCharacteristic(BluetoothGattCharacteristic characteristic, byte[] data) {\n    if (mBluetoothAdapter == null || mBluetoothGatt == null) {\n        Log.w(TAG, \"BluetoothAdapter \u5c1a\u672a\u88ab\u521d\u59cb\u5316\");\n        return;\n    }\n    characteristic.setValue(data);\n    mBluetoothGatt.writeCharacteristic(characteristic);\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u958b\u555f\u901a\u77e5<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"java\" class=\"language-java line-numbers\">public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,\n                                              boolean enabled) {\n    if (mBluetoothAdapter == null || mBluetoothGatt == null) {\n        Log.w(TAG, \"BluetoothAdapter \u5c1a\u672a\u88ab\u521d\u59cb\u5316\");\n        return;\n    }\n    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);\n        UUID uuid = UUID.fromString(GattAttributes.CLIENT_CHARACTERISTIC_CONFIG);\n        BluetoothGattDescriptor descriptor = characteristic.getDescriptor(uuid);\n        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);\n        mBluetoothGatt.writeDescriptor(descriptor);\n    }<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>BLE BLE\uff08Bluetooth Low Energy\uff0c\u4f4e\u529f\u8017\u85cd\u82bd\uff09\uff0c\u662f\u85cd\u82bd4.0\u7248\u672c\u4e2d\u52a0\u5165\u7684\u4e00\u500b\u7279\u6027\u3002 A &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[10,17],"class_list":["post-1152","post","type-post","status-publish","format-standard","hentry","category-2","tag-android-app","tag-java"],"_links":{"self":[{"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=\/wp\/v2\/posts\/1152","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1152"}],"version-history":[{"count":8,"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=\/wp\/v2\/posts\/1152\/revisions"}],"predecessor-version":[{"id":1171,"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=\/wp\/v2\/posts\/1152\/revisions\/1171"}],"wp:attachment":[{"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1152"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1152"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ray650128.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1152"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}