是设备解决方案的一部分。
由Android,我想介绍如何执行由蓝牙SPP(串行端口配置文件)的无线通信。
Android SDK中,以有一个名为“BluetoothChat”一个示例项目,因为它已经实现在执行蓝牙通信所需Ichitori的功能,我们想解释给该组。
BluetoothChat是,它说假设的Android终端之间的连接,但这次我想谈谈与其他设备,如PC连接,使用SPP目标。
●蓝牙授权(许可)
首先,为了使用蓝牙功能的应用,“蓝牙”“BLUETOOTH_ADMIN”两个蓝牙权限之一,则必须在许可证的表明自己的至少一种声明。
[蓝牙]
连接请求时,连接接受,并且,必要的,以便执行任何蓝牙通信,如THETA传输。
[BLUETOOTH_ADMIN]
必要的,以便进行设备发现和蓝牙设置的初始化操作。
※如果您想使用BLUETOOTH_ADMIN许可,还需要蓝牙权限。
它显示了声明允许在应用程序下清单蓝牙的过程。
①打开的应用程序清单
②[权限]选择选项卡
③按钮,然后单击[添加...]中
选择所需的权限④Uses
⑤[确定],然后点击按钮
⑥Attributes的名称列表的使用权限更
android.permission.BLUETOOTH或android.permission.BLUETOOTH_ADMIN
选择
通过该操作,下面的描述将被添加到清单。
●收购并启用蓝牙适配器
在应用程序通过蓝牙进行沟通,首先,你需要确保蓝牙设备支持。
调用BluetoothAdapter.getDefaultAdapter()方法来获取本地BluetoothAdapter。如果getDefaultAdapter(结果)为空,则表示该设备不支持蓝牙。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]
// Local Bluetooth adapterprivate BluetoothAdapter mBluetoothAdapter = null; public void onCreate(Bundle savedInstanceState) {
:
// 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;
}}
一旦确认该设备支持蓝牙,看是否已开启蓝牙功能,然后调用isEnabled()方法。如果isEnabled()的结果是假的,蓝牙被禁用。要启用蓝牙,使用ACTION_REQUEST_ENABLE行动意图,调用startActivityForResult()。
这使您可以生成请求通过系统配置以启用蓝牙。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]
// If BT is not on, request that it be enabled.// setupChat() will then be called during onActivityResultif (!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();}
当您启动ACTION_REQUEST_ENABLE行动意图,之后,对话框,请求允许启用蓝牙显示。如果选择“是”,系统会启动蓝牙激活。运行蓝牙激活,你会看到类似下面的对话框。
该过程完成后,将返回到应用程序。
蓝牙结果启用,您将收到的onActivityResult()回调。
在成功激活的情况下,结果代码是RESULT_OK。发生错误或者用户选择在许可证申请“否”,案件的结果代码不能启用蓝牙将RESULT_CANCELED。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
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();
}
}
以上,蓝牙设备存在于本地,它能够确认有效。
然后搜索连接的设备进行了说明。
●搜索远程设备的
要搜索的一个远程蓝牙设备,该方法和一对(键的话),以使用设备的发现功能有两种方法来查询的设备的列表。
该装置的发现是蓝牙功能的设备的过程中进行扫描是否在局部区域。为设备到发现的请求作出响应,必须具有有效的是有可能发现。(搭载的Android设备未在可能发现默认开启)
当它是第一次建立与远程设备的连接,配对的自动请求将被呈现给用户。当设备被配对,该装置的基本信息将被可使用蓝牙API(设备名称,类信息,例如MAC地址)尚未保存读取。因此,为了能够与已知的MAC地址到远程设备来初始化,则最好不要有运行的发现过程耗时。
应用程序中,首先提出了对设备到用户的列表,我认为这使得该装置的目标进行扫描用的设备的发现功能,除非该列表是可取的。BluetoothChat即使它已经以这种方式取得。
一对▼询问设备
到对器件的查询,调用getBondedDevices()方法。
此方法返回表示已配对设备一套BluetoothDevice类的。
在BluetoothChat,选择“连接设备”将被从选项菜单中执行时,对器件的询问。的源代码包含在DeviceListActivity.java。
[DeviceListActivity.java]
// Get the local Bluetooth adaptermBtAdapter = BluetoothAdapter.getDefaultAdapter(); // Get a set of currently paired devicesSet pairedDevices = mBtAdapter.getBondedDevices(); // If there are paired devices, add each one to the ArrayAdapterif (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);}
是MAC地址什么是需要BluetoothDevice类对象初始化连接。
在这个例子中,显示给用户(mPairedDevicesArrayAdapter)一个ArrayAdapter的一部分
作为已保存的MAC地址。(由的getAddress后天())
▼设备发现的
一对器件没有,或者,在对象被连接到配对的设备列表不存在,然后搜索是接近使用该设备的发现功能的蓝牙设备的情况。
要启动设备的发现,调用startDiscovery()方法。过程将是异步执行(被调用后立即返回startDiscovery())。发现的过程中,通常有大约12秒的查询扫描,然后页的扫描到寻找那些被发现之后每个设备的蓝牙名称。
在BluetoothChat上所显示DeviceListActivity当您选择从选项菜单“连接的设备”,该设备的发现将在执行“扫描设备”时进行。
以下是在BluetoothChat有关部分的源代码。
[DeviceListActivity.java]
@Overrideprotected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
:
// 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);
}
}); (途中省略) /**
* Start device discover with the BluetoothAdapter
*/private void doDiscovery() {
if (D) Log.d(TAG, "doDiscovery()"); // Indicate scanning in the title
setProgressBarIndeterminateVisibility(true);
setTitle(R.string.scanning); // Turn on sub-title for new devices
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE); // If we're already discovering, stop it
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
} // Request discover from BluetoothAdapter
mBtAdapter.startDiscovery();}
当您运行startDiscovery()时,系统会广播为每个设备ACTION_FOUND意图。该应用程序,以获得有关每个发现的设备的信息,您需要注册对ACTION_FOUND意图广播接收器。
以下是在BluetoothChat有关部分的源代码。
[DeviceListActivity.java]
// The BroadcastReceiver that listens for discovered devices and// changes the title when discovery is finishedprivate 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);
}
}
}};
发现过程的执行是蓝牙适配器一个沉重的处理,并消耗了大量的资源。有一次,我发现被连接的设备,你必须尝试连接前调用cancelDiscovery()停止的发现。
▼启用发现功能
进行说明,但是,搭载的Android设备不默认情况下,可能发现开启。如果要使得它可以发现本地设备从其他设备,调用startActivityForResult()中ACTION_REQUEST_DISCOVERABLE行动意图。
正如你已经启用了发现模式将通过系统配置公布结果。缺省情况下,设备,但它是120秒,习惯被发现,可以通过添加对意图EXTRA_DISCOVERABLE_DURATION附加信息来定义(最多300秒)为不同的时间段。
在BluetoothChat,使得在选择“让发现”从选项菜单做的时候发现功能。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChat.java]
private void ensureDiscoverable() {
if(D) Log.d(TAG, "ensure discoverable");
if (mBluetoothAdapter.getScanMode() !=
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}}
顺便说一句,有必要启用发现功能,只要你想举办一个接受该应用程序来在连接的服务器套接字。(不适用于连接作为一个客户端应用程序)
以上,要连接到的我们可以得到设备的MAC地址。然后描述该设备的连接。
●连接的设备
通过该应用程序,以创建该两个设备之间的连接时,将需要实现两个机制的服务器端和客户端。它必须是设备中的一个是打开一个服务器套接字,因为它需要将其他是初始化连接(初始化连接时,使用该服务器设备的MAC地址)。在相同的RFCOMM信道服务器和客户端,它被认为是已经连接到的情况下,每个接收到的已连接的BluetoothSocket。
在这一点上,每个设备都可以得到的输入和输出流,就可以开始数据传输。
服务器和客户端设备将赢得以不同的方式为每个所需的的BluetoothSocket。服务器收到的BluetoothSocket时传入的连接被接受,当你打开RFCOMM通道到服务器的客户端将收到的BluetoothSocket。
▼连接作为服务器
当你想在两个设备连接,你需要作为由一个服务器是打开BluetoothServerSocket持有它。服务器套接字的目的,并侦听传入的连接请求,就是当对方接受的连接,提供已经连接的BluetoothSocket。
当的BluetoothSocket从BluetoothServerSocket获得的,如果你不想接受进一步的连接,可以放弃BluetoothServerSocket。
1. listenUsingRfcommWithServiceRecord(字符串,UID)调用,以获得BluetoothServerSocket。
串,一个可以被识别的服务的名称,系统将设备它(服务发现协议:SDP)上自动新服务发现协议将写入数据库条目。(该名称是可选的,可以是一个简单的应用程序的名称)
此外,UUID也包含在SDP条目,这将是在与客户端设备的连接的组匹配。
为了确保连接被接受,你将需要这些UUID匹配。为UUID,后面描述。
2.调用accept()方法,并开始收听连接请求。
直到发生异常时此调用,连接被接受,将被阻止。
远程装置中,当服务器套接字发送了匹配连接请求注册的UUID,所述连接被接受。
成功的案例,accept()返回已连接的BluetoothSocket。
3.如果您不希望接受更多的连接,调用close()。
此服务器套接字和所有的资源被释放,但连接一直的BluetoothSocket返回的接受()未关闭。
不像TCP / IP协议,以允许RFCOMM是在同一时间,每个通道一个仅连接的客户端。因此,在大多数情况下,它接受一个连接的套接字之后立即调用BluetoothServerSocket的close()。
调用accept()方法,因为封锁的通话,你应该在一个单独的线程中运行。
处理,通常使用BluetoothServerSocket和的BluetoothSocket用一个新的线程,这是由应用程序管理处理。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChatService.java]
/**
* This thread runs while listening for incoming connections. It behaves
* like a server-side client. It runs until a connection is accepted
* (or until cancelled).
*/private class AcceptThread extends Thread {
// The local server socket
private final BluetoothServerSocket mmServerSocket; public AcceptThread() {
BluetoothServerSocket tmp = null; // Create a new listening server socket
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) {
Log.e(TAG, "listen() failed", e);
}
mmServerSocket = tmp;
} public void run() {
if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
setName("AcceptThread");
BluetoothSocket socket = null; // Listen to the server socket if we're not connected
while (mState != STATE_CONNECTED) {
try {
// This is a blocking call and will only return on a
// successful connection or an exception
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "accept() failed", e);
break;
} // If a connection was accepted
if (socket != null) {
synchronized (BluetoothChatService.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// Situation normal. Start the connected thread.
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
// Either not ready or already connected. Terminate new socket.
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}
}
if (D) Log.i(TAG, "END mAcceptThread");
} public void cancel() {
if (D) Log.d(TAG, "cancel " + this);
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of server failed", e);
}
}}
▼连接作为客户端
进行远程设备(即拥有一个开放的服务器套接字设备)的初始化,你需要赢得代表首次远程设备的BluetoothDevice类的对象。
然后,您需要初始化收购来连接使用BluetoothDevice类的的BluetoothSocket。
的基本程序如下。
1.使用BluetoothDevice类,通过调用createRfcommSocketToServiceRecord(UUID),初始化的BluetoothSocket。
UUID在这里通过,则需要以匹配使用由服务器设备时,服务器打开BluetoothServerSocket的UUID。
2.初始化通过调用connect连接()。
当远程设备接受,而你正在连接共享使用的连接,RFCOMM通道,你从连接回来()。
这种方法也已被呼叫。出于某种原因,异常如果connect()方法连接失败已成为发生超时。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChatService.java]
/**
* This thread runs while attempting to make an outgoing connection
* with a device. It runs straight through; the connection either
* succeeds or fails.
*/private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null; // Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
Log.e(TAG, "create() failed", e);
}
mmSocket = tmp;
} public void run() {
Log.i(TAG, "BEGIN mConnectThread");
setName("ConnectThread"); // Always cancel discovery because it will slow down a connection
mAdapter.cancelDiscovery(); // Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mmSocket.connect();
} catch (IOException e) {
connectionFailed();
// Close the socket
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() socket during connection failure", e2);
}
// Start the service over to restart listening mode
BluetoothChatService.this.start();
return;
} // Reset the ConnectThread because we're done
synchronized (BluetoothChatService.this) {
mConnectThread = null;
} // Start the connected thread
connected(mmSocket, mmDevice);
} public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}}
完成对远程设备或多个方面,我能够赢得必要的BluetoothSocket的发送和接收。这里,的UUID的简要说明,连接到PC等使用BluetoothChat时,这是一个问题。
★对于UUID
的UUID(通用唯一标识符=通用唯一标识符)是128位的标准格式,它会被用于字符串唯一标识信息的ID。
下表是蓝牙分配的数字文档中定义的最常见的UUID的列表。
如果通过SPP通信,必须指定00001101-0000-1000-8000-00805F9B34FB到UUID。
[BluetoothChatService.java]
// Unique UUID for this applicationprivate static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
// Unique UUID for SPP applicationprivate static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
●传输和接收数据
的设备连接成功后,你们每个人都已经连接的BluetoothSocket。
使用的BluetoothSocket,处理发送和接收的任何数据如下。
1.在的getOutputStream的getInputStream()()方法来获取InputStream和OutputStream使用套接字来办理过户。
2.流方面,阅读和在read()和write()方法写入数据。
阅读(),write()方法,因为阻止呼叫,你应该使用线程流的读取和写入。
以下是在BluetoothChat有关部分的源代码。
[BluetoothChatService.java]
/**
* This thread runs during a connection with a remote device.
* It handles all incoming and outgoing transmissions.
*/private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream; public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, "create ConnectedThread");
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null; // Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
} 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 = mmInStream.read(buffer); // 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;
}
}
} /**
* Write to the connected OutStream.
* @param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer); // Share the sent message back to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
} public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}}
●检查操作
在模拟器上的,因为它不执行蓝牙功能的确认,您需要使用实际的设备做验证。
在这一次的远程端,我们使用那些有蓝牙适配器到您的台式PC的USB类型。您分配一个COM端口在蓝牙设置屏幕传入。在PC端的软件用于TeraTerm。在Android终端上的UUID安装BluetoothChat改变为一个用于SPP,并运行。
这里,出现问题。。。
它不杀尽快运行BluetoothChat开始。启用USB调试终端,进行真机调试,USB连接,onResume()被称为在启动的时候,我们发现当你调用启动的BluetoothChatService()方法,从他们中间那个秋天。
因为我不知道好(这似乎是实例的关系),但现在通过注释掉相应行正常工作。
[BluetoothChat.java]
@Override public synchronized void onResume() {
super.onResume();
if(D) Log.e(TAG, "+ ON RESUME +"); // Performing this check in onResume() covers the case in which BT was
// not enabled during onStart(), so we were paused to enable it...
// onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
if (mChatService != null) {
// Only if the state is STATE_NONE, do we know that we haven't started already
if (mChatService.getState() == BluetoothChatService.STATE_NONE) {
// Start the Bluetooth chat services
// mChatService.start();
}
}
在启动后,运行从选项菜单中的“连接设备”,并连接到从列表中选择PC的主题。(对于在这种情况下,第一次,扫描设备运行)
连接到并连接到标题的右上角:我认为这是(PC名称)和显示。
输入在Android终端侧按“Android的蓝牙SPP测试”[发送]按钮,
确认已收到在PC端的字符串后,表示“OK(行)”当你进入屏幕以下。
尽管有改进,如下面被发现,就可以确认是否可以成功地发送和接收。
-分隔符并不在句子中的新行代码在传输时不添加坚持
和接收是不是已经进入每次在一行接一行的基础,并输出到日志中的字符(1字符在时间显示)
这个区域,我认为,可以通过查看阅读的地方()从实现和write()方法的ConnectedThread流得到改善。
此外,我认为这是通过查看TeraTerm屏幕注意到,但在PC端的波特率是相当快,921600bps。
以上,我想结束蓝牙SPP通信。