先写一个Ble服务 AdvertiserService 代码如下
/**
* Created by lxy on 2022/04/26.
*/
public class AdvertiserService extends Service {
// private RecvDelegate delegate;
private static final String TAG="AdvertiserService";
String CHANNEL_ONE_ID = "com.shanling.com";
// private static final int FOREGROUND_NOTIFICATION_ID=1;
public static BluetoothDevice mDevice;
private static final String NOTIF_ID = "com.shanling.bleService"; //这里的id里面输入自己的项目的包的路径
private static final String NOTIF_NAME = "Channel_One";
public static boolean running=false;
public static final String ADVERTISENG_FILED="com.shanLing.Lxy.advertising_failed";
public static final String ADVERTISING_FAILED_EXTRA_CODE="failureCode";
// public static final int ADVERTISING_TIMED_OUT=6;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private AdvertiseCallback mAdertiseCallback;
// private Handler mHandler;
// private Runnable timeoutRunnable;
// private long TIMEOUT= TimeUnit.MILLISECONDS.convert(10,TimeUnit.MINUTES);
public static BluetoothGattServer mBluetoothGattServer;
BluetoothManager mBluetoothManager;
public static BluetoothGattCharacteristic characteristicNotif;
private static UUID UUID_SERVER = UUID.fromString("0000d33c-0000-1000-8000-00805f9b34fb");
private static UUID UUID_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
private static UUID UUID_CHARNOTIF = UUID.fromString("0000D351-0000-1000-8000-00805f9b34fb");
// private static UUID UUID_CHARREAD = UUID.fromString("0000D352-0000-1000-8000-00805f9b34fb");
private static UUID UUID_CHARWRITE = UUID.fromString("0000D353-0000-1000-8000-00805f9b34fb");
//D33C服务的UUID
public static final ParcelUuid Service_UUID=ParcelUuid.fromString("0000d33c-0000-1000-8000-00805f9b34fb");
private byte[] rData;
private int len = 0;
@Override
public void onCreate(){
running=true;
initialize();
startAdvertising();
setTimeout();
super.onCreate();
}
@Override
public void onDestroy(){
stopAdvertising();
running=false;
// mHandler.removeCallbacks(timeoutRunnable);
// stopForeground(true);
//防止在系统蓝牙切换后无法启动D33C的服务特征 从而无法进行Ble交互
mBluetoothGattServer = null;
rData = null;
len = 0;
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private void initialize(){
if(mBluetoothLeAdvertiser==null){
mBluetoothManager=(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
if(mBluetoothManager!=null){
BluetoothAdapter bluetoothAdapter=mBluetoothManager.getAdapter();
if(bluetoothAdapter!=null){
mBluetoothLeAdvertiser=bluetoothAdapter.getBluetoothLeAdvertiser();
}else{
Toast.makeText(this,"设备不支持蓝牙广播",Toast.LENGTH_SHORT).show();
}
}else{
Toast.makeText(this,"不支持蓝牙",Toast.LENGTH_SHORT).show();
}
}
}
private void setTimeout(){
//服务开启后不设定时间关闭
// mHandler=new Handler();
// timeoutRunnable=new Runnable(){
// @Override
// public void run(){
// Log.d(TAG,"广播服务已经运行"+TIMEOUT+"秒,停止停止广播");
// sendFailureIntent(ADVERTISING_TIMED_OUT);
// stopSelf();
// }
// };
// mHandler.postDelayed(timeoutRunnable,TIMEOUT);
}
private void startAdvertising(){
// goForeground();
// Log.d(TAG,"服务开始广播");
if(mAdertiseCallback==null){
AdvertiseSettings settings=buildAdvertiseSettings();
AdvertiseData data = buildAdvertiseData();
AdvertiseData aa = buildScanResponseData();
mAdertiseCallback=new SampleAdvertiseCallback();
if(mBluetoothLeAdvertiser!=null){
//这种方式发送可以广播62个字节
//还有个方式发送广播包 ,不添加扫描响应包参数,最多只能发送31个字节
mBluetoothLeAdvertiser.startAdvertising(settings,data,aa,mAdertiseCallback);
}
}
}
private void stopAdvertising(){
// Log.d(TAG,"服务停止广播");
if(mBluetoothLeAdvertiser!=null){
if (mDevice!=null){
SynclinkLogic.getInstance().getChatService().sendDataToClient(new byte[0],SynclinkConstants.SL_LOGOUT_RESP);
SystemClock.sleep(20);//休眠20毫秒
}
mBluetoothLeAdvertiser.stopAdvertising(mAdertiseCallback);
// startAdvertising();//开始广播
// mAdertiseCallback=null; //lxy 这个必须要屏蔽 否则会出现多个读写特征 屏蔽的话 就只有一个
}
}
/*创建广播包数据*/
private AdvertiseData buildAdvertiseData(){
AdvertiseData.Builder dataBuilder=new AdvertiseData.Builder();
dataBuilder.addServiceUuid(Service_UUID);//服务ID D33C
// dataBuilder.setIncludeTxPowerLevel(true); //是否广播信号强度
return dataBuilder.build();
}
/*扫描响应包数据*/
private AdvertiseData buildScanResponseData(){
AdvertiseData.Builder mScanResponseData = new AdvertiseData.Builder()
.setIncludeDeviceName(true);//是否显示设备名称
String aa= NetUtils.getBluetoothAddress();
String bb = aa.replace(":", "");
byte[] mm = stringToBytes(bb);
if (mm!=null && mm.length == 6){
byte[] a = new byte[2];
byte[] b = new byte[4];
System.arraycopy(mm,0,a,0, 2);
int c = ((a[0] & 0xFF) << 8)+ (a[1]& 0xfff);
System.arraycopy(mm,2,b,0, 4);
mScanResponseData.addManufacturerData(c,b); //添加厂商信息
}
return mScanResponseData.build();
}
private AdvertiseSettings buildAdvertiseSettings(){
AdvertiseSettings.Builder settingsBuilder=new AdvertiseSettings.Builder();
settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER);
settingsBuilder.setTimeout(0);
return settingsBuilder.build();
}
private class SampleAdvertiseCallback extends AdvertiseCallback{
@Override
public void onStartFailure(int errorCode){
super.onStartFailure(errorCode);
// Log.d(TAG,"广播失败");
sendFailureIntent(errorCode);
stopSelf();
}
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect){
super.onStartSuccess(settingsInEffect);
// Log.d(TAG,"服务端的广播成功开启");
// Log.d(TAG,"BLE服务的广播启动成功后:TxPowerLv="+settingsInEffect.getTxPowerLevel()+";mode="+settingsInEffect.getMode()+";timeout="+settingsInEffect.getTimeout());
initServices(getContext());//该方法是添加一个服务,在此处调用即将服务广播出去
}
}
private void sendFailureIntent(int errorCode){
Intent failureIntent=new Intent();
failureIntent.setAction(ADVERTISENG_FILED);
failureIntent.putExtra(ADVERTISING_FAILED_EXTRA_CODE,errorCode);
sendBroadcast(failureIntent);
}
//添加一个服务,该服务有一个读特征、该特征有一个描述;一个写特征。
//用BluetoothGattServer添加服务,并实现该类的回调接口
private void initServices(Context context){
if (mBluetoothGattServer!=null){//防止出现多个服务和特征
return;
}
mBluetoothGattServer = mBluetoothManager.openGattServer(context,bluetoothGattServerCallback);
BluetoothGattService service=new BluetoothGattService(UUID_SERVER,BluetoothGattService.SERVICE_TYPE_PRIMARY);
// characteristicRead=new BluetoothGattCharacteristic(UUID_CHARREAD,BluetoothGattCharacteristic.PROPERTY_READ,BluetoothGattCharacteristic.PERMISSION_READ);
// BluetoothGattDescriptor descriptor=new BluetoothGattDescriptor(UUID_DESCRIPTOR,BluetoothGattCharacteristic.PERMISSION_WRITE);
// characteristicRead.addDescriptor(descriptor);
// service.addCharacteristic(characteristicRead);
BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHARWRITE,
BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
service.addCharacteristic(characteristicWrite);
characteristicNotif = new BluetoothGattCharacteristic(UUID_CHARNOTIF,
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PROPERTY_NOTIFY);
BluetoothGattDescriptor descriptor=new BluetoothGattDescriptor(UUID_DESCRIPTOR,BluetoothGattCharacteristic.PROPERTY_NOTIFY);
characteristicNotif.addDescriptor(descriptor);
service.addCharacteristic(characteristicNotif);
mBluetoothGattServer.addService(service);
// Log.d(TAG,"初始化服务成功:initServices ok");
}
//服务事件的回调
private BluetoothGattServerCallback bluetoothGattServerCallback=new BluetoothGattServerCallback() {
//1、首先是连接状态的回调
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
if (newState == 2) {
mDevice = device;
// Log.e(TAG, "连接状态成功 "+ "status=" + status + "newstate=" + newState);
} else {
mDevice = null;
SynclinkLogic.getInstance().setSyncLinkServerConnection(false);
// Log.e(TAG, "连接状态失败 "+ "status=" + status + "newstate=" + newState);
}
}
// @Override
// public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
// super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
// Log.e(TAG,"客户端有读的请求,安卓系统回调该onCharacteristicReadRequest()方法");
//
// mBluetoothGattServer.sendResponse(device,requestId, BluetoothGatt.GATT_SUCCESS,offset,characteristic.getValue());
// }
//接受具体字节,当有特征被写入时,回调该方法,写入的数据为参数中的value
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
// Log.e(TAG,"客户端有写入Id="+requestId+"写入的字节长度Length="+value.length);
/**
* 该方法必须调用 否则会半分钟断开连接
* 特征被读取,在该回调方法中回复客户端响应成功
*/
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,offset,value);
//处理响应内容 value:客户端发送过来的数据
onResponseToClient(value,device,requestId,characteristic);
// if (running) { //连接成功后关闭关闭
// if(mBluetoothLeAdvertiser!=null){
// mBluetoothLeAdvertiser.stopAdvertising(mAdertiseCallback);
// }
// }
}
//特征被读取。当回复相应成功后,客户端胡读取然后触发本方法
@Override
public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
super.onDescriptorReadRequest(device, requestId, offset, descriptor);
// Log.e(TAG, "onDescriptorReadRequest: "+"特征被读取。当回复相应成功后,客户端胡读取然后触发本方法" );
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,offset,null);
}
//2、其次,当有描述请求被写入时,回调该方法,
@Override
public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
// Log.e(TAG, "onDescriptorWriteRequest: "+"其次,当有描述请求被写入时,回调该方法,");
mBluetoothGattServer.sendResponse(device,requestId,BluetoothGatt.GATT_SUCCESS,offset,value);
}
//通知
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
// Log.e(TAG, "onNotificationSent: 通知 状态status = "+status );
}
@Override
public void onServiceAdded(int status,BluetoothGattService service){
super.onServiceAdded(status,service);
// Log.e(TAG,"添加服务成功,安卓系统回调该onServiceAdded()方法");
}
};
//4.处理相应内容,requestBytes是客户端发送过来的数据
private void onResponseToClient(byte[] data,BluetoothDevice device,int requestId,BluetoothGattCharacteristic characteristic){
// //在服务端接受数据
// if (SynclinkLogic.getInstance()!=null &&SynclinkLogic.getInstance().getChatService()!=null ) //SynclinkLogic.getInstance().getChatService().parseDataForIOS(data);
if (SynclinkLogic.getInstance()!=null &&SynclinkLogic.getInstance().getChatService()!=null ){
if (rData!=null && rData.length>= 12){
System.arraycopy(data,0, rData, 180, data.length);
SynclinkLogic.getInstance().getChatService().parseDataForIOS(rData);
rData = null;
len = 0;
}else {
if (data.length>=12){
byte[] lenBytes = new byte[]{data[8], data[9], data[10], data[11]};
len = ConvertUtils.bytes2Int(lenBytes, lenBytes.length);
rData = new byte[len+12]; //12 168
System.arraycopy(data, 0, rData, 0, data.length);
}
if (len<=168 && len>=0) {
SynclinkLogic.getInstance().getChatService().parseDataForIOS(rData);
rData = null;
len = 0;
}
}
}
}
public Context getContext(){
return this;
}
/**
* String转byte[]
* @return byte[]
*/
public static byte[] stringToBytes(String src) {
int m=0,n=0;
int l=src.length()/2;
System.out.println(l);
byte[] ret = new byte[l];
for (int i = 0; i < l; i++){
m=i*2+1;
n=m+1;
int aa = Integer.decode("0x" + src.substring(i*2, m) + src.substring(m,n));
ret[i] = (byte) aa;
}
return ret;
}
App只关心D33C服务,在D33C服务下存在 无回应写(D353) 和 通知(D351) 两个特征
创建完后再Manifest注册服务
<service
android:name=".Synclink.AdvertiserService"
android:enabled="true"
android:exported="false" />
注册完在需要用到的地方注册服务回调(回调可以省略不写,但是广播出错时,可是添加服务回调,查看错误信息)
//by lxy
private BroadcastReceiver advertisingFailureReceiver;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
baseSetContentView(R.layout.sync_link_activity);
ButterKnife.bind(this);
init();
advertisingFailureReceiver=new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int errorCode=intent.getIntExtra(AdvertiserService.ADVERTISING_FAILED_EXTRA_CODE,-1);
setSyncLinkServer(false);
String errorMessage="广告启动失败类型:";
switch (errorCode){
case AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED:
errorMessage+=" 已经开始";
break;
case AdvertiseCallback.ADVERTISE_FAILED_DATA_TOO_LARGE:
errorMessage+=" 数据包太长";
break;
case AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED:
errorMessage+=" 设备不支持广告";
break;
case AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR:
errorMessage+=" 整形错误";
break;
case AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS:
errorMessage+=" 太多广告";
break;
// case AdvertiserService.ADVERTISING_TIMED_OUT:
// errorMessage+=" 广告超时";
// break;
default:
errorMessage+=" 未知错误";
}
Log.e("TAG", "onReceive: "+errorMessage );
// Toast.makeText(SynclinkActivity.this,errorMessage,Toast.LENGTH_SHORT).show();
}
};
}
在onResume和onPause中进行注册和取消注册
//by lxy add
@Override
protected void onResume() {
super.onResume();
IntentFilter failureFilter=new IntentFilter(AdvertiserService.ADVERTISENG_FILED);
registerReceiver(advertisingFailureReceiver,failureFilter);
}
@Override
public void onPause(){
super.onPause();
unregisterReceiver(advertisingFailureReceiver);
}
创建单例 声明属性 并实现/关闭Ble服务
private Intent bleServiceIntent;
实例化该属性
bleServiceIntent = new Intent(mContext,AdvertiserService.class);
开启/关闭Ble服务
//by lxy 停止ble服务
if (AdvertiserService.running) {
mContext.stopService(bleServiceIntent);
}
//开启Ble服务
if (!AdvertiserService.running)
mContext.startService(bleServiceIntent);
到此功能完成
服务开启后可通过iPhone上的LightBlue App查看ble设备(没有LightBlue可在App Store下载)。通过LightBlue也可查看发出的广播包以及服务特征,如图:其中UUID为AA15的为该手机自身携带的特征。D33C为我们自定义的特征
该功能是去年完成 ,转载请说明出处。