Android蓝牙遥控器

这是以前做的一个手机蓝牙遥控器,原本是用来控制一个微型四旋翼的。四旋翼做了第二版后改NRF2401控制了,所以这个程序最终还是没用,下面介绍一下这个程序的关键代码。

连接的对象是一个蓝牙4.0模块,连接上了之后通过串口对飞机进行控制。说一下蓝牙模块的距离,可能因为是用的是蓝牙4.0的缘故,我在走廊里面测试是33米的距离,还是挺远的,足够了。这个程序连接的上蓝牙模块的是有概率失败的,我使用的魅族手机,失败的概率非常高,但是换成的华为手机后,失败概率就小多了,所以这个成功率还是跟手机厂商的优化有关的,但是连接上了还是非常稳定的。

下面介绍程序功能,该程序该程序包括两个Activity,主Activity如下图所示:

主Activity

主Activity是一个遥控界面(按钮是随便拉进去的,各种没对齐-_-),点击蓝牙连接按钮,可以跳到第二个Activity,如下图:

从Activity

这个Activity包括一个ListView,点击开启蓝牙Button查找设备,并把所查找到的设备名称放在ListView里面,点击ListView中要连接的设备,就会跳回主Activity,同时进行蓝牙连接。在屏幕的中间显示连接状态,连接成功后即可传输数据。

总的来说,要使用Android的蓝牙,主要分下面几步:

  • 开启蓝牙
  • 查找设备
  • 蓝牙连接
  • 接收或发送数据

因为开启蓝牙Button在从Activity里面,所以开启蓝牙的操作应该是在从Activity中的,因此需要从主Activity切换到从Activity,这里很简单就省略了。下面是开启蓝牙的代码:

Intent enable = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//3600为蓝牙设备可见时间
enable.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 3600); 
startActivity(enable);

通过Intent开启蓝牙,写到按钮事件中就可以点击按钮开启蓝牙。
开启蓝牙蓝牙之后,就可以调用适配器来查找蓝牙设备了,其代码如下:

private BluetoothAdapter adapter;
private BluetoothReceiver receiver;

adapter = BluetoothAdapter.getDefaultAdapter();//得到默认的蓝牙适配器
if (adapter.getState() != BluetoothAdapter.STATE_ON) {// 如果蓝牙还没开启
    Toast.makeText(MyBlueTooth.this, "请开启蓝牙", Toast.LENGTH_SHORT).show();
}else {
    adapter.startDiscovery();//开始搜索
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    receiver = new BluetoothReceiver();
    registerReceiver(receiver, filter);//注册广播
}

首先通过adtapter查看蓝牙是否开启,如开启则开始搜索,其中adapter.startDiscovery()是一个异步方法,调用后在后台对周围的蓝牙设备进行搜索,然后将搜索结果通过广播的形式发送,所以这里要注册广播,BluetoothReceiver是自定义的一个广播接收器,继承自BroadcastReceiver类,这里定义成了一内部类,代码如下:

private ListView listView; 
private List<String> deviceNames;                                                                                             
private List deviceList; 

listView = (ListView)findViewById(R.id.listView);
deviceNames = new ArrayList<String>();                                                
deviceList = new ArrayList();
//内部类
private class BluetoothReceiver extends 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 (deviceNames.indexOf(device.getName()) == -1) {//列表中没有名字
                deviceNames.add(device.getName());//添加
            }
            deviceList.add(device);
        }
        showDevices();
    }
}
private void showDevices() {
    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,deviceNames);
    listView.setAdapter(adapter);
}

这里涉及到了一些ListView的用法,就不展开了。
前面几步进展地还比较顺利,但到了蓝牙连接这里就出现了很多问题。第一是BluetoothSocket的两种创建方式,一种是通过特定的UUID来创建,我试了很多次连接都成问题,然后查到了第二种方法,通过调用BlueDevice的隐藏方法createRfcommSocket()来创建socket,应用到了Java的反射机制,这种方法只能说能够连上,失败的概率还是很大的(之后测试了其他手机,连接成功率就上去了,可能跟不同的手机厂商的优化有关,问题也可能出在代码本身上)。第二是连接耗时比较长,所以不能放到主线程中,需要新开一个线程进行连接,用Handler接收连接状态,更新UI界面。第三是前面所有的操作都是在从Activity中完成的,但是蓝牙连接的操作需要放到主Activity中,所以要实现主Activity与从Activity 的数据传输。先说Activity间的数据传输问题,我用了Android的Application类来传输,代码如下:

BluetoothDevice device = deviceList.get(position);//获取蓝牙设备
adapter.cancelDiscovery();//取消搜索
bridge.setDate(device);//传递数据
MainActivity.back = true;
//返回操作界面
Intent backIntent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(backIntent);

需要说明的是,在连接之前需要取消搜索,就是调用cancelDiscovery()方法,否则连接会变得异常缓慢。其中的 bridge继承自Application类,用于传递数据,可以看到,这里是把一个BluetoothDevice的实例化对象传递了过去。然后在主Activity中接收该对象:

public static boolean back = false;
private BluetoothConnect client;
//onCreate()
if(back){//返回后开始读数据
    device = bridge.getDate();
    client = new BluetoothConnect(device,handler);
    client.connect();//开始连接
}

可以看到,这里蓝牙设备的获取是在从Activity中,而创建连接是在主Activity中。在主Activity中接收到了device,通过device就可以创建socket进行连接了,这个过程我封装到一个BluetoothConnect类中,方便以后调用,下面是其中的connect()方法:

private BluetoothSocket socket;

public void connect(){
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            Method method;
            state = CONNECT_SUCCESS;
            try {
                //利用反射机制创建socket
                method = device.getClass().getMethod("createRfcommSocket", new Class[]{int.class});
                socket = (BluetoothSocket) method.invoke(device, 1);
                socket.connect();                                                                                                                               
                isConnect = ture;
            } catch (IOException e) {
                state = CONNECT_FAILED;
                Log.e("TAG", e.toString());
            } catch (InvocationTargetException e) {
                state = CONNECT_FAILED;
                Log.e("TAG", e.toString());
            } catch (NoSuchMethodException e) {
                state = CONNECT_FAILED;
                Log.e("TAG", e.toString());
            } catch (IllegalAccessException e) {
                state = CONNECT_FAILED;
                Log.e("TAG", e.toString());
            }
            //发送连接状态
            Message msg = handler.obtainMessage();
            msg.what = state;
            handler.sendMessage(msg);
        }
    });
thread.start();
}

因为连接过程必须放到子线程中,因此这是一个异步方法,该方法中通过调用createRfcommSocket()方法创建socket,然后向主Activity中的Handler发送消息,需要用到消息机制,主线程接收消息并更新UI,即在屏幕的中间显示连接状态。接下来代码是主Activity中的Handler,作用是更新UI:

private final Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BluetoothConnect.CONNECT_FAILED:
                Toast.makeText(MainActivity.this, "连接失败", Toast.LENGTH_SHORT).show();
                break;
            case BluetoothConnect.CONNECT_SUCCESS:
                Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
                client.receive();//接收数据
                break;
            case BluetoothConnect.READ_FAILED:
                Toast.makeText(MainActivity.this, "读取失败", Toast.LENGTH_SHORT).show();
                break;
            case BluetoothConnect.WRITE_FAILED:
                Toast.makeText(MainActivity.this, "写入失败", Toast.LENGTH_SHORT).show();
                break;
            case BluetoothConnect.DATA:
                Toast.makeText(MainActivity.this, ""+msg.arg1, Toast.LENGTH_SHORT).show();
                break;
        }
    }
};

可以在此Handler中用TextView等来显示连接状态,这里简单起见就用只用了Toast。可以看到这里有一个receive()方法,这个方法也是封装到BluetoothConnect中的一个异步方法,该方法如下:

public void receive(){
    //连接成功则接收数据
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            if(isConnect){
                try {
                    InputStream inputStream = socket.getInputStream();
                    int data;
                    while (true){
                        try {
                            data = inputStream.read();
                            Message msg = handler.obtainMessage();
                            msg.what = BluetoothConnect.DATA;
                            msg.arg1 = data;
                            handler.sendMessage(msg);
                        }catch (IOException e){
                            Log.e("TAG", e.toString());
                            break;
                        }
                    }
                } catch (IOException e) {
                    Log.e("TAG", e.toString());
                }
            }
        }
    });
    thread.start();

主要是通过不断读取输入流的内容来接收数据,可以看到,这里又利用了消息机制,将接收到的数据通过消息打包发回了主线程,在主线程的Handle中接收消息即可更新UI,显示所接收的内容。
最后不要忘了加上相关的蓝牙权限。

 <uses-permission android:name="android.permission.BLUETOOTH"/>
 <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

本文参考来源:http://www.cnblogs.com/wenjiang/p/3200138.html#!comments

### 关于Android平台上蓝牙遥控器的实现 #### 一、开发准备 为了在Android设备上创建一个能够作为蓝牙遥控器的应用程序,开发者需要熟悉`BluetoothAdapter`, `BluetoothDevice`以及`BluetoothSocket`类。这些API允许应用程序发现其他蓝牙设备并建立RFCOMM通道用于数据传输[^1]。 #### 二、构建简单的蓝牙通信应用 基于官方文档中的`BluetoothChat`案例,这是一个很好的起点来理解基本概念和技术细节。此项目展示了怎样设置客户端和服务端之间的连接,并通过SPP协议交换消息。 ```java // 获取默认的 BluetoothAdapter 实例 private final BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 创建 UUID 以便唯一标识服务 public static final UUID MY_UUID_SECURE = UUID.fromString("fa87c0d0-8a39-0800200c9a66"); // 连接到指定地址的远程设备 public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final. BluetoothSocket tmp = null; try { // Get a BluetoothSocket to connect with the given BluetoothDevice. tmp = device.createRfcommSocketToServiceRecord(MY_UUID_SECURE); } catch (IOException e) { } mmSocket = tmp; } ``` 上述代码片段来自`BluetoothChat`示例,它说明了如何初始化蓝牙适配器和尝试打开到另一台设备的安全RFComm套接字链接。 #### 三、处理输入事件 当涉及到实际操作如接收按钮按压时,则需关注从硬件层面捕获原始信号直到最终转换成可用于应用程序级别的标准化事件的过程。对于蓝牙HID设备而言,这一路径涉及多个层次的数据映射与解释,具体来说是从物理键值经过一系列变换直至成为安卓系统认可的标准键位编码[^4]。 #### 四、现有解决方案 除了自行编写完整的应用程序之外,市场上也存在一些成熟的第三方工具可以帮助简化蓝牙串口通讯的任务。“蓝牙串口调试助手”就是一个例子,这类软件通常提供了图形界面让用户更容易配置参数并与外部装置交互而不必深入研究底层编程接口[^3]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值