是设备解决方案的一部分。
由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
选择
通过该操作,下面的描述将被添加到清单。
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
●收购并启用蓝牙适配器
在应用程序通过蓝牙进行沟通,首先,你需要确保蓝牙设备支持。
调用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<BluetoothDevice> 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通信。