Android串口蓝牙开发实战

最近,正在做关于Android串口蓝牙遥控小车的APP,在此罗列出相关技术细节,用以备忘。

1.蓝牙权限的申请

在AndroidManifest.xml加入以下权限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />

其中”android.permission.BLUETOOTH_PRIVILEGED” 用于蓝牙配对权限;

更改build.gradle(Module:app)中minSdkVersion项改为大于19;

2.检测用户设备是否支持蓝牙功能

mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter==null){
    AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
    builder.setTitle("错误");
    builder.setMessage("该设备不具备蓝牙功能,无法启动应用程序!");
    builder.setCancelable(false);
    builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            ActivityCollector.finishAll();
        }
    });builder.show();
}

其中 ActivityCollector.finishAll();为关闭应用程序;

3.询问用户蓝牙权限

当打开应用程序后,其应用程序会检测当前设备蓝牙功能是否启动。若没有开启,则会弹出窗口询问用户是否开启蓝牙功能;

private static final int REQUEST_ENABLE=1;

if(!mBluetoothAdapter.isEnabled()){
            Intent intent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent,REQUEST_ENABLE);
        }

相关回调函数检测如下:

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode==REQUEST_ENABLE){
            if(resultCode!=RESULT_OK){
                ActivityCollector.finishAll();
            }
        }
    }

检测用户是否开启蓝牙功能,若否则关闭应用程序;

4.蓝牙设备的搜索

蓝牙设备的搜索需要用到几个蓝牙设备的系统广播,如下所示:

BluetoothDevice.ACTION_FOUND              //搜索到新设备
BluetoothAdapter.ACTION_DISCOVERY_FINISHED//搜索完成

注册的广播:

IntentFilter intentFilter=new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
intentFilter.setPriority(Integer.MAX_VALUE);
registerReceiver(mBlueToothReceiver,intentFilter);

相关的广播接收器如下:

 private final BroadcastReceiver mBlueToothReceiver=new BroadcastReceiver() 
 {
 @Override
 public void onReceive(Context context, Intent intent) {
     String action=intent.getAction();
     if(BluetoothDevice.ACTION_FOUND.equals(action))
     {
         BluetoothDevice
         device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
         if(device.getBondState()!=BluetoothDevice.BOND_BONDED){
            //将新设备添加到列表中
            mRecycleList.add(new
            RecycleItems(device.getName(),device.getAddress()));
            madapter.notifyDataSetChanged();
         }
     }
     else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action))
     {
         if(isSearchDev)
         {
             isSearchDev=false;
             msearchDevBtn.setText("搜索设备");
         }
     }
}

启动搜索蓝牙设备代码如下:

if(mBluetoothAdapter.isDiscovering()){
    mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();

5.蓝牙设备的配对

蓝牙设备的配对需要用到的蓝牙设备的系统广播,如下所示:

BluetoothDevice.ACTION_PAIRING_REQUEST

自定义的广播如下:

com.SerialBlueTeeth.PairingDev

主要用于传递用户点击设备列表的位置信息;
将以上两个广播添加到蓝牙设备搜索的广播中,代码如下:

 IntentFilter intentFilter=new IntentFilter();
 intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
 intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
 intentFilter.addAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
 intentFilter.addAction("com.SerialBlueTeeth.PairingDev");
 intentFilter.setPriority(Integer.MAX_VALUE);
 registerReceiver(mBlueToothReceiver,intentFilter);

配对信息对话框代码如下:

madapter.setOnItemClickListener(new DevAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(View view, final int position) {
        if(position>mPairingDevCounter) {
            final AlertDialog.Builder builder = new AlertDialog.Builder(searchDev.this);
            builder.setTitle("提示");
            mEditText=new EditText(builder.getContext());
            builder.setView(mEditText);
            builder.setMessage("请输入设备的PIN码或配对密码(注:配对密码通常为0000或1234)");
            builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {

                }
            });
            builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent=new Intent("com.SerialBlueTeeth.PairingDev");
                    intent.putExtra("position",position);
                    sendBroadcast(intent);
                }
            });
            builder.show();
        }
    }
});

更改后的广播接收器代码如下:

 private final BroadcastReceiver mBlueToothReceiver=new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action=intent.getAction();
        if(BluetoothDevice.ACTION_FOUND.equals(action)){
            BluetoothDevice device=intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            if(device.getBondState()!=BluetoothDevice.BOND_BONDED){
                mRecycleList.add(new RecycleItems(device.getName(),device.getAddress()));
                madapter.notifyDataSetChanged();
            }
        }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)){
            if(isSearchDev){
                isSearchDev=false;
                msearchDevBtn.setText("搜索设备");
            }
        }
        else if("com.SerialBlueTeeth.PairingDev".equals(action))
        {
            int position=intent.getIntExtra("position",0);
            if(position==0){
                return;
            }
            mdevice=mBluetoothAdapter.getRemoteDevice(mRecycleList.get(position).getDevAddr());
            if(mdevice.createBond()==false){
                Toast.makeText(searchDev.this,"配对失败,请重试!",Toast.LENGTH_SHORT).show();
                return;
            }

        }
        else if(BluetoothDevice.ACTION_PAIRING_REQUEST.equals(action))
        {
            mdevice.setPairingConfirmation(true);//确定配对
            mdevice.setPin(mEditText.getText().toString().getBytes());//写入配对密码
        }
    }
};

6.蓝牙设备的连接与通信

特别说明,蓝牙设备的几个状态:
1.查找设备阶段;
2.蓝牙配对阶段;
3.设备连接阶段;
蓝牙配对成功不等于蓝牙设备已连接;
在配对阶段,经行主从机PIN码阶段,手机之间的配对会自动生成特定的UUID,但对于串口蓝牙设备则仅有一组特定的UUID{00001101-0000-1000-8000-00805F9B34FB};

关于蓝牙设备的连接和通信放置在服务中,数据的接受独自放在一个线程中,数据的发送则通过广播进行。具体代码如下:

public class SerialBlueToothService extends Service {
    private static final UUID SerialBlueToothDev_UUID=UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private BluetoothAdapter mBluetoothAdapter=null;
    private BluetoothSocket mBluetoothSocket=null;
    private OutputStream mOutputStream=null;
    private InputStream mInputStream=null;
    private String DevAddr=null;
    private boolean mFlag=true;

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String BlueToothMsg=intent.getStringExtra("BLE_CMD");
            writeSerialBlueTooth(BlueToothMsg);
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();

        try{
        mBluetoothSocket.close();

        }catch (IOException e){
            e.printStackTrace();
        }
        unregisterReceiver(mReceiver);
    }

    public SerialBlueToothService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int onStartCommand(Intent intent,int flags, int startId) {
        DevAddr=intent.getStringExtra("DevAddr");//通过建立Service,Intent传入蓝牙地址

        IntentFilter intentFilter=new IntentFilter();
        intentFilter.addAction("com.example.SerialBLE.SEND_MSG");
        intentFilter.addAction("com.example.SerialBLE.RECEIVE_MSG");
        registerReceiver(mReceiver,intentFilter);

        mBluetoothAdapter=BluetoothAdapter.getDefaultAdapter();
        SerialBlueToothThread thread=new SerialBlueToothThread();
        thread.start();
        return super.onStartCommand(intent, flags, startId);
    }

    private int readSerialBlueTooth(){
        int ret=0;
        byte[] rsp=null;

        if(!mFlag){
            return -1;
        }

        try {
            rsp=new byte[mInputStream.available()];
            ret=mInputStream.read(rsp);
        }catch (IOException e){
            e.printStackTrace();
        }
        return ret;
    }

    private void writeSerialBlueTooth(String msg){
        try{
            mOutputStream.write(msg.getBytes());
            mOutputStream.flush();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    private class SerialBlueToothThread extends Thread{
        @Override
        public void run() {
            super.run();

            try{
                BluetoothDevice device=mBluetoothAdapter.getRemoteDevice(DevAddr);
                mBluetoothSocket=device.createRfcommSocketToServiceRecord(SerialBlueToothDev_UUID);
            }catch (Exception e){
                e.printStackTrace();
                mFlag=false;
            }

            mBluetoothAdapter.cancelDiscovery();

            try{
                mBluetoothSocket.connect();
            }catch (IOException e){
                e.printStackTrace();
                try{
                    mBluetoothSocket.close();
                    mFlag=false;
                }catch (IOException e1){
                    e1.printStackTrace();
                }
            }

            if(mFlag){
                try {
                    mInputStream=mBluetoothSocket.getInputStream();
                    mOutputStream=mBluetoothSocket.getOutputStream();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }

            while(true){
                readSerialBlueTooth();
                try {
                    Thread.sleep(30);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
}

7最后

由于作者知识有限,文章有所纰漏,还望指正。
注:关于蓝牙配对结果如何检测(是否有系统广播可以使用)求告知,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值