Android开发之蓝牙详解(一)

来源:互联网 发布:淘宝e客服在哪里 编辑:程序博客网 时间:2024/05/16 18:09

一.概述

这篇文章是我学习Android开发官网以及网上一些其他文章总结而来,主要就是为了好好研究一下蓝牙开发,看完这篇文章以后,我们就知道了怎样使用蓝牙API完成建立蓝牙连接的必要四步:1.打开蓝牙;2.查找附近已配对或可用的设备;3.连接设备;4.设备间数据交换。由于文章比较长,为了方便大家的学习,所以将文章分为三篇,这是第一篇。

二.基础

1.API

所有的蓝牙API都在android.bluetooth 包下.下面有一些类和接口的摘要,我们需要它们来建立蓝牙连接:
BluetoothAdapter
代表本地蓝牙适配器(蓝牙无线)。BluetoothAdapter是所有蓝牙交互的入口。使用这个类,你能够发现其他的蓝牙设备,查询已配对设备的列表,使用已知的MAC地址来实例化一个BluetoothDevice对象,并且创建一个BluetoothServerSocket对象来监听与其他设备的通信。
BluetoothDevice
代表一个远程的蓝牙设备。使用这个类通过BluetoothSocket或查询诸如名称、地址、类和配对状态等设备信息来请求跟远程设备的连接。
BluetoothSocket
代表蓝牙socket的接口(类似TCP的Socket)。这是允许一个应用程序跟另一个蓝牙设备通过输入流和输出流进行数据交换的连接点。
BluetoothServerSocket
代表一个打开的监听传入请求的服务接口(类似于TCP的ServerSocket)。为了连接两个Android设备,一个设备必须用这个类打开一个服务接口。当远程蓝牙设备请求跟本设备建立连接请求时,BluetoothServerSocket会在连接被接收时返回一个被连接的BluetoothSocket对象。
BluetoothClass
描述了蓝牙设备的一般性特征和功能。这个类是一个只读的属性集,这些属性定义了设备的主要和次要设备类和服务。但是,这个类并不保证描述了设备所支持的所有的蓝牙配置和服务,但是这种对设备类型的提示是有益的
BluetoothProfile
代表一个蓝牙配置的接口。蓝牙配置是基于蓝牙通信的设备间的无线接口规范。一个例子是免提的配置。更多的配置讨论,请看下文的用配置来工作。
BluetoothHeadset
提供对使用蓝牙耳机的移动电话的支持。它同时包含了Bluetooth Headset和Hands-Free(v1.5)的配置。
BluetoothA2dp
定义如何把高品质的音频通过蓝牙连接从一个设备流向另一个设备。“A2DP”是Advanced Audio Distribution Profile的缩写。
BluetoothHealth
代表一个健康保健设备配置的控制蓝牙服务的代理。
BluetoothHealthCallback
用于实现BluetoothHealth回调的抽象类。你必须继承这个类,并实现它的回调方法,来接收应用程序的注册状态和蓝牙通道状态变化的更新。
BluetoothHealthAppConfiguration
代表蓝牙相关的第三方健康保健应用程序所注册的与远程蓝牙健康保健设备进行通信的配置。
BluetoothProfile.ServiceListener
BluetoothProfile IPC客户端连接或断开服务的通知接口(它是运行特俗配置的内部服务)。

2.权限
为了在你的应用程序中使用蓝牙功能,至少要声明两个蓝牙权限(BLUETOOTH和BLUETOOTH_ADMIN)中的一个。
为了执行任何蓝牙通信(如请求连接、接收连接和传输数据),你必须申请BLUETOOTH权限。
为了启动设备发现或维护蓝牙设置,你必须申请BLUETOOTH_ADMIN权限。大多数需要这个权限的应用程序,仅仅是为能够发现本地的蓝牙设备。这个权限所授予的其他能力应该不被使用,除非是电源管理的应用程序,它会在依据用户的请求来修改蓝牙设置。注意:如果你使用了BLUETOOTH_ADMIN权限,那么必须要有BLUETOOTH权限。
在清单文件中声明如下权限:

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

3.设置蓝牙
在应用程序能够利用蓝牙通道通信之前,需要确认设备是否支持蓝牙通信,如果支持,要确保它是可用的。
如果不支持蓝牙,那么你应该有好的禁用所有蓝牙功能。如果支持蓝牙,但是被禁用的,那么你要在不离开你的应用程序的情况下,请求用户启用蓝牙功能,这种设置要使用BluetoothAdapter对象,在以下两个步骤中完成。
(1)获得BluetoothAdapter对象
BluetoothAdapter对象是所有蓝牙活动都需要的,要获得这个对象,就要调用静态的getDefaultAdapter()方法。这个方法会返回一个代表设备自己的蓝牙适配器的BluetoothAdapter对象。整个系统有一个蓝牙适配器,你的应用程序能够使用这个对象来进行交互。如果getDefaultAdapter()方法返回null,那么该设备不支持蓝牙,你的处理也要在此结束。例如:

BluetoothAdapter mBluetoothAdapter =BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter ==null){// 设备不支持蓝牙}

(2)启用蓝牙功能
接下来,你需要确保蓝牙是可用的。调用isEnabled()方法来检查当前蓝牙是否可用。如果这个方法返回false,那么蓝牙是被禁用的。要申请启用蓝牙功能,就要调用带有ACTION_REQUEST_ENABLE操作意图的startActivityForResult()方法。它会给系统设置发一个启用蓝牙功能的请求(不终止你的应用程序)。例如:

 if(!mAdapter.isEnabled()) {            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);            activity.startActivityForResult(intent, requestCode);        }

这时会显示一个请求用户启用蓝牙功能的对话框
这里写图片描述

如果用户点击允许,那么系统会开始启用蓝牙功能,完成启动过程(有可能失败),焦点会返回给你的应用程序。传递给startActivityForResult()方法的requestCode是一个你的应用程序中定义的整数(它必须大于0),系统会把它作为参数返回到你的onActivityResult()回调实现中。
如果蓝牙功能启用成功,你的Activity会在onActivityResult()回调中接收到RESULT_OK结果,如果蓝牙没有被启动(或者用户响应了拒绝),那么该结果编码是RESULT_CANCELED。
可选地,你的应用程序还可以监听ACTION_STATE_CHANGED广播Intent,无论蓝牙状态何时改变,系统都会广播这个Intent。这个广播包含的附加字段EXTRA_STATE和EXTRA_PREVIOUS_STATE中分别指明了新的和旧的蓝牙状态。这些附加字段中可能的值是:STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF和STATE_OFF。监听这个广播对于在应用程序运行时检测蓝牙的状态是有用的。
提示:启用可发现能力会自动启动蓝牙功能。如果你计划在执行蓝牙活动之前,要始终启用设备的可发现机制,就可以跳过上面的步骤,详细请参考下文“启用蓝牙可发现”。

到此,我们先给出一个例子,让大家理解一下:
这里写图片描述

我们设置了四个按钮,分别用于判断是否支持蓝牙,蓝牙是否开启,开启蓝牙,关闭蓝牙,由于使用的是模拟器,所以我们可以看到实验结果是当前设备不支持蓝牙,并且蓝牙未开启,点击后面两个按钮没反应,如果是在真是设备上,我们可以进行开启蓝牙和关闭蓝牙的操作,大家下来可以试一试,下面给出代码:

/** * Created by lxn on 2016/3/4. * 蓝牙管理类 */public class BluetoothController {    private BluetoothAdapter mAdapter;    public BluetoothController(){        mAdapter = BluetoothAdapter.getDefaultAdapter();    }    /**     * 判断当前设备是否支持蓝牙     * @return     */    public boolean isSupportBluetooth(){        if(mAdapter!=null){            return true;        }        return false;    }    /**     * 获取蓝牙的状态     * @return     */    public boolean getBluetoothStatus(){        if(mAdapter!=null){            return mAdapter.isEnabled();        }        return false;    }    /**     * 打开蓝牙     * @param activity     * @param requestCode     */    public void turnOnBluetooth(Activity activity,int requestCode){        if(mAdapter!=null&&!mAdapter.isEnabled()) {            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);            activity.startActivityForResult(intent, requestCode);        }    }    /**     * 关闭蓝牙     */    public void turnOffBluetooth(){        if(mAdapter!=null&&mAdapter.isEnabled()) {            mAdapter.disable();        }    }}
public class MainActivity extends AppCompatActivity {    public static final int REQUESTCODE_OPEN = 1;    private BluetoothController mController = new BluetoothController();    private BroadcastReceiver receiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {            int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 1);            switch (state) {                case BluetoothAdapter.STATE_OFF:                    Toast.makeText(MainActivity.this, "蓝牙已关闭", Toast.LENGTH_SHORT).show();                    break;                case BluetoothAdapter.STATE_ON:                    Toast.makeText(MainActivity.this, "蓝牙已打开", Toast.LENGTH_SHORT).show();                    break;                case BluetoothAdapter.STATE_TURNING_ON:                    Toast.makeText(MainActivity.this, "正在打开蓝牙", Toast.LENGTH_SHORT).show();                    break;                case BluetoothAdapter.STATE_TURNING_OFF:                    Toast.makeText(MainActivity.this, "正在关闭蓝牙", Toast.LENGTH_SHORT).show();                    break;                default:                    Toast.makeText(MainActivity.this, "未知状态", Toast.LENGTH_SHORT).show();            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);        registerReceiver(receiver, filter);    }    public void click(View view) {        switch (view.getId()) {            case R.id.btnIsSupport:                boolean flag = mController.isSupportBluetooth();                Toast.makeText(this, "flag = " + flag, Toast.LENGTH_SHORT).show();                break;            case R.id.btnIsTurnOn:                boolean isTurnOn = mController.getBluetoothStatus();                Toast.makeText(this, "isTurnOn" + isTurnOn, Toast.LENGTH_SHORT).show();                break;            case R.id.btnTurnOn:                mController.turnOnBluetooth(this, REQUESTCODE_OPEN);                break;            case R.id.btnTrunOff:                mController.turnOffBluetooth();                break;        }    }    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        if(resultCode==RESULT_OK){            Toast.makeText(this, "终于打开了", Toast.LENGTH_SHORT).show();        }    }}

4.查找设备
使用BluetoothAdapter对象,能够通过设备发现或查询已配对的设备列表来找到远程的蓝牙设备。
设备发现是一个扫描过程,该过程搜索本地区域内可用的蓝牙设备,然后请求一些彼此相关的一些信息(这个过程被叫做“发现”、“查询”或“扫描”)。但是,本地区域内的蓝牙设备只有在它们也启用了可发现功能时,才会响应发现请求。如果一个设备是可发现的,那么它会通过共享某些信息(如设备名称、类别和唯一的MAC地址)来响应发现请求。使用这些信息,执行发现处理的设备能够有选择的初始化跟被发现设备的连接。
一旦跟远程的设备建立的首次连接,配对请求就会自动的被展现给用户。当设备完成配对,相关设备的基本信息(如设备名称、类别和MAC地址)就会被保存,并能够使用蓝牙API来读取。使用已知的远程设备的MAC地址,在任何时候都能够初始化一个连接,而不需要执行发现处理(假设设备在可连接的范围内)。
要记住配对和连接之间的差异。配对意味着两个设备对彼此存在性的感知,它们之间有一个共享的用于验证的连接密钥,用这个密钥两个设备之间建立被加密的连接。连接意味着当前设备间共享一个RFCOMM通道,并且能够被用于设备间的数据传输。当前Android蓝牙API在RFCOMM连接被建立之前,要求设备之间配对。(在使用蓝牙API初始化加密连接时,配对是自动被执行的。)
以下章节介绍如何发现已配对的设备,或发现新的使用了可发现功能的设备。
注意:默认Android设备是不可发现的。用户能够通过系统设置在限定的时间内变成可发现的设备,或者应用程序能够请求用户启用可发现性,而不离开应用程序。如何启用可发现性,会在下文来讨论。
查询配对设备
在执行设备发现之前,应该先查询已配对的设备集合,来看期望的设备是否是已知的。调用getBondedDevices()方法来完成这件工作。这个方法会返回一个代表已配对设备的BluetoothDevice对象的集合。例如,你能够查询所有的配对设备,然后使用一个ArrayAdapter对象把每个已配对设备的名称显示给用户。

  /**     * 获取已经配对的设备     */    public Set<BluetoothDevice> getConnetedDevices() {        if (mAdapter != null && mAdapter.isEnabled()) {           return mAdapter.getBondedDevices();        }        return null;    }
Set<BluetoothDevice>   connetedDevices = mController.getConnetedDevices();  for(BluetoothDevice device:connetedDevices){            adapter.add(device.getName()+"\n"+ device.getAddress());            listView.setAdapter(adapter);        }

这里写图片描述

从BluetoothDevice对象来初始化一个连接所需要的所有信息就是MAC地址。在这个例子中,MAC地址被作为ArrayAdapter的一部分来保存,并显示给用户。随后,该MAC地址能够被提取用于初始化连接。
发现设备
简单的调用startDiscovery()方法就可以开始发现设备。该过程是异步的,并且该方法会立即返回一个布尔值来指明发现处理是否被成功的启动。通常发现过程会查询扫描大约12秒,接下来获取扫描发现的每个设备的蓝牙名称。
为了接收每个被发现设备的的信息,你的应用程序必须注册一个ACTION_FOUND类型的广播接收器。对应每个蓝牙设备,系统都会广播ACTION_FOUND类型的Intent。这个Intent会携带EXTRA_DEVICE和EXTRA_CLASS附加字段,这个两个字段分别包含了BluetoothDevice和BluetoothClass对象。例如,下列演示了你如何注册和处理设备发现时的广播:

//第一步:注册广播 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);        registerReceiver(mReceiver, filter);//不要忘了在onDestory中unregister        第二部:开始发现 mController.startDiscovery();//开始搜索可发现的设备 //第三步:接收广播   private BroadcastReceiver mReceiver = new BroadcastReceiver() {        @Override        public void onReceive(Context context, Intent intent) {          String action = intent.getAction();            //发现了设备            if(BluetoothDevice.ACTION_FOUND.equals(action)){                Toast.makeText(MainActivity.this, "发现设备", Toast.LENGTH_SHORT).show();                //从Intent中获取设备的BluetoothDevice对象               BluetoothDevice device =  intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);                adapter.add(device.getName()+"\n"+ device.getAddress());                listView.setAdapter(adapter);            }        }    };

结果如下:
这里写图片描述
可以看到,我们已经把发现的设备展示到了ListView中。
警告:执行设备发现,对于蓝牙适配器来说是一个沉重的过程,它会消耗大量的资源。一旦发现要连接设备,在尝试连接之前一定要确认用cancelDiscovery()方法来终止发现操作。另外,如果已经有一个跟设备的连接,那么执行发现会明显的减少连接的可用带宽,因此在有连接的时候不应该执行发现处理。

好了,第一篇就是这么多,大家如果理解了可以去看第二篇了。Android开发之蓝牙详解(二)

2 1
原创粉丝点击