Android App创建Ble服务

先写一个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为我们自定义的特征

 

 

该功能是去年完成 ,转载请说明出处。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值