一、Android蓝牙基础(1)

参考:Android官方文档      http://developer.android.com/guide/topics/connectivity/bluetooth.html


自2.0之后,Android平台就开始支持蓝牙通信,它允许一个设备通过无线网络与其他蓝牙设备交换数据。应用程序框架提供通过蓝牙的api访问蓝牙功能。这些api允许应用程序通过无线连接到其他蓝牙设备,使用点对点或者多点无线功能。

使用蓝牙的api,一个Android的应用程序可以进行如下的操作:

扫描其他的蓝牙设备

通过当地的蓝牙适配器查询已配对的设备

建立RFCOMM渠道

通过搜索服务连接到其他的设备

与其他设备传输数据

管理多个连接


下面看看蓝牙基础部分的api:

BluetoothAdapter

代表当地的蓝牙适配器(无线蓝牙),它是所有蓝牙功能的入口,使用它,可以去查询你的蓝牙的开启状态,搜索状态,连接状态。可以去搜索和连接其他的蓝牙设备。可以用一个已知的MAC地址去实例化一个BluetoothDevice,并创建一个BluetoothServerSocket监听通信。

BluetoothDevice

代表一个远程的蓝牙设备。通过它可以去请求连接一个远程的蓝牙设备。也可以查新一个远程设备的信心,如名字,mac地址,类型和连接状态。

BluetoothSocket

代表一个蓝牙socket接口(类似于TCPsocket)。这是一个运行设备之间使用InputStream和OutputStream进行数据交换的连接点。

BluetoothServerSocket

代表一个开放的服务socket,监听接入的请求(类似于TCP的ServerSocket)。为了连接两个Android蓝牙设备,其中的一个设备必须用这个类打开一个服务socket。当一个远程设备请求一个连接的时候并且连接被接受的时候,BluetoothServerSocket将返回一个BluetoothSocket。

BluetoothClass

描述一个蓝牙设备的基本特征和功能。这是一个只读属性集,定义了设备的主要和次要设备类型和它的服务。虽然它可能并不能完全描述一个设备支持的服务和蓝牙配置文件,但是对设备类型的提示还是有一定的作用。


这些api位于android.bluetooth包下,它只是基础的部分,但是已经可以完成基本的蓝牙功能。


一、(Bluetooth Permissions)权限

为了能在应用程序中使用蓝牙功能,你至少要在manifest文件中声明下面两个权限中的一个:蓝牙权限和蓝牙管理权限。

        <uses-permission android:name="android.permission.BLUETOOTH" />     <!-- 蓝牙权限 -->
        <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />    <!-- 蓝牙管理权限 -->

蓝牙权限:为了执行任何蓝牙通信,如请求连接,接受一个连接,传输数据。

蓝牙管理权限:为了启动搜索服务和设置蓝牙。大部分蓝牙程序都需要使用这个权限。并且如果有这个权限,那么必须要有第一个蓝牙权限。

在自己的蓝牙应用程序中,最好还是将这两个权限都加上。


二、(Setting Up Bluetooth)蓝牙设置

在使用蓝牙进行通信前,你需要检测你的手机是否支持蓝牙,并且蓝牙是否可用。

如果你的手机不支持蓝牙,那么你就不能去使用蓝牙的功能。如果支持但是没有开启,那么你可以直接去请求用户开启蓝牙,而不用离开你的程序去系统设置里面设置。

该过程需要使用BluetoothAdapter类来完成:

1、获得BluetoothAdapter,判断蓝牙是否可用

任何蓝牙的活动都离不开BluetoothAdapter。调用BluetoothAdapter的静态方法getDefaultAdapter()。将返回一个代表你自己的设备蓝牙适配器(无线蓝牙)。

        BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter == null) {
            // 该设备不支持蓝牙,那么你就不可以继续执行其他的蓝牙操作
        }else{
            //执行其他的蓝牙操作
        }

2、如果确定支持蓝牙,在判断蓝牙当前的状态。

①、判断蓝牙的状态

蓝牙的状态分为四种:(STATE_OFF)已关闭,(STATE_TURNING_ON)正在开启,(STATE_ON)已开启,(STATE_TURNING_OFF)正在关闭。

在BluetoothAdapter中有着四种状态的声明:

        public static final int STATE_OFF = 10;
        public static final int STATE_TURNING_ON = 11;
        public static final int STATE_ON = 12;
        public static final int STATE_TURNING_OFF = 13;

我们可以通过调用getState()方法来获取设备蓝牙当前的状态。

        //因为查看BluetoothAdapter类,四个状态的声明都是连续的int值,所以用state-10就是数组中对应的字符串
        String[] stateStr=new String[]{
                "已关闭","正在开启","已开启","正在关闭"
        };
        // 获取当前的蓝牙状态
        int currentState = mBluetoothAdapter.getState();
        System.out.println(stateStr[currentState-10]);

也可以直接调用isEnabled()方法来判断蓝牙是否已经启用。该方法返回值为boolean,true代表可用,false为不可用。


②、开启和关闭蓝牙

根据上面得到的蓝牙状态,我们可以选择开启或者去关闭蓝牙。在一个用户体验良好的程序中,显然如果直接给出个Toast叫用户自己去系统设置里面进行操作,真的很戳。所以我们可以使用下面的方法,在不离开你的应用的情况下,开启或者关闭蓝牙。

可以使用startActivityForResult()并给出Action为BluetoothAdapter.ACTION_REQUEST_ENABLE的Intent,来请求启动蓝牙。

        if (!mBluetoothAdapter.isEnabled()) {// 蓝牙未开启,请求开启蓝牙
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
        }

这段代码将会弹出一个对话框来向用户请求开启蓝牙,REQUEST_ENABLE_BT(要大于0)是请求的状态码,我们需要重写onActivityResult()方法来获取开启蓝牙是否成功。如果成功将返回RESULT_OK,如果开启失败或者是用户不同意开启蓝牙,那么将返回RESULT_CANCELED

        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == REQUEST_BT) {
                if (resultCode == Activity.RESULT_OK) {
                    Toast.makeText(this, "蓝牙开启成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "蓝牙开启失败", Toast.LENGTH_SHORT).show();
                }
            }
        }

还可以在不给予用户提示的情况下,偷偷的开启:不过这种情况下,也要去判断时候开启成功,enable()方法的返回值为boolean,true表示已经成功的从关闭状态到正在开启状态,过段时间就会从正在开启状态转换到开启状态。用这种方法,我们应该注册一个BrodcastReceiver来监听蓝牙状态的改变(后面提到)。不过不提倡这种方式,应该用第一种让用户选择的方式,而不是自己偷偷的区开启。

        if (!mBluetoothAdapter.isEnabled()) {// 蓝牙未开启,请求开启蓝牙
            mBluetoothAdapter.enable();
        }

关闭只提供了一种方式:

        if (mBluetoothAdapter.isEnabled()) {
            mBluetoothAdapter.disable();
        }

同样在api上也提到,不提倡这种方式,但是貌似没找到别的方式关闭蓝牙。


③、蓝牙状态的监听

蓝牙的状态的改变,会发送BluetoothAdapter.ACTION_STATE_CHANGED="android.bluetooth.adapter.action.STATE_CHANGED"的广播,可以注册一个BroadcastReceiver来监听蓝牙状态的改变。并且改广播的intent会带两个参数,如下:

        private class BtStateChangeReceiver extends BroadcastReceiver {
            String[] stateStr=new String[]{
                    "已关闭","正在开启","已开启","正在关闭"
            };
            
            @Override
            public void onReceive(Context context, Intent intent) {
                if (!intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
                    return;
                }
                int nowState=intent.getExtras().getInt(BluetoothAdapter.EXTRA_STATE);//当前的状态
                int oldState=intent.getExtras().getInt(BluetoothAdapter.EXTRA_PREVIOUS_STATE);//之前的状态
                String nowStateStr=stateStr[nowState-10];
                String oldStateStr=stateStr[oldState-10];
                System.out.println("蓝牙状态从    "+oldStateStr+"----------->"+nowStateStr);
            }
        }

然后注册该广播接受者,就可以监听蓝牙状态的改变:

        IntentFilter intentFilter=new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
        registerReceiver(new BtStateChangeReceiver(), intentFilter);



三、(Finding Devices)查找设备

使用BlueAdapter,可以去搜索远程的设备和查询已经配对的设备。

搜索设备是一个扫描的程序,它可以去寻找附近已经开启了蓝牙的设备,并且请求每一个设备的信息。这些信息包括设备名称,类型,和唯一的MAC地址。

根据搜索的得到的BluetoothDevice,我们可以去请求配对或者连接。

配对和连接是两个不同的概念,配对是指两个配对的设备都知道彼此的存在,并且有能力去建立一个连接。而连接是指共享一个RFCOMM通道并且可以传输数据。


1、查询已经配对的设备

在开始执行搜索之前,去查询一下已经配对的设备列表时相当有意义的。可以调用getBondedDevices()方法,该方法将返回一个BluetoothDevice的集合。如下:

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

2、搜索设备

调用startDiscovery()方法就可以开始搜索设备。这是一个异步的方法,这个方法会直接返回一个boolean值,true代表开始搜索成功,false代表失败。这个搜索的过程大概会持续12秒。

要想得到搜索的结果,需要设置一个广播接受者去接受搜索结果的广播。该广播会带两个参数进来,一个是代表蓝牙设备的BluetoothDevice,一个是代表蓝牙类型的BluetoothClass。具体过程如下:

    // Create a BroadcastReceiver for ACTION_FOUND
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            // When discovery finds a device
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
                // Get the BluetoothDevice object from the Intent
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                System.out.println("设备名称:" + device.getName() + "\n" + "设备地址:"
                        + device.getAddress());
                // Get the BluetoothClass object from the Intent
                BluetoothClass btClass = intent.getParcelableExtra(BluetoothDevice.EXTRA_CLASS);
                // the method .getDeviceClass() while return a int value that represent the device class which is define in the BluetoothClass.Device
                System.out.println("设备类型:"+btClass.getDeviceClass());
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED)) {
                System.out.println("开始搜索");
            } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
                System.out.println("搜索完毕或被终止");
            }
        }
    };

然后注册这个广播,并且开始搜索:

        IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
        boolean startSuc = mBluetoothAdapter.startDiscovery();
        if (startSuc) {
            Toast.makeText(this, "开始搜索成功", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "开始搜索失败", Toast.LENGTH_SHORT).show();
        }


注意:执行搜索设备对于蓝牙适配器是一个沉重的过程,会消耗大量的资源。在你执行连接之前,记得要调用cancelDiscovery()去关闭搜索。并且,如果你已经连接了一个设备,并且还去执行搜索设备,那么就会明显的减少连接的带宽。所以要随时记得关闭搜索设备。如下:

        if(mBluetoothAdapter.isDiscovering()){
            boolean endSuc = mBluetoothAdapter.cancelDiscovery();
            if (endSuc) {
                Toast.makeText(this, "停止搜索成功", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "停止搜索失败", Toast.LENGTH_SHORT).show();
            }
        }else {
            Toast.makeText(this, "当前没有进行搜索", Toast.LENGTH_SHORT).show();
        }

四、(Enabling discoverability)能被其他的设备查找并建立连接

上面提到的是由你去搜索其他的蓝牙设备,这种情况下,你的蓝牙必须要是开启的。

但是蓝牙开启的情况下,你不一定能被其他的蓝牙设备搜索到。

在API中,将蓝牙能被其他设备搜索到,叫做scan。有三种scan模式,定义在API中:

    public static final int SCAN_MODE_NONE = 20;   //不能被其他设备搜索到
    public static final int SCAN_MODE_CONNECTABLE = 21;   //不能被其他设备搜索到,但是可以被连接到
    public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;  //可以被搜索到,也可以被连接到

如果你的应用程序只是要去连接一个远程设备,是没必要开启被搜索功能的。只有当你的应用程序需要作为一个蓝牙服务端,去接受其他设备的请求时候才需要开启被搜索功能。

可以使用下面的方式去开启被搜索功能:

        BluetoothAdapter mAdapter=BluetoothAdapter.getDefaultAdapter();
        int scanMode=mAdapter.getScanMode();
        if(scanMode!=BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE){
            Intent discoverableIntent = new Intent(
                    BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
            discoverableIntent.putExtra(
                    BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
            startActivityForResult(discoverableIntent);
        }
开启被搜索有时间的限制问题,默认情况下是120秒,我们可以再Intent带上BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION的参数,来指定一个时间。这个时间值是0到3600秒之间。如果为0的话,就是不限制时间。

执行上面的代码后,会弹出一个提示用户开启被搜索的对话框。同样的,我们可以再onActivityResult来得到用户的选择。


同样的,被搜索模式的改变,也会产生一个系统的广播,我们可以注册下面的广播接受者来监听模式的改变:

private final class ScanModeChangeBroadcastReceiver extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			if(!intent.getAction().equals(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED))//模式改变的广播
				return;
			int nowMode=intent.getExtras().getInt(BluetoothAdapter.EXTRA_SCAN_MODE);//当前的模式
			int oldMode=intent.getExtras().getInt(BluetoothAdapter.EXTRA_PREVIOUS_SCAN_MODE);//之前的模式
		}
	}

五、BluetoothAdapter的其他基本方法

public String getAddress()   //获取本地蓝牙设备的mac地址
public String getName()        //获取本地蓝牙设备的名称
public boolean setName(String name)       //设置本地蓝牙设备的名称 
public BluetoothDevice getRemoteDevice(String address)      //根据一个mac地址,得到一个代表远程设备的BluetoothDevice
public static boolean checkBluetoothAddress(String address)        //检测一个mac地址是否是有效的,即格式是否正确

当更改了蓝牙适配器的名字的时候,会发送一个广播,我们可以注册下面的广播接受者,来监听这个改变:

       private final class LocalNameChangeBroadcastReceiver extends BroadcastReceiver{

		@Override
		public void onReceive(Context context, Intent intent) {
			if(!intent.getAction().equals(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED))
				return;
			String name=intent.getExtras().getString(BluetoothAdapter.EXTRA_LOCAL_NAME);//更改的名字
		}
	}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值