安卓蓝牙开发——综合实训工作总结

前言

大二暑期参加了学校组织的综合实训,选择的课题是机器人儿童教育,其主要任务是做一个app,实现蓝牙连接机器人并通过蓝牙发送指令使机器人行动的功能。目前综合实训时间过半,我们组已经实现了蓝牙连接的基本功能,能够手机互连蓝牙并发送消息。在此对前半段小组遇到的关键知识点和坑点进行总结。

权限

使用蓝牙首先要在清单文件中添加权限。在AndroidManifest.xml中添加:

    
    

因为目前手机都是安卓6.0以上的版本,还需要获得位置权限


这里有个小坑,对于写好的app,可能部分机型需要给appGPS定位的权限,不然可能无法使用app的搜索蓝牙功能。我的组员使用小米手机就遇到了这个坑。

查看是否支持蓝牙设备

使用BluetoothAdapter,如果手机能够支持蓝牙,会返回一个BluetoothAdapter的对象

BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null){
    //Device does not support Bluetooth
}

打开蓝牙

用自动的.enable()方法

public void requestEnableBt() {
if (!mBluetoothAdapter.isEnabled())
//开启蓝牙
mBluetoothAdapter.enable();
}

但是更建议用手动的办法

Intent enableIntent = new Intent(
        BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent,REQUEST_ENABLE_BT);

启动设备可见性

蓝牙为了安全性,在设计的时候有一个可见性,如果蓝牙不可见,那么是连不上的。这个时候我们必须将设备设为可见。
让设备在300s内设为可见的

Intent discoverableIntent = new Intent(BluetoothAdapter  ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdpter EXTRA_DISCOVERABLE_DURATION,300);
startActivity(discoverableIntent);

搜索蓝牙设备

适配器搜索蓝牙设备后将结果以广播形式传出去,所以蓝牙搜索主要是要实现一个BroadcastReceiver 广播接收者。在onReceive方法中获得并处理蓝牙设备的搜索结果。
关键代码如下:

private final BroadcastReceiver mReceiver = 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){
                mNewDevicesArrayAdapter.add(device.getName()+"\n" +
                        device.getAddress());
            }
        }else if(BluetoothAdapter.ACTION_DISCOVERY_FINISHED
                .equals(action)){
            progressBar.setVisibility(View.GONE);
            Toast.makeText(DeviceList.this,"搜索完毕",Toast.LENGTH_SHORT).show();
            if(mNewDevicesArrayAdapter.getCount()==0){
                String noDevices=getResources().getText(
                        R.string.none_found).toString();
                mNewDevicesArrayAdapter.add(noDevices);
            }
        }
    }
};

蓝牙连接功能

这个时候就需要用到线程了,java学得好的同学肯定上手得特快。反正我在这里卡了很久,找了很多资料,学习了很多大佬们的代码,这里要感谢网上的大佬们。按我自己的理解,蓝牙连接和java中基于Socket套接字的低层次Java网络编程类似。这里也用到三个线程,分别是AcceptThread 监听线程、ConnectThread 连接线程和ConnectedThread管理线程。

监听线程

监听线程就是被动等待连接的线程,调用了BluetoothServerSocket.accept()方法

    private class AcceptThread extends Thread{
        private final BluetoothServerSocket mmServerSocket;

        public AcceptThread(){
            BluetoothServerSocket tmp = null;
            try{
                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,MY_UUID);
            }catch (IOException e){}
            mmServerSocket = tmp;
        }


        public void run(){
        BluetoothSocket socket= null;
        while(mState != STATE_CONNECTED){
            try{
                socket = mmServerSocket.accept();
            }catch (IOException e) {
                break;
            }
            if(socket != null){
                connected(socket,socket.getRemoteDevice());
                try{
                    mmServerSocket.close();
                }catch (IOException e){}
            }
        }
    }
    
        public void cancel(){
            try{
                mmServerSocket.close();
            }catch (IOException e){}
        }
}

连接线程

连接线程的作用是对外发出连接蓝牙的请求

private class ConnectThread extends Thread{
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device){
        mmDevice=device;
        BluetoothSocket tmp = null;

        try{
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        }catch (IOException e){}
        mmSocket = tmp;
    }

    public void run(){

        mAdapter.cancelDiscovery();
        try{
            mmSocket.connect();
        }catch (IOException e){
            connectionFailed();
            try{
                mmSocket.close();
            }catch (IOException e2){}
           return;
       }
        synchronized(ChatService.this){
            mConnectedThread = null;
       }
        connected(mmSocket,mmDevice);
    }

    public void cancel(){
        try{
            mmSocket.close();
        }catch (IOException e){}
    }
}

管理线程

管理线程的作用是管理连接上的双方。也起到数据传输的作用

private class ConnectedThread extends Thread{
    private final BluetoothSocket mmSocket;
    private final InputStream mmInStream;
    private final OutputStream mmOutStream;

    public ConnectedThread(BluetoothSocket socket){
        mmSocket = socket;
        InputStream tmpIn = null;
        OutputStream tmpOut=null;
        try{
            tmpIn=mmSocket.getInputStream();
            tmpOut=mmSocket.getOutputStream();
        }catch (IOException e){}

        mmInStream = tmpIn;
        mmOutStream = tmpOut;
    }

    public void run(){
        byte[]buffer=new byte[1024];
        int bytes;
        while (true){
            try{
                bytes = mmInStream.read(buffer);
                mHandler.obtainMessage(MainActivity.MESSAGE_READ,bytes,-1,buffer).sendToTarget();
            }catch (IOException e){
                connectionLost();
                break;
            }
        }
    }

    public void write(byte[]buffer){
        try{
            mmOutStream.write(buffer);
        }catch (IOException e){
            Log.d("MainActivity","Send Fail");
        }
        mHandler.obtainMessage(MainActivity.MESSAGE_WRITE,buffer).sendToTarget();
    }

    public void cancel(){
        try{
            mmSocket.close();
        }catch (IOException e){}
    }
}

一些学习中的“坑点”

1.现在安卓版本大多数比较高,在建工程的时候最低API要选择19
安卓蓝牙开发——综合实训工作总结_第1张图片
2.在学习过程中我们时常要借鉴别人大佬优秀的代码,而有的时候下载大佬的代码包在自己AS上运行的时候容易出现这样的错误:
WARNING: The specified Android SDK Build Tools version (27.0.3) is ignored, as it is below the minimum supported version (28.0.3) for Android Gradle Plugin 3.4.1.
Android SDK Build Tools 28.0.3 will be used.
To suppress this warning, remove “buildToolsVersion ‘27.0.3’” from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.
Remove Build Tools version and sync project
Affected Modules: app
这个错误的原因是因为版本号和API不匹配,这个时候只需要在Tools/SDK Manager 下载对应的API,然后在build.gradle(Module:app)中的buildToolsVersion '27.0.3’修改对应的版本号就能解决啦。
安卓蓝牙开发——综合实训工作总结_第2张图片
3.我在运行代码的过程中有时候出现app闪退的现象,我总结下主要原因可能如下:
有些API在老版本中有,在新版本中没有,造成对象为空引起闪退;
页面布局问题。
4.在代码下载到手机上时,需要把Settings里面Instant Run中的第一个框取消勾选。
安卓蓝牙开发——综合实训工作总结_第3张图片

目前遇到的坑点只想到这些,以后还会慢慢总结

注:此文章目的只用于自己学习总结,如有错误的地方还请指正!

你可能感兴趣的:(安卓蓝牙开发——综合实训工作总结)