蓝牙4.0

Posted 取法乎上★止于至善

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝牙4.0相关的知识,希望对你有一定的参考价值。

http://www.voidcn.com/blog/wangqjpp/article/p-5038224.html

 

android使用蓝牙4.0的条件:Android API Level 要在18及以上,即android 4.3以上。

 

 

一、蓝牙的几个参数:

 
 1.1 BluetoothGatt
 继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到BluetoothGattCallback 
 1.2 BluetoothGattCharacteristic
 相当于一个数据类型,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)
 1.3 BluetoothGattDescriptor
 描述符,对Characteristic的描述,包括范围、计量单位等
 1.4 BluetoothGattService
 服务,Characteristic的集合。
 1.5 BluetoothProfile
  一个通用的规范,按照这个规范来收发数据。
 1.6 BluetoothManager
  通过BluetoothManager来获取BluetoothAdapter
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
1.7 BluetoothAdapter 
一个Android系统只有一个BluetoothAdapter ,通过BluetoothManager 获取 
BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
1.8 BluetoothGattCallback
 
 已经连接上设备,对设备的某些操作后返回的结果。这里必须提醒下,已经连接上设备后的才可以返回,没有返回的认真看看有没有连接上设备。
private BluetoothGattCallback GattCallback = new BluetoothGattCallback() {
    // 这里有9个要实现的方法,看情况要实现那些,用到那些就实现那些
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState){};
    public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status){};
};
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
BluetoothGatt gatt = device.connectGatt(this, false, mGattCallback);
1.8.1:notification对应onCharacteristicChanged; 
gatt.setCharacteristicNotification(characteristic, true);
1.8.2:readCharacteristic对应onCharacteristicRead; 
gatt.readCharacteristic(characteristic);
1.8.3: writeCharacteristic对应onCharacteristicWrite; 
gatt.wirteCharacteristic(mCurrentcharacteristic);
1.8.4:连接蓝牙或者断开蓝牙 对应 onConnectionStateChange; 
1.8.5: readDescriptor对应onDescriptorRead; 
1.8.6:writeDescriptor对应onDescriptorWrite;  
gatt.writeDescriptor(descriptor);
1.8.7:readRemoteRssi对应onReadRemoteRssi;  
gatt.readRemoteRssi()
1.8.8:executeReliableWrite对应onReliableWriteCompleted; 
1.8.9:discoverServices对应onServicesDiscovered。 
gatt.discoverServices()
1.9 BluetoothDevice 
扫描后发现可连接的设备,获取已经连接的设备
 

二、蓝牙权限与是否支持蓝牙4.0

1.使用蓝牙要打开相关权限

 

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

其中,如果android.hardware.bluetooth_le设置为false,可以安装在不支持的设备上使用。

 

2.判断是否支持蓝牙4.0

方法1:

 

<span style="font-family:SimSun;font-size:12px;">if (!mContext.getPackageManager().hasSystemFeature(
				PackageManager.FEATURE_BLUETOOTH_LE)) {
			Toast.makeText(mContext, R.string.ble_not_supported,
					Toast.LENGTH_SHORT).show();
			return;
		}</span>

 

方法2:
<span style="font-family:SimSun;">BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothManager BmBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(mContext, R.string.ble_not_supported,
Toast.LENGTH_SHORT).show();
}</span>

三.蓝牙打开与关闭

1.强制打开,不提示用户

<span style="font-family:SimSun;">//true为强制打开成功,false为失败
public static boolean turnOnBluetooth()	{
		BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		if (bluetoothAdapter != null){
			return bluetoothAdapter.enable();
		}
		return false;
	}</span>

2.调用系统弹出框提示用户打开

<span style="font-family:SimSun;"><pre name="code" class="java">public class MainActivity extends Activity{
	//自定义的打开 Bluetooth 的请求码,与 onActivityResult 中返回的 requestCode 匹配。
	private static final int REQUEST_CODE_BLUETOOTH_ON = 999;
	//Bluetooth 设备可见时间,单位:秒。
	private static final int BLUETOOTH_DISCOVERABLE_DURATION = 300;
	@Override
	protected void onCreate(Bundle savedInstanceState)	{
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.activity_main);
		if ((BluetoothManager.isBluetoothSupported())
				&& (!BluetoothManager.isBluetoothEnabled())){
			this.turnOnBluetooth();
		}
	}	
	//弹出系统弹框提示用户打开 Bluetooth
	private void turnOnBluetooth()	{
		// 请求打开 Bluetooth
		Intent requestBluetoothOn = new Intent(
				BluetoothAdapter.ACTION_REQUEST_ENABLE);
		// 设置 Bluetooth 设备可以被其它 Bluetooth 设备扫描到
		requestBluetoothOn.setAction(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
		// 设置 Bluetooth 设备可见时间
		requestBluetoothOn.putExtra(
				BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION,
				BLUETOOTH_DISCOVERABLE_DURATION);
		// 请求开启 Bluetooth
		this.startActivityForResult(requestBluetoothOn,
				REQUEST_CODE_BLUETOOTH_ON);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data){
		// requestCode 与请求开启 Bluetooth 传入的 requestCode 相对应
		if (requestCode == REQUEST_CODE_BLUETOOTH_ON){
			switch (resultCode){
			// 点击确认按钮
				case Activity.RESULT_OK:
		//  用户选择开启 Bluetooth,Bluetooth 会被开启				
				break;
				// 点击取消按钮或点击返回键
				case Activity.RESULT_CANCELED:
				       // TODO 用户拒绝打开 Bluetooth, Bluetooth 不会被开启
				break;
				default:
				break;
			}
		}
	}
}</span>

3.跳转到系统设置中让用户手动打开

<span style="font-family:SimSun;">this.startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
</span>

4.强制关闭蓝牙

<span style="font-family:SimSun;">   //强制关闭 Bluetooth 成功 false:强制关闭 Bluetooth 失败	
	public static boolean turnOffBluetooth(){
		BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		if (bluetoothAdapter != null){
			return bluetoothAdapter.disable();
		}
		return false;
	}</span>

5.跳转到系统设置中让用户手动关闭

<span style="font-family:SimSun;">this.startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
</span>

四、扫描设备并连接设备

1.通过startLeScan查找LE设备,并实现LeScanCallback作为参数

注意点:
a、一旦找到所查找的设备,立即停止扫描;
b、要么搜索经典蓝牙,要么搜索BLE,两者不能同时搜索
<span style="font-family:SimSun;"> private static final long SCAN_PERIOD = 10000; 
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);
        }
    }</span>

2.设备扫描的回调(此时将获得设备信息)

<span style="font-family:SimSun;">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() {
					//通过这个device获取设备的name、address、uuid等信息
                    mLeDeviceListAdapter.addDevice(device);
                    mLeDeviceListAdapter.notifyDataSetChanged();
                    }
            });
        }
    };</span>

3.建立BluetoothGattService服务

主要用来管理连接服务和数据托管在一个给定的蓝牙LE设备GATT服务器通信。
<span style="font-family:SimSun;">import java.util.List;
import java.util.UUID;

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.os.Binder;
import android.os.IBinder;
import android.util.Log;

/**
 * 用来管理连接服务和数据托管在一个给定的蓝牙LE设备GATT服务器通信
 */
public class BluetoothLeService extends Service {
	private final boolean D = false;
	private final static String TAG = BluetoothLeService.class.getSimpleName();

	private BluetoothManager mBluetoothManager;
	private BluetoothAdapter mBluetoothAdapter;
	private BluetoothGatt mBluetoothGatt;
	private String mBluetoothDeviceAddress;
	private int mConnectionState = STATE_DISCONNECTED;
	public int m_rssi;

	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);

	public final static UUID UUID_ISSC_RX = UUID
			.fromString(SampleGattAttributes.ISSC_CHAR_RX_UUID);

	// 实现GATT的事件回调接口,例如:连接更改和服务发现
	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);
				if (D)
					Log.i(TAG, "Connected to GATT server.");
				// Attempts to discover services after successful connection.
				boolean result = mBluetoothGatt.discoverServices();
				if (D)
					Log.i(TAG, "Attempting to start service discovery:"
							+ result);

			} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
				intentAction = ACTION_GATT_DISCONNECTED;
				mConnectionState = STATE_DISCONNECTED;
				if (D)
					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 {
				if (D)
					Log.e(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);
		}

		@Override
		public void onCharacteristicWrite(BluetoothGatt gatt,
				BluetoothGattCharacteristic characteristic, int status) {
			super.onCharacteristicWrite(gatt, characteristic, status);
		}

		@Override
		public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
			super.onReliableWriteCompleted(gatt, status);
		}

		@Override
		public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
			m_rssi = rssi;
			// Log.e("RSSI", "rssi: " + m_rssi);
			// super.onReadRemoteRssi(gatt, rssi, status);
		}
	};

	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;
				if (D)
					Log.d(TAG, "Heart rate format UINT16.");
			} else {
				format = BluetoothGattCharacteristic.FORMAT_UINT8;
				if (D)
					Log.d(TAG, "Heart rate format UINT8.");
			}
			final int heartRate = characteristic.getIntValue(format, 1);
			if (D)
				Log.d(TAG, String.format("Received heart rate: %d", heartRate));
			// intent.putExtra(EXTRA_DATA, String.valueOf(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) {
				if (Public.b_ascii_mode) {
					intent.putExtra(EXTRA_DATA, new String(data) + "\n");
				} else {
					final StringBuilder stringBuilder = new StringBuilder(
							data.length);
					for (byte byteChar : data) {
						stringBuilder.append(String.format("%02X ", byteChar));
					}
					intent.putExtra(EXTRA_DATA, stringBuilder.toString() + "\n");
				}
			}
		}
		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) {
		// 使用一个给定的设备之后,你应该确保 BluetoothGatt.close()在资源被完全清理后执行的,在这个特定的示例中,
		// close() 应该是UI和服务已经断开的时候被调用
		close();
		return super.onUnbind(intent);
	}

	private final IBinder mBinder = new LocalBinder();

	// 初始化蓝牙适配器
	public boolean initialize() {
		// API 18及以上,就可以通过bluetoothmanager管理蓝牙适配器.
		if (mBluetoothManager == null) {
			mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
			if (mBluetoothManager == null) {
				if (D)
					Log.e(TAG, "Unable to initialize BluetoothManager.");
				return false;
			}
		}

		mBluetoothAdapter = mBluetoothManager.getAdapter();
		if (mBluetoothAdapter == null) {
			if (D)
				Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
			return false;
		}

		return true;
	}

	/**
	 * 连接到托管在BLE设备的GATT服务器。
	 * 
	 * @param adress
	 * 
	 * @return 如果连接成功启动,则返回true。通过回调异步报告连接结果。
	 */
	public boolean connect(final String address) {
		if (mBluetoothAdapter == null || address == null) {
			if (D)
				Log.w(TAG,
						"BluetoothAdapter not initialized or unspecified address.");
			return false;
		}

		if (mBluetoothDeviceAddress != null
				&& address.equals(mBluetoothDeviceAddress)
				&& mBluetoothGatt != null) {
			if (D)
				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) {
			if (D)
				Log.w(TAG, "Device not found.  Unable to connect.");
			return false;
		}

		// 直接连接到设备,所以我们设置自动连接参数为false。
		mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
		if (D)
			Log.d(TAG, "Trying to create a new connection.");
		mBluetoothDeviceAddress = address;
		mConnectionState = STATE_CONNECTING;
		return true;
	}

	public void update_rssi() {
		if (mBluetoothGatt != null) {
			mBluetoothGatt.readRemoteRssi();
		}
	}

	public int get_rssi() {
		return m_rssi;
	}

	/**
	 * Disconnects an existing connection or cancel a pending connection. The
	 * disconnection result is reported asynchronously through the callback.
	 * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
	 * 
	 */
	public void disconnect() {
		if (mBluetoothAdapter == null || mBluetoothGatt == null) {
			if (D)
				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) {
			if (D)
				Log.w(TAG, "BluetoothAdapter not initialized");
			return;
		}
		mBluetoothGatt.readCharacteristic(characteristic);
	}

	public void writeCharacteristic(BluetoothGattCharacteristic characteristic,
			byte[] value) {
		if (mBluetoothAdapter == null || mBluetoothGatt == null) {
			Log.w(TAG, "BluetoothAdapter not initialized");
		}
		final int charaProp = characteristic.getProperties();
		if ((charaProp | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) > 0) {
			characteristic
					.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
		} else {
			characteristic
					.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT);
		}

		characteristic.setValue(value);
		mBluetoothGatt.writeCharacteristic(characteristic);

		// mBluetoothGatt.beginReliableWrite();
		// mBluetoothGatt.executeReliableWrite();
	}

	/**
	 * 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) {
			if (D)
				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);
		}

		if (UUID_ISSC_RX.equals(characteristic.getUuid())) {
			BluetoothGattDescriptor descriptor = characteristic
					.getDescriptor(UUID
							.fromString(SampleGattAttributes.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();
	}
}
</span>

4.绑定service,创建ServiceConnection建立连接,注册BroadcastReceiver来接受蓝牙传回的数据

<span style="font-family:SimSun;">		Intent gattServiceIntent = new Intent(this,BicycleBluetoothService.class);
		bindService(gattServiceIntent, mBicycleServiceConnection,BIND_AUTO_CREATE);
		Public.bicycle_ascii_mode = false;
		mHandler = new Handler();
		registerReceiver(mBicycleGattUpdateReceiverBicycle,makeBicycleGattUpdateIntentFilter());</span>

ServiceConnection和BroadcastReceiver不在此列出来
最后销毁Activity的时候不要忘记解绑service、注销广播。

以上是关于蓝牙4.0的主要内容,如果未能解决你的问题,请参考以下文章

微信(支付宝)小程序蓝牙4.0线上项目

蓝牙4.0的模块可以升级为蓝牙5.0模块吗?

蓝牙4.0所有信道数分别是多少?

Android 4.0 中的蓝牙智能 (4.0) / GATT 支持?

蓝牙 4.0 真的会减少智能手机的电池消耗吗?

蓝牙 4.0+ BLE 吗?