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

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

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


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

首先看聊天室的效果图:




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



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



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

BluetoothChat.java

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

import android.app.Activity;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.content.Intent;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.KeyEvent;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.View;import android.view.Window;import android.view.View.OnClickListener;import android.view.inputmethod.EditorInfo;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.EditText;import android.widget.ListView;import android.widget.TextView;import android.widget.Toast;/** * This is the main Activity that displays the current chat session. */public class BluetoothChat extends Activity {    // Debugging    private static final String TAG = "BluetoothChat";    private static final boolean D = true;    // Message types sent from the BluetoothChatService Handler    public static final int MESSAGE_STATE_CHANGE = 1;    public static final int MESSAGE_READ = 2;    public static final int MESSAGE_WRITE = 3;    public static final int MESSAGE_DEVICE_NAME = 4;    public static final int MESSAGE_TOAST = 5;    // Key names received from the BluetoothChatService Handler    public static final String DEVICE_NAME = "device_name";    public static final String TOAST = "toast";    // Intent request codes    private static final int REQUEST_CONNECT_DEVICE = 1;    private static final int REQUEST_ENABLE_BT = 2;    // Layout Views    private TextView mTitle;    private ListView mConversationView;    private EditText mOutEditText;    private Button mSendButton;    // Name of the connected device    private String mConnectedDeviceName = null;    // Array adapter for the conversation thread    private ArrayAdapter mConversationArrayAdapter;    // String buffer for outgoing messages    private StringBuffer mOutStringBuffer;    // Local Bluetooth adapter    private BluetoothAdapter mBluetoothAdapter = null;    // Member object for the chat services    private BluetoothChatService mChatService = null;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if(D) Log.e(TAG, "+++ ON CREATE +++");        // Set up the window layout        requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);        setContentView(R.layout.main);        getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);        // Set up the custom title        mTitle = (TextView) findViewById(R.id.title_left_text);        mTitle.setText(R.string.app_name);        mTitle = (TextView) findViewById(R.id.title_right_text);        // 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;        }    }    @Override    public void onStart() {        super.onStart();        if(D) Log.e(TAG, "++ ON START ++");        // 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();        }    }    @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();            }        }    }    private void setupChat() {        Log.d(TAG, "setupChat()");        // Initialize the array adapter for the conversation thread        mConversationArrayAdapter = new ArrayAdapter(this, R.layout.message);        mConversationView = (ListView) findViewById(R.id.in);        mConversationView.setAdapter(mConversationArrayAdapter);        // Initialize the compose field with a listener for the return key        mOutEditText = (EditText) findViewById(R.id.edit_text_out);        mOutEditText.setOnEditorActionListener(mWriteListener);        // 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 view = (TextView) findViewById(R.id.edit_text_out);                String message = view.getText().toString();                sendMessage(message);            }        });        // Initialize the BluetoothChatService to perform bluetooth connections        mChatService = new BluetoothChatService(this, mHandler);        // Initialize the buffer for outgoing messages        mOutStringBuffer = new StringBuffer("");    }    @Override    public synchronized void onPause() {        super.onPause();        if(D) Log.e(TAG, "- ON PAUSE -");    }    @Override    public void onStop() {        super.onStop();        if(D) Log.e(TAG, "-- ON STOP --");    }    @Override    public void onDestroy() {        super.onDestroy();        // Stop the Bluetooth chat services        if (mChatService != null) mChatService.stop();        if(D) Log.e(TAG, "--- ON DESTROY ---");    }    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);        }    }    /**     * Sends a message.     * @param message  A string of text to send.     */    private void sendMessage(String message) {        // Check that we're actually connected before trying anything        if (mChatService.getState() != BluetoothChatService.STATE_CONNECTED) {            Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();            return;        }        // Check that there's actually something to send        if (message.length() > 0) {            // Get the message bytes and tell the BluetoothChatService to write            byte[] send = message.getBytes();            mChatService.write(send);            // Reset out string buffer to zero and clear the edit text field            mOutStringBuffer.setLength(0);            mOutEditText.setText(mOutStringBuffer);        }    }    // The action listener for the EditText widget, to listen for the return key    private TextView.OnEditorActionListener mWriteListener =        new TextView.OnEditorActionListener() {        public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {            // If the action is a key-up event on the return key, send the message            if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {                String message = view.getText().toString();                sendMessage(message);            }            if(D) Log.i(TAG, "END onEditorAction");            return true;        }    };    // The Handler that gets information back from the BluetoothChatService    private final Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {            case MESSAGE_STATE_CHANGE:                if(D) Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);                switch (msg.arg1) {                case BluetoothChatService.STATE_CONNECTED:                    mTitle.setText(R.string.title_connected_to);                    mTitle.append(mConnectedDeviceName);                    mConversationArrayAdapter.clear();                    break;                case BluetoothChatService.STATE_CONNECTING:                    mTitle.setText(R.string.title_connecting);                    break;                case BluetoothChatService.STATE_LISTEN:                case BluetoothChatService.STATE_NONE:                    mTitle.setText(R.string.title_not_connected);                    break;                }                break;            case MESSAGE_WRITE:                byte[] writeBuf = (byte[]) msg.obj;                // construct a string from the buffer                String writeMessage = new String(writeBuf);                mConversationArrayAdapter.add("Me:  " + writeMessage);                break;            case MESSAGE_READ:                byte[] readBuf = (byte[]) msg.obj;                // construct a string from the valid bytes in the buffer                String readMessage = new String(readBuf, 0, msg.arg1);                mConversationArrayAdapter.add(mConnectedDeviceName+":  " + readMessage);                break;            case MESSAGE_DEVICE_NAME:                // save the connected device's name                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);                Toast.makeText(getApplicationContext(), "Connected to "                               + mConnectedDeviceName, Toast.LENGTH_SHORT).show();                break;            case MESSAGE_TOAST:                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST),                               Toast.LENGTH_SHORT).show();                break;            }        }    };    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();            }        }    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        MenuInflater inflater = getMenuInflater();        inflater.inflate(R.menu.option_menu, menu);        return true;    }    @Override    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;    }}

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 。

import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.util.UUID;import android.annotation.SuppressLint;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.bluetooth.BluetoothServerSocket;import android.bluetooth.BluetoothSocket;import android.content.Context;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;/** * This class does all the work for setting up and managing Bluetooth * connections with other devices. It has a thread that listens for * incoming connections, a thread for connecting with a device, and a * thread for performing data transmissions when connected. */@SuppressLint("NewApi")public class BluetoothChatService {    // Debugging    private static final String TAG = "BluetoothChatService";    private static final boolean D = true;    // Name for the SDP record when creating server socket    private static final String NAME = "BluetoothChat";    // Unique UUID for this application    private static final UUID MY_UUID = UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");    // Member fields    private final BluetoothAdapter mAdapter;    private final Handler mHandler;    private AcceptThread mAcceptThread;    private ConnectThread mConnectThread;    private ConnectedThread mConnectedThread;    private int mState;    // Constants that indicate the current connection state    public static final int STATE_NONE = 0;       // we're doing nothing    public static final int STATE_LISTEN = 1;     // now listening for incoming connections    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection    public static final int STATE_CONNECTED = 3;  // now connected to a remote device    /**     * Constructor. Prepares a new BluetoothChat session.     * @param context  The UI Activity Context     * @param handler  A Handler to send messages back to the UI Activity     */    public BluetoothChatService(Context context, Handler handler) {        mAdapter = BluetoothAdapter.getDefaultAdapter();        mState = STATE_NONE;        mHandler = handler;    }    /**     * Set the current state of the chat connection     * @param state  An integer defining the current connection state     */    private synchronized void setState(int state) {        if (D) Log.d(TAG, "setState() " + mState + " -> " + state);        mState = state;        // Give the new state to the Handler so the UI Activity can update        mHandler.obtainMessage(BluetoothChat.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();    }    /**     * Return the current connection state. */    public synchronized int getState() {        return mState;    }    /**     * Start the chat service. Specifically start AcceptThread to begin a     * session in listening (server) mode. Called by the Activity onResume() */    public synchronized void start() {        if (D) Log.d(TAG, "start");        // Cancel any thread attempting to make a connection        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}        // Cancel any thread currently running a connection        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}        // Start the thread to listen on a BluetoothServerSocket        if (mAcceptThread == null) {            mAcceptThread = new AcceptThread();            mAcceptThread.start();        }        setState(STATE_LISTEN);    }    /**     * Start the ConnectThread to initiate a connection to a remote device.     * @param device  The BluetoothDevice to connect     */    public synchronized void connect(BluetoothDevice device) {        if (D) Log.d(TAG, "connect to: " + device);        // Cancel any thread attempting to make a connection        if (mState == STATE_CONNECTING) {            if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}        }        // Cancel any thread currently running a connection        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}        // Start the thread to connect with the given device        mConnectThread = new ConnectThread(device);        mConnectThread.start();        setState(STATE_CONNECTING);    }    /**     * Start the ConnectedThread to begin managing a Bluetooth connection     * @param socket  The BluetoothSocket on which the connection was made     * @param device  The BluetoothDevice that has been connected     */    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {        if (D) Log.d(TAG, "connected");        // Cancel the thread that completed the connection        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}        // Cancel any thread currently running a connection        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}        // Cancel the accept thread because we only want to connect to one device        if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}        // Start the thread to manage the connection and perform transmissions        mConnectedThread = new ConnectedThread(socket);        mConnectedThread.start();        // Send the name of the connected device back to the UI Activity        Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);        Bundle bundle = new Bundle();        bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());        msg.setData(bundle);        mHandler.sendMessage(msg);        setState(STATE_CONNECTED);    }    /**     * Stop all threads     */    public synchronized void stop() {        if (D) Log.d(TAG, "stop");        if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}        if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}        if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}        setState(STATE_NONE);    }    /**     * Write to the ConnectedThread in an unsynchronized manner     * @param out The bytes to write     * @see ConnectedThread#write(byte[])     */    public void write(byte[] out) {        // Create temporary object        ConnectedThread r;        // Synchronize a copy of the ConnectedThread        synchronized (this) {            if (mState != STATE_CONNECTED) return;            r = mConnectedThread;        }        // Perform the write unsynchronized        r.write(out);    }    /**     * Indicate that the connection attempt failed and notify the UI Activity.     */    private void connectionFailed() {        setState(STATE_LISTEN);        // Send a failure message back to the Activity        Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);        Bundle bundle = new Bundle();        bundle.putString(BluetoothChat.TOAST, "Unable to connect device");        msg.setData(bundle);        mHandler.sendMessage(msg);    }    /**     * Indicate that the connection was lost and notify the UI Activity.     */    private void connectionLost() {        setState(STATE_LISTEN);        // Send a failure message back to the Activity        Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_TOAST);        Bundle bundle = new Bundle();        bundle.putString(BluetoothChat.TOAST, "Device connection was lost");        msg.setData(bundle);        mHandler.sendMessage(msg);    }    /**     * 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).     */    @SuppressLint("NewApi")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);            }        }    }    /**     * 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);            }        }    }    /**     * 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);            }        }    }}

DeviceListActivity.java

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

import java.util.Set;import android.annotation.SuppressLint;import android.app.Activity;import android.bluetooth.BluetoothAdapter;import android.bluetooth.BluetoothDevice;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.Window;import android.view.View.OnClickListener;import android.widget.AdapterView;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget.ListView;import android.widget.TextView;import android.widget.AdapterView.OnItemClickListener;/** * This Activity appears as a dialog. It lists any paired devices and * devices detected in the area after discovery. When a device is chosen * by the user, the MAC address of the device is sent back to the parent * Activity in the result Intent. */@SuppressLint("NewApi")public class DeviceListActivity extends Activity {    // Debugging    private static final String TAG = "DeviceListActivity";    private static final boolean D = true;    // Return Intent extra    public static String EXTRA_DEVICE_ADDRESS = "device_address";    // Member fields    private BluetoothAdapter mBtAdapter;    private ArrayAdapter mPairedDevicesArrayAdapter;    private ArrayAdapter mNewDevicesArrayAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // Setup the window        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);        setContentView(R.layout.device_list);        // Set result CANCELED incase the user backs out        setResult(Activity.RESULT_CANCELED);        // 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);            }        });        // Initialize array adapters. One for already paired devices and        // one for newly discovered devices        mPairedDevicesArrayAdapter = new ArrayAdapter(this, R.layout.device_name);        mNewDevicesArrayAdapter = new ArrayAdapter(this, R.layout.device_name);        // 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);        // 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();        // Get a set of currently paired devices        Set pairedDevices = mBtAdapter.getBondedDevices();        // If there are paired devices, add each one to the ArrayAdapter        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);        }    }    @Override    protected void onDestroy() {        super.onDestroy();        // Make sure we're not doing discovery anymore        if (mBtAdapter != null) {            mBtAdapter.cancelDiscovery();        }        // Unregister broadcast listeners        this.unregisterReceiver(mReceiver);    }    /**     * 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();    }    // The on-click listener for all devices in the ListViews    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();        }    };    // 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);                }            }        }    };}


更多相关文章

  1. Activity有几点你可能不知道的
  2. Android中有关Handler的使用(一)
  3. Android(安卓)Service 详解二:创建一个service
  4. Android(安卓)Accessibility(辅助功能) 学习
  5. Android(安卓)NDK调用Java方法
  6. [置顶] Android中inflate方法的用法
  7. Android中音视频合成的几种方案详析
  8. Android中SharedPreferences的使用详解
  9. WindowManager与窗口管理机制

随机推荐

  1. 【Android】日期拾取器、时间拾取器与菜
  2. 一个Android小白的学习经验
  3. [置顶] android 从资源中获取数组
  4. 第3.2.2节 抽象布局与抽象样式
  5. Android(安卓)断点续传,手写多线程下载文
  6. 应用程序如何获取系统权限
  7. Android获取SD卡总容量,可用大小,机身内存
  8. Unity与Android交互方案优化版
  9. Android(安卓)插件化
  10. Android绘图机制(二)--2D绘图基础