Android 蓝牙开发实例--蓝牙聊天程序的设计和实现

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现


转自:http://blog.csdn.net/dlutbrucezhang/article/details/8963696

作者在这里介绍的这个实例是Google SDK中提供的一个蓝牙聊天程序,简单但信息量巨大,非常适合初学者学习蓝牙方面的知识。

在学习这个实例前请读者仔细阅读并理解Socket的工作原理和实现机制,作者的这篇博客中有详细的介绍:

http://blog.csdn.net/dlutbrucezhang/article/details/8577810


在 Android1.x 的时候,相关 API 非常不完善,还不能简单的使用 Bluetooth 开发,有一个开源项目可以帮助程序员使用、开发蓝牙,支持直接方法 bluetooth 协议栈。在 Android2 以后,框架提供了一些官方 API 来进行蓝牙的通信,但目前的程序也比较不完善。本文主要讨论 Android2 后的 Bluetooth 通信的 API 使用方法。

首先看聊天室的效果图:

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现


蓝牙设备连接的过程如下图所示:

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现


下面这张图展示的是蓝牙聊天的时序图:

Android 蓝牙开发实例--蓝牙聊天程序的设计和实现


接下来将贴出源码并对源码做出详细的解释说明:

BluetoothChat.java

例程的主 Activity 。 onCreate() 得到本地 BluetoothAdapter 设备,检查是否支持。 onStart() 中检查是否启用蓝牙,并请求启用,然后执行 setupChat() 。 setupChat() 中先对界面中的控件进行初始化增加点击监听器等,然创建 BluetoothChatService 对象,该对象在整个应用过程中存在,并执行蓝牙连接建立、消息发送接受等实际的行为。

  1. import android.app.Activity;  
  2. import android.bluetooth.BluetoothAdapter;  
  3. import android.bluetooth.BluetoothDevice;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6. import android.os.Handler;  
  7. import android.os.Message;  
  8. import android.util.Log;  
  9. import android.view.KeyEvent;  
  10. import android.view.Menu;  
  11. import android.view.MenuInflater;  
  12. import android.view.MenuItem;  
  13. import android.view.View;  
  14. import android.view.Window;  
  15. import android.view.View.OnClickListener;  
  16. import android.view.inputmethod.EditorInfo;  
  17. import android.widget.ArrayAdapter;  
  18. import android.widget.Button;  
  19. import android.widget.EditText;  
  20. import android.widget.ListView;  
  21. import android.widget.TextView;  
  22. import android.widget.Toast;  
  23.   
  24. /** 
  25.  * This is the main Activity that displays the current chat session. 
  26.  */  
  27. public class BluetoothChat extends Activity {  
  28.     // Debugging  
  29.     private static final String TAG = "BluetoothChat";  
  30.     private static final boolean D = true;  
  31.   
  32.     // Message types sent from the BluetoothChatService Handler  
  33.     public static final int MESSAGE_STATE_CHANGE = 1;  
  34.     public static final int MESSAGE_READ = 2;  
  35.     public static final int MESSAGE_WRITE = 3;  
  36.     public static final int MESSAGE_DEVICE_NAME = 4;  
  37.     public static final int MESSAGE_TOAST = 5;  
  38.   
  39.     // Key names received from the BluetoothChatService Handler  
  40.     public static final String DEVICE_NAME = "device_name";  
  41.     public static final String TOAST = "toast";  
  42.   
  43.     // Intent request codes  
  44.     private static final int REQUEST_CONNECT_DEVICE = 1;  
  45.     private static final int REQUEST_ENABLE_BT = 2;  
  46.   
  47.     // Layout Views  
  48.     private TextView mTitle;  
  49.     private ListView mConversationView;  
  50.     private EditText mOutEditText;  
  51.     private Button mSendButton;  
  52.   
  53.     // Name of the connected device  
  54.     private String mConnectedDeviceName = null;  
  55.     // Array adapter for the conversation thread  
  56.     private ArrayAdapter<String> mConversationArrayAdapter;  
  57.     // String buffer for outgoing messages  
  58.     private StringBuffer mOutStringBuffer;  
  59.     // Local Bluetooth adapter  
  60.     private BluetoothAdapter mBluetoothAdapter = null;  
  61.     // Member object for the chat services  
  62.     private BluetoothChatService mChatService = null;  
  63.   
  64.   
  65.     @Override  
  66.     public void onCreate(Bundle savedInstanceState) {  
  67.         super.onCreate(savedInstanceState);  
  68.         if(D) Log.e(TAG, "+++ ON CREATE +++");  
  69.   
  70.         // Set up the window layout  
  71.         requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);  
  72.         setContentView(R.layout.main);  
  73.         getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);  
  74.   
  75.         // Set up the custom title  
  76.         mTitle = (TextView) findViewById(R.id.title_left_text);  
  77.         mTitle.setText(R.string.app_name);  
  78.         mTitle = (TextView) findViewById(R.id.title_right_text);  
  79.   
  80.         // Get local Bluetooth adapter  
  81.         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();  
  82.   
  83.         // If the adapter is null, then Bluetooth is not supported  
  84.         if (mBluetoothAdapter == null) {  
  85.             Toast.makeText(this"Bluetooth is not available", Toast.LENGTH_LONG).show();  
  86.             finish();  
  87.             return;  
  88.         }  
  89.     }  
  90.   
  91.     @Override  
  92.     public void onStart() {  
  93.         super.onStart();  
  94.         if(D) Log.e(TAG, "++ ON START ++");  
  95.   
  96.         // If BT is not on, request that it be enabled.  
  97.         // setupChat() will then be called during onActivityResult  
  98.         if (!mBluetoothAdapter.isEnabled()) {  
  99.             Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);  
  100.             startActivityForResult(enableIntent, REQUEST_ENABLE_BT);  
  101.         // Otherwise, setup the chat session  
  102.         } else {  
  103.             if (mChatService == null) setupChat();  
  104.         }  
  105.     }  
  106.   
  107.     @Override  
  108.     public synchronized void onResume() {  
  109.         super.onResume();  
  110.         if(D) Log.e(TAG, "+ ON RESUME +");  
  111.   
  112.         // Performing this check in onResume() covers the case in which BT was  
  113.         // not enabled during onStart(), so we were paused to enable it...  
  114.         // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.  
  115.         if (mChatService != null) {  
  116.             // Only if the state is STATE_NONE, do we know that we haven't started already  
  117.             if (mChatService.getState() == BluetoothChatService.STATE_NONE) {  
  118.               // Start the Bluetooth chat services  
  119.               mChatService.start();  
  120.             }  
  121.         }  
  122.     }  
  123.   
  124.     private void setupChat() {  
  125.         Log.d(TAG, "setupChat()");  
  126.   
  127.         // Initialize the array adapter for the conversation thread  
  128.         mConversationArrayAdapter = new ArrayAdapter<String>(this, R.layout.message);  
  129.         mConversationView = (ListView) findViewById(R.id.in);  
  130.         mConversationView.setAdapter(mConversationArrayAdapter);  
  131.   
  132.         // Initialize the compose field with a listener for the return key  
  133.         mOutEditText = (EditText) findViewById(R.id.edit_text_out);  
  134.         mOutEditText.setOnEditorActionListener(mWriteListener);  
  135.   
  136.         // Initialize the send button with a listener that for click events  
  137.         mSendButton = (Button) findViewById(R.id.button_send);  
  138.         mSendButton.setOnClickListener(new OnClickListener() {  
  139.             public void onClick(View v) {  
  140.                 // Send a message using content of the edit text widget  
  141.                 TextView view = (TextView) findViewById(R.id.edit_text_out);  
  142.                 String message = view.getText().toString();  
  143.                 sendMessage(message);  
  144.             }  
  145.         });  
  146.   
  147.         // Initialize the BluetoothChatService to perform bluetooth connections  
  148.         mChatService = new BluetoothChatService(this, mHandler);  
  149.   
  150.         // Initialize the buffer for outgoing messages  
  151.         mOutStringBuffer = new StringBuffer("");  
  152.     }  
  153.   
  154.     @Override  
  155.     public synchronized void onPause() {  
  156.         super.onPause();  
  157.         if(D) Log.e(TAG, "- ON PAUSE -");  
  158.     }  
  159.   
  160.     @Override  
  161.     public void onStop() {  
  162.         super.onStop();  
  163.         if(D) Log.e(TAG, "-- ON STOP --");  
  164.     }  
  165.   
  166.     @Override  
  167.     public void onDestroy() {  
  168.         super.onDestroy();  
  169.         // Stop the Bluetooth chat services  
  170.         if (mChatService != null) mChatService.stop();  
  171.         if(D) Log.e(TAG, "--- ON DESTROY ---");  
  172.     }  
  173.   
  174.     private void ensureDiscoverable() {  
  175.         if(D) Log.d(TAG, "ensure discoverable");  
  176.         if (mBluetoothAdapter.getScanMode() !=  
  177.             BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {  
  178.             Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);  
  179.             discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);  
  180.             startActivity(discoverableIntent);  
  181.         }  
  182.     }  
  183.   
  184.     /** 
  185.      * Sends a message. 
  186.      * @param message  A string of text to send. 
  187.      */  
  188.     private void sendMessage(String message) {  
  189.         // Check that we're actually connected before trying anything  
  190.         if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {  
  191.             Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();  
  192.             return;  
  193.         }  
  194.   
  195.         // Check that there's actually something to send  
  196.         if (message.length() > 0) {  
  197.             // Get the message bytes and tell the BluetoothChatService to write  
  198.             byte[] send = message.getBytes();  
  199.             mChatService.write(send);  
  200.   
  201.             // Reset out string buffer to zero and clear the edit text field  
  202.             mOutStringBuffer.setLength(0);  
  203.             mOutEditText.setText(mOutStringBuffer);  
  204.         }  
  205.     }  
  206.   
  207.     // The action listener for the EditText widget, to listen for the return key  
  208.     private TextView.OnEditorActionListener mWriteListener =  
  209.         new TextView.OnEditorActionListener() {  
  210.         public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {  
  211.             // If the action is a key-up event on the return key, send the message  
  212.             if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {  
  213.                 String message = view.getText().toString();  
  214.                 sendMessage(message);  
  215.             }  
  216.             if(D) Log.i(TAG, "END onEditorAction");  
  217.             return true;  
  218.         }  
  219.     };  
  220.   
  221.     // The Handler that gets information back from the BluetoothChatService  
  222.     private final Handler mHandler = new Handler() {  
  223.         @Override  
  224.         public void handleMessage(Message msg) {  
  225.             switch (msg.what) {  
  226.             case MESSAGE_STATE_CHANGE:  
  227.                 if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);  
  228.                 switch (msg.arg1) {  
  229.                 case BluetoothChatService.STATE_CONNECTED:  
  230.                     mTitle.setText(R.string.title_connected_to);  
  231.                     mTitle.append(mConnectedDeviceName);  
  232.                     mConversationArrayAdapter.clear();  
  233.                     break;  
  234.                 case BluetoothChatService.STATE_CONNECTING:  
  235.                     mTitle.setText(R.string.title_connecting);  
  236.                     break;  
  237.                 case BluetoothChatService.STATE_LISTEN:  
  238.                 case BluetoothChatService.STATE_NONE:  
  239.                     mTitle.setText(R.string.title_not_connected);  
  240.                     break;  
  241.                 }  
  242.                 break;  
  243.             case MESSAGE_WRITE:  
  244.                 byte[] writeBuf = (byte[]) msg.obj;  
  245.                 // construct a string from the buffer  
  246.                 String writeMessage = new String(writeBuf);  
  247.                 mConversationArrayAdapter.add("Me:  " + writeMessage);  
  248.                 break;  
  249.             case MESSAGE_READ:  
  250.                 byte[] readBuf = (byte[]) msg.obj;  
  251.                 // construct a string from the valid bytes in the buffer  
  252.                 String readMessage = new String(readBuf, 0, msg.arg1);  
  253.                 mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);  
  254.                 break;  
  255.             case MESSAGE_DEVICE_NAME:  
  256.                 // save the connected device's name  
  257.                 mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);  
  258.                 Toast.makeText(getApplicationContext(), "Connected to "  
  259.                                + mConnectedDeviceName, Toast.LENGTH_SHORT).show();  
  260.                 break;  
  261.             case MESSAGE_TOAST:  
  262.                 Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),  
  263.                                Toast.LENGTH_SHORT).show();  
  264.                 break;  
  265.             }  
  266.         }  
  267.     };  
  268.   
  269.     public void onActivityResult(int requestCode, int resultCode, Intent data) {  
  270.         if(D) Log.d(TAG, "onActivityResult " + resultCode);  
  271.         switch (requestCode) {  
  272.         case REQUEST_CONNECT_DEVICE:  
  273.             // When DeviceListActivity returns with a device to connect  
  274.             if (resultCode == Activity.RESULT_OK) {  
  275.                 // Get the device MAC address  
  276.                 String address = data.getExtras()  
  277.                                      .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);  
  278.                 // Get the BLuetoothDevice object  
  279.                 BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);  
  280.                 // Attempt to connect to the device  
  281.                 mChatService.connect(device);  
  282.             }  
  283.             break;  
  284.         case REQUEST_ENABLE_BT:  
  285.             // When the request to enable Bluetooth returns  
  286.             if (resultCode == Activity.RESULT_OK) {  
  287.                 // Bluetooth is now enabled, so set up a chat session  
  288.                 setupChat();  
  289.             } else {  
  290.                 // User did not enable Bluetooth or an error occured  
  291.                 Log.d(TAG, "BT not enabled");  
  292.                 Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();  
  293.                 finish();  
  294.             }  
  295.         }  
  296.     }  
  297.   
  298.     @Override  
  299.     public boolean onCreateOptionsMenu(Menu menu) {  
  300.         MenuInflater inflater = getMenuInflater();  
  301.         inflater.inflate(R.menu.option_menu, menu);  
  302.         return true;  
  303.     }  
  304.   
  305.     @Override  
  306.     public boolean onOptionsItemSelected(MenuItem item) {  
  307.         switch (item.getItemId()) {  
  308.         case R.id.scan:  
  309.             // Launch the DeviceListActivity to see devices and do scan  
  310.             Intent serverIntent = new Intent(this, DeviceListActivity.class);  
  311.             startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);  
  312.             return true;  
  313.         case R.id.discoverable:  
  314.             // Ensure this device is discoverable by others  
  315.             ensureDiscoverable();  
  316.             return true;  
  317.         }  
  318.         return false;  
  319.     }  
  320.   
  321. }  

BluetoothChatService.java

public synchronized void start() :

开启 mAcceptThread 线程,由于样例程序是仅 2 人的聊天过程,故之前先检测 mConnectThread 和 mConnectedThread 是否运行,运行则先退出这些线程。

public synchronized voidconnect(BluetoothDevice device) :

取消 CONNECTING 和 CONNECTED 状态下的相关线程,然后运行新的 mConnectThread 线程。

public synchronized voidconnected(BluetoothSocket socket, BluetoothDevice device) :

开启一个 ConnectedThread 来管理对应的当前连接。之前先取消任意现存的 mConnectThread 、 mConnectedThread 、 mAcceptThread 线程,然后开启新 mConnectedThread ,传入当前刚刚接受的socket 连接。最后通过 Handler 来通知 UI 连接 OK 。

public synchronized void stop() :

停止所有相关线程,设当前状态为 NONE 。

public void write(byte[] out) :

在 STATE_CONNECTED 状态下,调用 mConnectedThread 里的 write 方法,写入 byte 。

private void connectionFailed() :

连接失败的时候处理,通知 ui ,并设为 STATE_LISTEN 状态。

private void connectionLost() :

当连接失去的时候,设为 STATE_LISTEN 状态并通知 ui 。

内部类:

private class AcceptThread extendsThread :

创建监听线程,准备接受新连接。使用阻塞方式,调用 BluetoothServerSocket.accept() 。提供 cancel 方法关闭 socket 。

private class ConnectThread extendsThread :

这是定义的连接线程,专门用来对外发出连接对方蓝牙的请求和处理流程。构造函数里通过 BluetoothDevice.createRfcommSocketToServiceRecord() ,从待连接的 device 产生 BluetoothSocket. 然后在run 方法中 connect ,成功后调用 BluetoothChatSevice 的 connected() 方法。定义 cancel() 在关闭线程时能够关闭相关 socket 。

private class ConnectedThread extendsThread :

这个是双方蓝牙连接后一直运行的线程。构造函数中设置输入输出流。 Run 方法中使用阻塞模式的 InputStream.read() 循环读取输入流, 然后 post 到 UI 线程中更新聊天消息。也提供了 write() 将聊天消息写入输出流传输至对方,传输成功后回写入 UI 线程。最后 cancel() 关闭连接的 socket 。

  1. import java.io.IOException;  
  2. import java.io.InputStream;  
  3. import java.io.OutputStream;  
  4. import java.util.UUID;  
  5.   
  6. import android.annotation.SuppressLint;  
  7. import android.bluetooth.BluetoothAdapter;  
  8. import android.bluetooth.BluetoothDevice;  
  9. import android.bluetooth.BluetoothServerSocket;  
  10. import android.bluetooth.BluetoothSocket;  
  11. import android.content.Context;  
  12. import android.os.Bundle;  
  13. import android.os.Handler;  
  14. import android.os.Message;  
  15. import android.util.Log;  
  16.   
  17. /** 
  18.  * This class does all the work for setting up and managing Bluetooth 
  19.  * connections with other devices. It has a thread that listens for 
  20.  * incoming connections, a thread for connecting with a device, and a 
  21.  * thread for performing data transmissions when connected. 
  22.  */  
  23. @SuppressLint("NewApi")  
  24. public class BluetoothChatService {  
  25.     // Debugging  
  26.     private static final String TAG = "BluetoothChatService";  
  27.     private static final boolean D = true;  
  28.   
  29.     // Name for the SDP record when creating server socket  
  30.     private static final String NAME = "BluetoothChat";  
  31.   
  32.     // Unique UUID for this application  
  33.     private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");  
  34.   
  35.     // Member fields  
  36.     private final BluetoothAdapter mAdapter;  
  37.     private final Handler mHandler;  
  38.     private AcceptThread mAcceptThread;  
  39.     private ConnectThread mConnectThread;  
  40.     private ConnectedThread mConnectedThread;  
  41.     private int mState;  
  42.   
  43.     // Constants that indicate the current connection state  
  44.     public static final int STATE_NONE = 0;       // we're doing nothing  
  45.     public static final int STATE_LISTEN = 1;     // now listening for incoming connections  
  46.     public static final int STATE_CONNECTING = 2// now initiating an outgoing connection  
  47.     public static final int STATE_CONNECTED = 3;  // now connected to a remote device  
  48.   
  49.     /** 
  50.      * Constructor. Prepares a new BluetoothChat session. 
  51.      * @param context  The UI Activity Context 
  52.      * @param handler  A Handler to send messages back to the UI Activity 
  53.      */  
  54.     public BluetoothChatService(Context context, Handler handler) {  
  55.         mAdapter = BluetoothAdapter.getDefaultAdapter();  
  56.         mState = STATE_NONE;  
  57.         mHandler = handler;  
  58.     }  
  59.   
  60.     /** 
  61.      * Set the current state of the chat connection 
  62.      * @param state  An integer defining the current connection state 
  63.      */  
  64.     private synchronized void setState(int state) {  
  65.         if (D) Log.d(TAG, "setState() " + mState + " -> " + state);  
  66.         mState = state;  
  67.   
  68.         // Give the new state to the Handler so the UI Activity can update  
  69.         mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();  
  70.     }  
  71.   
  72.     /** 
  73.      * Return the current connection state. */  
  74.     public synchronized int getState() {  
  75.         return mState;  
  76.     }  
  77.   
  78.     /** 
  79.      * Start the chat service. Specifically start AcceptThread to begin a 
  80.      * session in listening (server) mode. Called by the Activity onResume() */  
  81.     public synchronized void start() {  
  82.         if (D) Log.d(TAG, "start");  
  83.   
  84.         // Cancel any thread attempting to make a connection  
  85.         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}  
  86.   
  87.         // Cancel any thread currently running a connection  
  88.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}  
  89.   
  90.         // Start the thread to listen on a BluetoothServerSocket  
  91.         if (mAcceptThread == null) {  
  92.             mAcceptThread = new AcceptThread();  
  93.             mAcceptThread.start();  
  94.         }  
  95.         setState(STATE_LISTEN);  
  96.     }  
  97.   
  98.     /** 
  99.      * Start the ConnectThread to initiate a connection to a remote device. 
  100.      * @param device  The BluetoothDevice to connect 
  101.      */  
  102.     public synchronized void connect(BluetoothDevice device) {  
  103.         if (D) Log.d(TAG, "connect to: " + device);  
  104.   
  105.         // Cancel any thread attempting to make a connection  
  106.         if (mState == STATE_CONNECTING) {  
  107.             if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}  
  108.         }  
  109.   
  110.         // Cancel any thread currently running a connection  
  111.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}  
  112.   
  113.         // Start the thread to connect with the given device  
  114.         mConnectThread = new ConnectThread(device);  
  115.         mConnectThread.start();  
  116.         setState(STATE_CONNECTING);  
  117.     }  
  118.   
  119.     /** 
  120.      * Start the ConnectedThread to begin managing a Bluetooth connection 
  121.      * @param socket  The BluetoothSocket on which the connection was made 
  122.      * @param device  The BluetoothDevice that has been connected 
  123.      */  
  124.     public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {  
  125.         if (D) Log.d(TAG, "connected");  
  126.   
  127.         // Cancel the thread that completed the connection  
  128.         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}  
  129.   
  130.         // Cancel any thread currently running a connection  
  131.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}  
  132.   
  133.         // Cancel the accept thread because we only want to connect to one device  
  134.         if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}  
  135.   
  136.         // Start the thread to manage the connection and perform transmissions  
  137.         mConnectedThread = new ConnectedThread(socket);  
  138.         mConnectedThread.start();  
  139.   
  140.         // Send the name of the connected device back to the UI Activity  
  141.         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);  
  142.         Bundle bundle = new Bundle();  
  143.         bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());  
  144.         msg.setData(bundle);  
  145.         mHandler.sendMessage(msg);  
  146.   
  147.         setState(STATE_CONNECTED);  
  148.     }  
  149.   
  150.     /** 
  151.      * Stop all threads 
  152.      */  
  153.     public synchronized void stop() {  
  154.         if (D) Log.d(TAG, "stop");  
  155.         if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}  
  156.         if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}  
  157.         if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}  
  158.         setState(STATE_NONE);  
  159.     }  
  160.   
  161.     /** 
  162.      * Write to the ConnectedThread in an unsynchronized manner 
  163.      * @param out The bytes to write 
  164.      * @see ConnectedThread#write(byte[]) 
  165.      */  
  166.     public void write(byte[] out) {  
  167.         // Create temporary object  
  168.         ConnectedThread r;  
  169.         // Synchronize a copy of the ConnectedThread  
  170.         synchronized (this) {  
  171.             if (mState != STATE_CONNECTED) return;  
  172.             r = mConnectedThread;  
  173.         }  
  174.         // Perform the write unsynchronized  
  175.         r.write(out);  
  176.     }  
  177.   
  178.     /** 
  179.      * Indicate that the connection attempt failed and notify the UI Activity. 
  180.      */  
  181.     private void connectionFailed() {  
  182.         setState(STATE_LISTEN);  
  183.   
  184.         // Send a failure message back to the Activity  
  185.         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);  
  186.         Bundle bundle = new Bundle();  
  187.         bundle.putString(BluetoothChat.TOAST, "Unable to connect device");  
  188.         msg.setData(bundle);  
  189.         mHandler.sendMessage(msg);  
  190.     }  
  191.   
  192.     /** 
  193.      * Indicate that the connection was lost and notify the UI Activity. 
  194.      */  
  195.     private void connectionLost() {  
  196.         setState(STATE_LISTEN);  
  197.   
  198.         // Send a failure message back to the Activity  
  199.         Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);  
  200.         Bundle bundle = new Bundle();  
  201.         bundle.putString(BluetoothChat.TOAST, "Device connection was lost");  
  202.         msg.setData(bundle);  
  203.         mHandler.sendMessage(msg);  
  204.     }  
  205.   
  206.     /** 
  207.      * This thread runs while listening for incoming connections. It behaves 
  208.      * like a server-side client. It runs until a connection is accepted 
  209.      * (or until cancelled). 
  210.      */  
  211.     @SuppressLint("NewApi")  
  212.     private class AcceptThread extends Thread {  
  213.         // The local server socket  
  214.         private final BluetoothServerSocket mmServerSocket;  
  215.   
  216.         public AcceptThread() {  
  217.             BluetoothServerSocket tmp = null;  
  218.   
  219.             // Create a new listening server socket  
  220.             try {  
  221.                 //开启监听  
  222.                 tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);  
  223.             } catch (IOException e) {  
  224.                 Log.e(TAG, "listen() failed", e);  
  225.             }  
  226.             mmServerSocket = tmp;  
  227.         }  
  228.   
  229.         public void run() {  
  230.             if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);  
  231.             setName("AcceptThread");  
  232.             BluetoothSocket socket = null;  
  233.   
  234.             // Listen to the server socket if we're not connected  
  235.             while (mState != STATE_CONNECTED) {  
  236.                 try {  
  237.                     // This is a blocking call and will only return on a  
  238.                     // successful connection or an exception  
  239.                     socket = mmServerSocket.accept();  
  240.                 } catch (IOException e) {  
  241.                     Log.e(TAG, "accept() failed", e);  
  242.                     break;  
  243.                 }  
  244.   
  245.                 // If a connection was accepted  
  246.                 if (socket != null) {  
  247.                     synchronized (BluetoothChatService.this) {  
  248.                         switch (mState) {  
  249.                         case STATE_LISTEN:  
  250.                         case STATE_CONNECTING:  
  251.                             // Situation normal. Start the connected thread.  
  252.                             connected(socket, socket.getRemoteDevice());  
  253.                             break;  
  254.                         case STATE_NONE:  
  255.                         case STATE_CONNECTED:  
  256.                             // Either not ready or already connected. Terminate new socket.  
  257.                             try {  
  258.                                 socket.close();  
  259.                             } catch (IOException e) {  
  260.                                 Log.e(TAG, "Could not close unwanted socket", e);  
  261.                             }  
  262.                             break;  
  263.                         }  
  264.                     }  
  265.                 }  
  266.             }  
  267.             if (D) Log.i(TAG, "END mAcceptThread");  
  268.         }  
  269.   
  270.         public void cancel() {  
  271.             if (D) Log.d(TAG, "cancel " + this);  
  272.             try {  
  273.                 mmServerSocket.close();  
  274.             } catch (IOException e) {  
  275.                 Log.e(TAG, "close() of server failed", e);  
  276.             }  
  277.         }  
  278.     }  
  279.   
  280.   
  281.     /** 
  282.      * This thread runs while attempting to make an outgoing connection 
  283.      * with a device. It runs straight through; the connection either 
  284.      * succeeds or fails. 
  285.      */  
  286.     private class ConnectThread extends Thread {  
  287.         private final BluetoothSocket mmSocket;  
  288.         private final BluetoothDevice mmDevice;  
  289.   
  290.         public ConnectThread(BluetoothDevice device) {  
  291.             mmDevice = device;  
  292.             BluetoothSocket tmp = null;  
  293.   
  294.             // Get a BluetoothSocket for a connection with the  
  295.             // given BluetoothDevice  
  296.             try {  
  297.                 tmp = device.createRfcommSocketToServiceRecord(MY_UUID);  
  298.             } catch (IOException e) {  
  299.                 Log.e(TAG, "create() failed", e);  
  300.             }  
  301.             mmSocket = tmp;  
  302.         }  
  303.   
  304.         public void run() {  
  305.             Log.i(TAG, "BEGIN mConnectThread");  
  306.             setName("ConnectThread");  
  307.   
  308.             // Always cancel discovery because it will slow down a connection  
  309.             mAdapter.cancelDiscovery();  
  310.   
  311.             // Make a connection to the BluetoothSocket  
  312.             try {  
  313.                 // This is a blocking call and will only return on a  
  314.                 // successful connection or an exception  
  315.                 mmSocket.connect();  
  316.             } catch (IOException e) {  
  317.                 connectionFailed();  
  318.                 // Close the socket  
  319.                 try {  
  320.                     mmSocket.close();  
  321.                 } catch (IOException e2) {  
  322.                     Log.e(TAG, "unable to close() socket during connection failure", e2);  
  323.                 }  
  324.                 // Start the service over to restart listening mode  
  325.                 BluetoothChatService.this.start();  
  326.                 return;  
  327.             }  
  328.   
  329.             // Reset the ConnectThread because we're done  
  330.             synchronized (BluetoothChatService.this) {  
  331.                 mConnectThread = null;  
  332.             }  
  333.   
  334.             // Start the connected thread  
  335.             connected(mmSocket, mmDevice);  
  336.         }  
  337.   
  338.         public void cancel() {  
  339.             try {  
  340.                 mmSocket.close();  
  341.             } catch (IOException e) {  
  342.                 Log.e(TAG, "close() of connect socket failed", e);  
  343.             }  
  344.         }  
  345.     }  
  346.   
  347.     /** 
  348.      * This thread runs during a connection with a remote device. 
  349.      * It handles all incoming and outgoing transmissions. 
  350.      */  
  351.     private class ConnectedThread extends Thread {  
  352.         private final BluetoothSocket mmSocket;  
  353.         private final InputStream mmInStream;  
  354.         private final OutputStream mmOutStream;  
  355.   
  356.         public ConnectedThread(BluetoothSocket socket) {  
  357.             Log.d(TAG, "create ConnectedThread");  
  358.             mmSocket = socket;  
  359.             InputStream tmpIn = null;  
  360.             OutputStream tmpOut = null;  
  361.   
  362.             // Get the BluetoothSocket input and output streams  
  363.             try {  
  364.                 tmpIn = socket.getInputStream();  
  365.                 tmpOut = socket.getOutputStream();  
  366.             } catch (IOException e) {  
  367.                 Log.e(TAG, "temp sockets not created", e);  
  368.             }  
  369.   
  370.             mmInStream = tmpIn;  
  371.             mmOutStream = tmpOut;  
  372.         }  
  373.   
  374.         public void run() {  
  375.             Log.i(TAG, "BEGIN mConnectedThread");  
  376.             byte[] buffer = new byte[1024];  
  377.             int bytes;  
  378.   
  379.             // Keep listening to the InputStream while connected  
  380.             while (true) {  
  381.                 try {  
  382.                     // Read from the InputStream  
  383.                     bytes = mmInStream.read(buffer);  
  384.   
  385.                     // Send the obtained bytes to the UI Activity  
  386.                     mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)  
  387.                             .sendToTarget();  
  388.                 } catch (IOException e) {  
  389.                     Log.e(TAG, "disconnected", e);  
  390.                     connectionLost();  
  391.                     break;  
  392.                 }  
  393.             }  
  394.         }  
  395.   
  396.         /** 
  397.          * Write to the connected OutStream. 
  398.          * @param buffer  The bytes to write 
  399.          */  
  400.         public void write(byte[] buffer) {  
  401.             try {  
  402.                 mmOutStream.write(buffer);  
  403.   
  404.                 // Share the sent message back to the UI Activity  
  405.                 mHandler.obtainMessage(BluetoothChat.MESSAGE_WRITE, -1, -1, buffer)  
  406.                         .sendToTarget();  
  407.             } catch (IOException e) {  
  408.                 Log.e(TAG, "Exception during write", e);  
  409.             }  
  410.         }  
  411.   
  412.         public void cancel() {  
  413.             try {  
  414.                 mmSocket.close();  
  415.             } catch (IOException e) {  
  416.                 Log.e(TAG, "close() of connect socket failed", e);  
  417.             }  
  418.         }  
  419.     }  
  420. }  

DeviceListActivity.java

该类包含 UI 和操作的 Activity 类,作用是得到系统默认蓝牙设备的已配对设备列表,以及搜索出的未配对的新设备的列表。然后提供点击后发出连接设备请求的功能。

  1. import java.util.Set;  
  2.   
  3. import android.annotation.SuppressLint;  
  4. import android.app.Activity;  
  5. import android.bluetooth.BluetoothAdapter;  
  6. import android.bluetooth.BluetoothDevice;  
  7. import android.content.BroadcastReceiver;  
  8. import android.content.Context;  
  9. import android.content.Intent;  
  10. import android.content.IntentFilter;  
  11. import android.os.Bundle;  
  12. import android.util.Log;  
  13. import android.view.View;  
  14. import android.view.Window;  
  15. import android.view.View.OnClickListener;  
  16. import android.widget.AdapterView;  
  17. import android.widget.ArrayAdapter;  
  18. import android.widget.Button;  
  19. import android.widget.ListView;  
  20. import android.widget.TextView;  
  21. import android.widget.AdapterView.OnItemClickListener;  
  22.   
  23. /** 
  24.  * This Activity appears as a dialog. It lists any paired devices and 
  25.  * devices detected in the area after discovery. When a device is chosen 
  26.  * by the user, the MAC address of the device is sent back to the parent 
  27.  * Activity in the result Intent. 
  28.  */  
  29. @SuppressLint("NewApi")  
  30. public class DeviceListActivity extends Activity {  
  31.     // Debugging  
  32.     private static final String TAG = "DeviceListActivity";  
  33.     private static final boolean D = true;  
  34.   
  35.     // Return Intent extra  
  36.     public static String EXTRA_DEVICE_ADDRESS = "device_address";  
  37.   
  38.     // Member fields  
  39.     private BluetoothAdapter mBtAdapter;  
  40.     private ArrayAdapter<String> mPairedDevicesArrayAdapter;  
  41.     private ArrayAdapter<String> mNewDevicesArrayAdapter;  
  42.   
  43.     @Override  
  44.     protected void onCreate(Bundle savedInstanceState) {  
  45.         super.onCreate(savedInstanceState);  
  46.   
  47.         // Setup the window  
  48.         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);  
  49.         setContentView(R.layout.device_list);  
  50.   
  51.         // Set result CANCELED incase the user backs out  
  52.         setResult(Activity.RESULT_CANCELED);  
  53.   
  54.         // Initialize the button to perform device discovery  
  55.         Button scanButton = (Button) findViewById(R.id.button_scan);  
  56.         scanButton.setOnClickListener(new OnClickListener() {  
  57.             public void onClick(View v) {  
  58.                 doDiscovery();  
  59.                 v.setVisibility(View.GONE);  
  60.             }  
  61.         });  
  62.   
  63.         // Initialize array adapters. One for already paired devices and  
  64.         // one for newly discovered devices  
  65.         mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);  
  66.         mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);  
  67.   
  68.         // Find and set up the ListView for paired devices  
  69.         ListView pairedListView = (ListView) findViewById(R.id.paired_devices);  
  70.         pairedListView.setAdapter(mPairedDevicesArrayAdapter);  
  71.         pairedListView.setOnItemClickListener(mDeviceClickListener);  
  72.   
  73.         // Find and set up the ListView for newly discovered devices  
  74.         ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);  
  75.         newDevicesListView.setAdapter(mNewDevicesArrayAdapter);  
  76.         newDevicesListView.setOnItemClickListener(mDeviceClickListener);  
  77.   
  78.         // Register for broadcasts when a device is discovered  
  79.         IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);  
  80.         this.registerReceiver(mReceiver, filter);  
  81.   
  82.         // Register for broadcasts when discovery has finished  
  83.         filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
  84.         this.registerReceiver(mReceiver, filter);  
  85.   
  86.         // Get the local Bluetooth adapter  
  87.         mBtAdapter = BluetoothAdapter.getDefaultAdapter();  
  88.   
  89.         // Get a set of currently paired devices  
  90.         Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();  
  91.   
  92.         // If there are paired devices, add each one to the ArrayAdapter  
  93.         if (pairedDevices.size() > 0) {  
  94.             findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);  
  95.             for (BluetoothDevice device : pairedDevices) {  
  96.                 mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());  
  97.             }  
  98.         } else {  
  99.             String noDevices = getResources().getText(R.string.none_paired).toString();  
  100.             mPairedDevicesArrayAdapter.add(noDevices);  
  101.         }  
  102.     }  
  103.   
  104.     @Override  
  105.     protected void onDestroy() {  
  106.         super.onDestroy();  
  107.   
  108.         // Make sure we're not doing discovery anymore  
  109.         if (mBtAdapter != null) {  
  110.             mBtAdapter.cancelDiscovery();  
  111.         }  
  112.   
  113.         // Unregister broadcast listeners  
  114.         this.unregisterReceiver(mReceiver);  
  115.     }  
  116.   
  117.     /** 
  118.      * Start device discover with the BluetoothAdapter 
  119.      */  
  120.     private void doDiscovery() {  
  121.         if (D) Log.d(TAG, "doDiscovery()");  
  122.   
  123.         // Indicate scanning in the title  
  124.         setProgressBarIndeterminateVisibility(true);  
  125.         setTitle(R.string.scanning);  
  126.   
  127.         // Turn on sub-title for new devices  
  128.         findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);  
  129.   
  130.         // If we're already discovering, stop it  
  131.         if (mBtAdapter.isDiscovering()) {  
  132.             mBtAdapter.cancelDiscovery();  
  133.         }  
  134.   
  135.         // Request discover from BluetoothAdapter  
  136.         mBtAdapter.startDiscovery();  
  137.     }  
  138.   
  139.     // The on-click listener for all devices in the ListViews  
  140.     private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {  
  141.         public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {  
  142.             // Cancel discovery because it's costly and we're about to connect  
  143.             mBtAdapter.cancelDiscovery();  
  144.   
  145.             // Get the device MAC address, which is the last 17 chars in the View  
  146.             String info = ((TextView) v).getText().toString();  
  147.             String address = info.substring(info.length() - 17);  
  148.   
  149.             // Create the result Intent and include the MAC address  
  150.             Intent intent = new Intent();  
  151.             intent.putExtra(EXTRA_DEVICE_ADDRESS, address);  
  152.   
  153.             // Set result and finish this Activity  
  154.             setResult(Activity.RESULT_OK, intent);  
  155.             finish();  
  156.         }  
  157.     };  
  158.   
  159.     // The BroadcastReceiver that listens for discovered devices and  
  160.     // changes the title when discovery is finished  
  161.     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {  
  162.         @Override  
  163.         public void onReceive(Context context, Intent intent) {  
  164.             String action = intent.getAction();  
  165.   
  166.             // When discovery finds a device  
  167.             if (BluetoothDevice.ACTION_FOUND.equals(action)) {  
  168.                 // Get the BluetoothDevice object from the Intent  
  169.                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  
  170.                 // If it's already paired, skip it, because it's been listed already  
  171.                 if (device.getBondState() != BluetoothDevice.BOND_BONDED) {  
  172.                     mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());  
  173.                 }  
  174.             // When discovery is finished, change the Activity title  
  175.             } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {  
  176.                 setProgressBarIndeterminateVisibility(false);  
  177.                 setTitle(R.string.select_device);  
  178.                 if (mNewDevicesArrayAdapter.getCount() == 0) {  
  179.                     String noDevices = getResources().getText(R.string.none_found).toString();  
  180.                     mNewDevicesArrayAdapter.add(noDevices);  
  181.                 }  
  182.             }  
  183.         }  
  184.     };  
  185.   
  186. }  
上一篇:基于IP SAN的ISCSI的存储系统


下一篇:mpeg文件格式分析