您的位置:首页 > 其它

[安卓] 11、串口蓝牙·将软硬结合进行到底

2015-01-18 17:23 357 查看
前言


上次我详细介绍了如何用笔记本搜索到蓝牙模块并与之通信:/content/4007693.html,这次将介绍如何让安卓手机的蓝牙和该蓝牙模块进行通信。




简单一步搞定

参看:【只需简单一步,android自带的示例程序 BluetoothChat 变蓝牙串口助手 :http://www.amobbs.com/thread-5426293-1-1.html】,只要把Android自带的BluetoothChat例程稍微改一下就能搞定:

BluetoothChatService.java的第49行
private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
中的字符串不同,于是把他替换成蓝牙串口服务 (SPP) 的 UUID
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");











实现了搜索设备,链接设备,并和设备完成了通信~[图中我用串口助手通过蓝牙模块向外发送"litao"字符串,这时手机收到了数据;当用手机发送"yes"字符串时,串口收到了"yes"]

安卓蓝牙详解

蓝牙主要涉及的操作有:1、开启蓝牙 2、关闭蓝牙 3、能被搜到 4、获取配对设备 5、数据传输【参考DLUTBruceZhang的专栏·部分摘抄:Android 通信--蓝牙

1、蓝牙设备-->蓝牙设备主要包括本地设备和远程设备,和他们相关的函数如下图:



// 获取本地的蓝牙适配器实例
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if(adapter!=null)
{
if(!adapter.isEnabled())
{
//通过这个方法来请求打开我们的蓝牙设备
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivity(intent);
}
}
else
{
System.out.println("本地设备驱动异常!");
}


2、搜索周边设备startDiscovery()-->这里startDiscovery是BluetoothAdapter的成员函数,其可以执行一个异步方式获得周边蓝牙设备,因为是一个异步的方法所以我们不需要考虑线程被阻塞问题,整个过程大约需要12秒时间,这时我们可以注册一个 BroadcastReceiver 对象来接收查找到的蓝牙设备信息,我们通过Filter来过滤ACTION_FOUND这个 Intent动作以获取每个远程设备的详细信息,通过Intent字段EXTRA_DEVICE 和 EXTRA_CLASS可以获得包含了每个BluetoothDevice 对象和对象的该设备类型 BluetoothClass。

//实现一个BluetoothReciever继承BroadcastReceiver来接收查找到的蓝牙设备信息
private class BluetoothReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
System.out.println(device.getAddress());
}
}
}
//注册这个Receiver
IntentFilter intentFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
bluetoothReceive = new BluetoothReciever();
registerReceiver(bluetoothReceive, intentFilter);
//因为在注册一个Receiver后,程序并不知道该何时去回收它,所以需要我们自己重写Activity类的onDestroy()方法。
protected void onDestroy() {
// TODO Auto-generated method stub
unregisterReceiver(bluetoothReceive);
super.onDestroy();
}


3、被周边设备所发现-->如果需要用户确认操作,不需要获取底层蓝牙服务实例,可以通过一个Intent来传递ACTION_REQUEST_DISCOVERABLE参数, 这里通过startActivity来请求开启。

Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
//50这个参数代表的是蓝牙设备能在多少秒内被发现
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 50);
startActivity(discoverableIntent);


4、配对-->通过下面的方法遍历所有配对设备,一会我们还会针对刚开始的例子进行详细讲解里面的具体过程。

//通过getBondedDevices方法来获取已经与本设备配对的设备
Set<BluetoothDevice> device= adapter.getBondedDevices();
if(device.size()>0)
{
for(Iterator iterator=device.iterator();iterator.hasNext();)
{
BluetoothDevice bluetoothDevice=(BluetoothDevice)iterator.next();
System.out.println(bluetoothDevice.getAddress());
}
}


5、通信-->蓝牙的通信过程和TCP的server和client类似,这里就不详细介绍了,我参考的这篇文章讲的很详细,如果不懂可以参考下:/article/1387757.html#。此外,一会我还会针对具体的例子讲解其流程。

6、权限-->当然,想使用蓝牙还得再AndroidManifest.xml中添加上权限:

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


BluetoothChat例子分析

1、BluetoothChat.java-->如下图在BluetoothChat中进行了获得蓝牙设备、开启蓝牙设备、并启动BluetoothChatService进行连接,然后用Handler mHandler进行接收从BluetoothCharService的消息并作出相应的处理。在这里的list是为了显示蓝牙设备的通话信息,一条一条显示;edittext用来获取本地输入的消息,按回车或者send按钮都可以触发sendMessage进行发送消息;此外,由于需要手动搜索蓝牙设备并选择要链接的蓝牙设备,所以这里重写了菜单按钮监听,包括onCreateOptionsMenu和onOptionsItemSelected,在onOptionsItemSelected中对san和discoverable进行分别处理。



下面是onCreate中的获取本地蓝牙设备,并确认是否支持,如果不支持就退出程序。

// Get local Bluetooth adapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// If the adapter is null, then Bluetooth is not supported
if (mBluetoothAdapter == null) {
Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
finish();
return;
}


下面是onStart中如果蓝牙没有打开就发送Intent意图,请求打开蓝牙,setupChat()将会被执行在onActivityResult中。

// If BT is not on, request that it be enabled.
// setupChat() will then be called during onActivityResult
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
// Otherwise, setup the chat session
} else {
if (mChatService == null) setupChat();
}


下面是onActivityResult函数,其中包括2个处理:①处理REQUEST_CONNECT_DEVICE请求连接设备;②处理REQUEST_ENABLE_BT请求打开蓝牙

public void onActivityResult(int requestCode, int resultCode, Intent data) {
if(D) Log.d(TAG, "onActivityResult " + resultCode);
switch (requestCode) {
case REQUEST_CONNECT_DEVICE:
// When DeviceListActivity returns with a device to connect
if (resultCode == Activity.RESULT_OK) {
// Get the device MAC address
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
// Get the BLuetoothDevice object
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
// Attempt to connect to the device
mChatService.connect(device);
}
break;
case REQUEST_ENABLE_BT:
// When the request to enable Bluetooth returns
if (resultCode == Activity.RESULT_OK) {
// Bluetooth is now enabled, so set up a chat session
setupChat();
} else {
// User did not enable Bluetooth or an error occured
Log.d(TAG, "BT not enabled");
Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
finish();
}
}
}


在setupChat中对发送按钮进行点击事件,其实比较简繁就是获取editText中的内容然后调用sendMessage发送。如上图所示,对editText的回车监听也和这个类似,这里就不多介绍了。

// Initialize the send button with a listener that for click events【发送按钮及事件】
mSendButton = (Button) findViewById(R.id.button_send);
mSendButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// Send a message using content of the edit text widget【从textview中获取字符然后调用sendMseeage发送出去】
TextView view = (TextView) findViewById(R.id.edit_text_out);
String message = view.getText().toString();
sendMessage(message);
}
});


下面是重写的菜单按钮监听,当选择的是scan按钮时,发送REQUEST_CONNECT_DEVICE意图。当选择discoverable按钮时,则确认是否能被搜到。这里需要特别说明下:这个菜单要在res/menu中写。

public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.scan:
// Launch the DeviceListActivity to see devices and do scan
Intent serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
return true;
case R.id.discoverable:
// Ensure this device is discoverable by others
ensureDiscoverable();
return true;
}
return false;
}


2、BluetoothChatService-->上面说过在setupChat中最后实例并初始化BluetoothChatService进行蓝牙连接,下面来大致分析下该类的构成及功能:该类主要实现连接蓝牙的3个状态,分别用3个线程来处理:①AcceptThread线程,就像service-client中的accept函数,一直等待,知道接受一个连接;②ConnectThread线程负责连接;③ConnectedThread线程负责和远程蓝牙设备进行通信,并将收到和要发送的信息通过handles进行传递。这里我们主要分析连接之后的数据交换过程,对于等待连接、尝试连接这里不做详解~



ConnectedThread线程主要负责数据收发,其把从远端收来的数据和要发向远端的数据都会通过handle发送给UI用于在list中显示。对于收数据,这里采用利用线程一直收,一旦收到数据就通过mHandler传递到BluetoothChat进行处理,对于发送数据,没必要采用线程轮流发,而是直接一个函数,什么时候需要就直接调用发送就可以。

// 利用线程一直收数据
// 将数据都是放在mHandler中,在BluetoothChat中对信息解析并处理
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;

// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
// bytes是返回读取的字符数量,其中数据存在buffer中
bytes = mmInStream.read(buffer);
String readMessage = new String(buffer, 0, bytes);
if (D)
Log.i(TAG, "read: " + bytes + "  mes: " + readMessage);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes,
-1, buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}


3、DeviceListActivity-->这个类主要负责搜索蓝牙设备并用列表显示出来。那么我们首先来分析一下它所包含的2个list:可见这里面并不是一个list,其中pairedListView是曾经配对过的设备,这些设备可能现在没有被搜到,但是也会显示出来;newDevicesListView是新发现的蓝牙设备,但是如果曾经有配对过的就不加入这个list中(所以,一定要弄清这两个list否则你会很晕的!),他们采用同一个选项监听:mDeviceClickListener,在他们共同的list点击监听中,当点击某一个item时,程序获得item的string信息,然后转换为MAC地址,通过Intent传到另一个Activity中,进行相应处理。

// Find and set up the ListView for paired devices
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
pairedListView.setOnItemClickListener(mDeviceClickListener);

// Find and set up the ListView for newly discovered devices
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(mDeviceClickListener);


// The on-click listener for all devices in the ListViews
//选择list中的设备,然后通过inet传出
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
// Cancel discovery because it's costly and we're about to connect
mBtAdapter.cancelDiscovery();

// Get the device MAC address, which is the last 17 chars in the View
String info = ((TextView) v).getText().toString();
String address = info.substring(info.length() - 17);

// Create the result Intent and include the MAC address
Intent intent = new Intent();
intent.putExtra(EXTRA_DEVICE_ADDRESS, address);

// Set result and finish this Activity
setResult(Activity.RESULT_OK, intent);
finish();
}
};


对于scan按钮,是用来启动搜索蓝牙设备的:从下面可以看出该按钮的点击事件中,主要是调用了doDiscovery()函数,而在doDiscovery()函数中,主要进行的其实就只有一个蓝牙设备自带的成员函数:mBtAdapter.startDiscovery();这是大家可能有点摸不着头脑,怎么写这一个函数就能获得蓝牙设备了,那我搜索到的设备信息是在哪里被保存的呢?我觉得是时候说一下蓝牙搜索其他设备的过程了!

// Initialize the button to perform device discovery
Button scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doDiscovery();
v.setVisibility(View.GONE);
}
});


在【安卓蓝牙详解】的第2点我已经简单介绍了startDiscovery()函数,这里再结合该程序介绍一下吧!这里startDiscovery是BluetoothAdapter的成员函数,其可以执行一个异步方式获得周边蓝牙设备,因为是一个异步的方法所以我们不需要考虑线程被阻塞问题,整个过程大约需要12秒时间,这时我们可以注册一个 BroadcastReceiver 对象来接收查找到的蓝牙设备信息,我们通过Filter来过滤ACTION_FOUND这个 Intent动作以获取每个远程设备的详细信息,通过Intent字段EXTRA_DEVICE 和 EXTRA_CLASS可以获得包含了每个BluetoothDevice 对象和对象的该设备类型 BluetoothClass。因此下面我们在onCreate后面注册一个BroadcastReceiver 对象来接收查找到的蓝牙设备信息,通过Filter来过滤ACTION_FOUND和ACTION_DISCOVERY_FINISHED这两个Intent动作,来获取每个远程设备的详细信息。

// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);

// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);

// Get the local Bluetooth adapter
mBtAdapter = BluetoothAdapter.getDefaultAdapter();


下面就是实现的BroadcastReceiver,承接上面的在scan按钮触发doDiscovery()函数之后整个逻辑我们不知道了的疑惑,现在我们就明白了,当doDiscovery()被执行时,下面的BroadcastReceiver将能够收取在onCreate中阐明的ACTION_FOUND和ACTION_DISCOVERY_FINISHED这两个Intent动作,如下当Intent是ACTION_FOUND时通过BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);获得蓝牙设备,如果这个设备没被配对过,则加入到newDevice数组。

// The BroadcastReceiver that listens for discovered devices and
// changes the title when discovery is finished
//【查找蓝牙设备】
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
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);
// If it's already paired, skip it, because it's been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount() == 0) {
String noDevices = getResources().getText(R.string.none_found).toString();
mNewDevicesArrayAdapter.add(noDevices);
}
}
}
};


最后在onCreate后还有点是将曾经配对过的蓝牙设备加入pairedListView中显示(再次提醒要区分好pairedListView和newDevicesListView这两个list!)

// If there are paired devices, add each one to the ArrayAdapter
// 如果有蓝牙设备就加入list中,这里mPairedDevicesArrayAdapter是列表的数组
if (pairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for (BluetoothDevice device : pairedDevices) {
mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
} else {
String noDevices = getResources().getText(R.string.none_paired).toString();
mPairedDevicesArrayAdapter.add(noDevices);
}


相关链接

本文链接:/article/5083914.html

更多精彩:http://www.cnblogs.com/zjutlitao/p/

参考文章:/article/1387757.html#

参考文章:http://www.amobbs.com/thread-5426293-1-1.html

一些资料:http://pan.baidu.com/s/1kTzdIR1 zptn
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐