android 蓝牙

有关蓝牙:

蓝牙可以让两个设备在一定距离内进行连接,以传输数据。这个数据可以互相传输,你传给我,我也可以传给你。

两个连接的设备,一个作为服务器端,一个作为客户端。


使用蓝牙,需要以下步骤

第一步打开蓝牙。

第二步发现对方蓝牙设备。服务端需要使自己置于可被发现状态(注意打开蓝牙并不一定可被发现),然后客户端去寻找蓝牙。

第三步客户端去连接服务端。

第四步传输数据。

第五部结束连接。

这些步骤都可以使用代码实现,不需要用户在安卓“设置”里面去操作。


如何实现:

首先需要分清谁是服务器端,谁是客户端。

服务端和客户端的区别在于,客户端是攻,服务端是受,对话都是客户端发起的。

注意的是两端是通过Socket连接,一般如果长时间没有数据交互的话要把Socket关闭,等下次要交互再重新创建Socket连接。

还有在manifest中声明蓝牙权限,代码如下

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


1 服务器端首先要做的是打开蓝牙、置于可被发现状态。

代码为

                // 打开本机的蓝牙发现功能(默认打开120秒,可以将时间最多延长至300秒) 
                Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
                // 设置持续时间(最多300秒)
                discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
                startActivityForResult(discoverableIntent, 0x1);
如果一开始没有打开,使用该代码也会自动打开蓝牙。

这个代码会弹出一个对话框询问是否打开蓝牙并可被发现。默认持续120秒可被发现,最多设置300秒。

然后在Activity的onActivityResult中

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)

resultCode为持续可被发现的时间,如上面传输打开300秒,返回的resultCode就为300秒。启用失败或者用户在对话框中选择拒绝则返回0。


2 客户端要打开蓝牙,然后去寻找服务端。

客户端不像服务端两行代码会自动打开蓝牙,他需要先打开蓝牙才能去寻找服务端。

打开蓝牙有两种方式:第一种方式会弹出是否打开蓝牙弹出框,确认后才能代开;第二种方式直接打开,不询问用户。


第一种打开蓝牙方式,询问用户是否打开蓝牙的方式

                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(intent, 0x2);
然后在onActivityResult中会有返回码,告诉成功还是失败。

如果蓝牙已经打开,则不会弹出询问框。

可以使用BluetoothAdapter.getDefaultAdapter().isEnabled();查看蓝牙当前是否打开。


第二种方式直接打开蓝牙而不询问用户

                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
                // 直接打开蓝牙,同步执行
                adapter.enable();
可以使用BluetoothAdapter.getDefaultAdapter().isEnabled();查看蓝牙当前是否打开。


打开蓝牙成功后,去寻找服务器端

                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
                adapter.startDiscovery();
该寻找过程是在异步线程中执行的,总共只会持续寻找12秒,寻找到的话会发广播出来。

因此需要注册广播接收蓝牙寻找结果

        <receiver
            android:name="com.example.testbluetooth.BlueToothReceiver">
            <intent-filter >
                <action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED"/>
                <action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>
                <action android:name="android.bluetooth.device.action.FOUND" />
            </intent-filter>
        </receiver>

蓝牙广播接收器代码

public class BlueToothReceiver extends BroadcastReceiver
{
    private static List<BluetoothDevice> mDeviceArr = new ArrayList<BluetoothDevice>();

    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (intent.getAction().equals(BluetoothAdapter.ACTION_DISCOVERY_STARTED))
        {
            // 开始寻找蓝牙
            mDeviceArr.clear();
            MainActivity mainActivity = MainActivity.getInstance();
            if (mainActivity != null)
            {
                mainActivity.refreshList(mDeviceArr);
            }
        }
        else if (intent.getAction().equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED))
        {
            // 结束寻找
        }
        else if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND))
        {
            // 找到一个设备
            // 从Intent中获取设备对象 
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 
            if (device.getName().isEmpty() || mDeviceArr.contains(device))
            {
                return;
            }
            // 将设备名称和地址放入array adapter,以便在ListView中显示 
            mDeviceArr.add(device);
            
            MainActivity mainActivity = MainActivity.getInstance();
            if (mainActivity != null)
            {
                mainActivity.refreshList(mDeviceArr);
            }
        }
    }
}

到此为止客户端已经找到服务端了,最好把寻找到的服务器地址给记录下来,下次就不需要再去寻找服务器端了。

通过device.getAddress()可以获取服务器地址,而找到过的历史服务器下次可以通过BluetoothAdapter.getDefaultAdapter().getBondedDevices()获取。

也就是下次可以通过BluetoothAdapter.getDefaultAdapter().getBondedDevices()去寻找这个BluetoothDevice。

现在要让客户端去连接服务端了。


3 客户端连接服务端

连接服务端前需要停止搜索,不然可能连接失败。

        if (adapter.isDiscovering())
        {
            adapter.cancelDiscovery();
        }


创建Socket:

            try
            {
                mSocket = mDevice.createRfcommSocketToServiceRecord(MY_UUID);
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
其中这个MY_UUID可以通过UUID.fromString("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx");获取一个。x为十六进制数1-f。

服务器端与客户端必须使用相同的UUID才能连接成功。


尝试连接:

            try
            {
                // Connect the device through the socket. This will block 
                // until it succeeds or throws an exception 
                mSocket.connect();
            }
            catch (IOException connectException)
            {
                connectException.printStackTrace();
            }
connect是同步执行,在没有连接结果返回前一直阻塞,所以需要创建一个线程来执行connect()。

如果没有配对过该设备连接会弹出对话框询问是否进行配对,如果曾经配对过则直接连接成功不弹出询问框。


如果connect通过以后可以调用mSocket.getOutputStream()与mSocket.getInputStream()获取输入输出流,通过这两个流与服务器端交换数据。

需要注意的是这个Stream是同步的,如果发送了对方没有接受,或者在接收可是没有接收到任何信息都会一直阻塞。

inputStream与outputStream就不再详述。



4 服务端接受客户端的连接


                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
                BluetoothServerSocket serverSocket;
                BluetoothSocket socket = null;
                try
                {
                    serverSocket = adapter.listenUsingRfcommWithServiceRecord("TestBluetooth", MY_UUID);
                    socket = serverSocket.accept();
                }
                catch (IOException e)
                {
                    e.printStackTrace();
                }

该accept方法是阻塞的,需要创建一个线程来执行。

MY_UUID必须与客户端的MY_UUID相同,两个设备才能进行连接。

如果两个设备连接成功过则不弹出对话框询问是否配对,否则会弹出对话框询问是否配对设备。


然后通过socket.getInputStream();和socket.getOutputStream();获取输入流输出流。

需要注意的是这个Stream是同步的,如果发送了对方没有接受,或者在接收可是没有接收到任何信息都会一直阻塞。

不再详述他们的用法。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值