在Xamarin(android)中使用蓝牙传输数据

Xamarin.Android Bluetooth

1、要操作蓝牙,必须先获得本地的蓝牙适配器,在Xamarin里使用如下语句便可获得,与Java没多大区别。

BluetoothAdapter localAdapter = BluetoothAdapter.DefaultAdapter;

一般设备只有一个蓝牙模块,所以用DefaultAdapter就好,如果有多个的话,我暂时也没有找到解决方案,如果哪位有办法,请告诉我,感激不尽。

2、打开蓝牙设备。有人说可以调用一个打开蓝牙的Activity:

 if (!bluetoothAdapter.IsEnabled) { 
    Intent enableIntent = new Intent (BluetoothAdapter.ActionRequestEnable); 
    StartActivityForResult (enableIntent, REQUEST_ENABLE_BT); 
}

这样会打开一个弹窗,要点击确定才能打开蓝牙。比较麻烦,我比较喜欢这样:

if (!localAdapter.IsEnabled) {
    localAdapter.Enable ();
}

这样不会弹窗提示,而是静默打开蓝牙,代码也少,嗯。
但是需要注意,在执行localAdapter.Enable ()后,这个函数会立即返回,并不会等待蓝牙成功打开。所以在调用它之后是不能直接操作localAdapter的,需要等到蓝牙完全打开。至于如何获得蓝牙状态,在BluetoothAdapter中有个State成员,这是一个枚举变量,它标明了蓝牙的当前状态。

3、获得已配对的设备列表。在已获得的localAdapter中有一个成员,定义如下:

public ICollection<BluetoothDevice> BondedDevices

可见,它是一个集合。不过为了方便使用,我更喜欢如下方法:

List<BluetoothDevice> bondedDevices= new List<BluetoothDevice> (localAdapter.BondedDevices);

这样就可以获得一个已配对的设备列表。

4、类似于Socket通讯,蓝牙传输需要建立一个socket server。关于Socket不再赘述。代码如下:

BluetoothServerSocket serverSock = localAdapter.ListenUsingRfcommWithServiceRecord ("Bluetooth", Java.Util.UUID.FromString ("xxxx-xxxx-xxxx-xxxx-xxxxxxx"));
BluetoothSocket sock = serverSock.Accept ();
serverSock.Close();//服务器获得连接后腰及时关闭ServerSocket
//启动新的线程,开始数据传输
Thread t = new Thread(connected);
t.Start(sock);

怎么样,是不是和Socket很相似?但需要注意的是ListenUsingRfcommWithServiceRecord()函数的参数。这个函数包含两个参数:第一个是字符串类型,标明这个ServerSocket连接的名称(但是我目前还没有发现它有什么用……);第二个参数是一个UUID,这个参数必须是明确指定的,因为蓝牙通讯双方必须持有相同的UUID才能够建立通讯。这个函数返回一个BluetoothServerSocket,接下来就可以用这个BluetoothServerSocket来进行Accept,等待客户端的连接了。
与Socket相同的是,Accept()函数会阻塞线程,千万不要放在UI线程里!!!

5、客户端连接。我会遍历已配对的设备尝试连接,然后代码如下:

foreach (BluetoothDevice d in bondedDevices) {
    BluetoothSocket sock = d.CreateRfcommSocketToServiceRecord (Java.Util.UUID.FromString ("xxxx-xxxx-xxxx-xxxx-xxxxxxx"));
    try{
        sock.Connect();//连接服务器
        //启动新的线程,开始传输数据
        Thread t = new Thread(connected);
        t.Start(sock);
        break;
    }catch(Exception e){
        sock.Dispose ();
        continue;
    }
}

每个设备实例的CreateRfcommSocketToServiceRecord()函数会返回一个BluetoothSocket,当然这个函数的参数需要与服务器的UUID相同。得到返回的BluetoothSocket之后,就可以调用connect()函数尝试连接服务器了。需要注意的是:connect()函数会阻塞线程,并且由于在connect()函数执行时才会真正尝试和服务器建立连接,所以大部分的连接错误会在此处报错,要注意try-catch。在connect()函数报错后,为了下一次connect()函数能够顺利执行,请及时调用Dispose()销毁资源。一旦connect()成功,就会使服务端Accept()函数返回。
有没有发现,connect()函数和Accept()函数都会返回一个BluetoothSocket?
有没有发现,客户端连接成功后,与服务器获得连接后,启动的是同一个方法来传输数据?

因为无论是客户端还是服务器,都依靠一个BluetoothSocket进行数据传输,所以其数据收发过程是一样的,当然可以用同一个方法。这就是接下来的步骤。

6、数据收发。与java开发Android类似,数据收发都通过流来实现。BluetoothSocket有两个流成员,分别是BluetoothSocket.InputStreamBluetoothSocket.OutputStream。以我近乎于四级的英语水平来看:InputStream必定是接收数据的流,而OutputStream是发送数据的流。既然是流,那么读写就方便的多了:
暴力一点,直接

InputStream.Read()
OutputStream.Write()

读写字节数组,但是想要获得有效的数据还得经过复杂的类型转换,才能把字节数组转换为需要的数据。
优雅一点,可以如下:

StreamReader sReader = new StreamReader (sock.InputStream);
StreamWriter sWriter = new StreamWriter (sock.OutputStream);

这样可以通过StreamReader 和 StreamWriter 直接读写字符串。是不是方便的多?
需要注意的是,无论是直接调用Read()函数还是通过StreamReader读取流,都会阻塞线程,读取操作在没有获得数据的情况下会一直等待,直到获得数据并返回数据长度。
虽然在sock.InputStream中有ReadTimeout这么一个成员。我也曾以为

sock.InputStream.ReadTimeout = 1000

这句代码会有用。但是直到我发现sock.InputStream.CanTimeout这个值永远都是false的时候,我发现我太天真了。

7、关闭连接。就跟随手关门、随手关灯一样,在不需要继续连接的时候请关闭连接,直接调用

sock.Close();

当然,数据收发双方,当有一方调用Close()函数后,双方对BluetoothSocket的调用都会报错,记得try-catch,否则程序会直接关闭(别问我怎么知道的)。

嗯,大约就这样。好像整个流程里少了扫描设备这个过程,嗯……本人才疏学浅,尚未参透扫描设备的方法,待他日我大彻大悟再来哔哔。或者哪位大哥愿意指教,小弟不胜感激。

再补充一句,小弟没有Android开发经验,以上内容纯属自己摸索,请大家不吝赐教,谢谢!

阅读更多
文章标签: Xamarin
个人分类: Xamarin
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭