Multi connection via bluetooth in android

不问归期 提交于 2019-12-01 01:13:13
package com.switching.bluetooth;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.UUID;

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;

import com.switching.ServerMainActivity;

/**
 * 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.
 */
public class BluetoothService {
    // Debugging
    private static final String TAG = "BluetoothService iBT";
    private static final boolean D = true;

    // Name for the SDP record when creating server socket
    private static final String NAME = "i BT";

    // Unique UUID for this application
    private static UUID MY_UUID;

    // Member fields
    private final BluetoothAdapter mAdapter;
    private final Handler mHandler;
    private AcceptThread mAcceptThread;
    private ConnectThread mConnectThread;
    private ConnectedThread mConnectedThread;
    private int mState;

    private ArrayList<String> mDeviceAddresses;
    private ArrayList<String> mDeviceNames;
    private ArrayList<ConnectedThread> mConnThreads;
    private ArrayList<BluetoothSocket> mSockets;

    /**
     * A bluetooth piconet can support up to 7 connections. This array holds 7
     * unique UUIDs. When attempting to make a connection, the UUID on the
     * client must match one that the server is listening for. When accepting
     * incoming connections server listens for all 7 UUIDs. When trying to form
     * an outgoing connection, the client tries each UUID one at a time.
     */
    private ArrayList<UUID> mUuids;

    // 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 BluetoothService(Context context, Handler handler) {
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mState = STATE_NONE;
        mHandler = handler;

        initializeArrayLists();

        // 7 randomly-generated UUIDs. These must match on both server and
        // client.
        // mUuids.add(UUID.fromString("b7746a40-c758-4868-aa19-7ac6b3475dfc"));
        // mUuids.add(UUID.fromString("2d64189d-5a2c-4511-a074-77f199fd0834"));
        // mUuids.add(UUID.fromString("e442e09a-51f3-4a7b-91cb-f638491d1412"));
        // mUuids.add(UUID.fromString("a81d6504-4536-49ee-a475-7d96d09439e4"));
        // mUuids.add(UUID.fromString("aa91eab1-d8ad-448e-abdb-95ebba4a9b55"));
        // mUuids.add(UUID.fromString("4d34da73-d0a4-4f40-ac38-917e0a9dee97"));
        // mUuids.add(UUID.fromString("5e14d4df-9c8a-4db7-81e4-c937564c86e0"));
    }

    public static UUID getMY_UUID() {
        return MY_UUID;
    }

    public static void setMY_UUID(UUID mY_UUID) {
        MY_UUID = mY_UUID;
    }

    public boolean isDeviceConnectedAtPosition(int position) {
        if (mConnThreads.get(position) == null) {
            return false;
        }
        return true;
    }

    private void initializeArrayLists() {
        mDeviceAddresses = new ArrayList<String>(5);
        mDeviceNames = new ArrayList<String>(5);
        mConnThreads = new ArrayList<ConnectedThread>(5);
        mSockets = new ArrayList<BluetoothSocket>(5);
        mUuids = new ArrayList<UUID>(5);

        for (int i = 0; i < 5; i++) {
            mDeviceAddresses.add(null);
            mDeviceNames.add(null);
            mConnThreads.add(null);
            mSockets.add(null);
            mUuids.add(null);
        }

        Log.i(TAG, "mConnThreads.size() in Service Constructor--"
                + mConnThreads.size());
    }

    public ArrayList<String> getmDeviceNames() {
        return this.mDeviceNames;
    }

    public void setmDeviceNames(ArrayList<String> mDeviceNames) {
        this.mDeviceNames = mDeviceNames;
    }

    public ArrayList<String> getmDeviceAddresses() {
        return mDeviceAddresses;
    }

    public void setmDeviceAddresses(ArrayList<String> mDeviceAddresses) {
        this.mDeviceAddresses = mDeviceAddresses;
    }

    /**
     * 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(ServerMainActivity.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,
            int selectedPosition) {

        if (getPositionIndexOfDevice(device) == -1) {
            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 (mConnThreads.get(selectedPosition) != null) {
                mConnThreads.get(selectedPosition).cancel();
                // mConnectedThread = null;
                mConnThreads.set(selectedPosition, null);
            }

            // Create a new thread and attempt to connect to each UUID
            // one-by-one.
            try {

                // String
                // s="00001101-0000-1000-8000"+device.getAddress().split(":");
                ConnectThread mConnectThread = new ConnectThread(device,
                        UUID.fromString("00001101-0000-1000-8000-"
                                + device.getAddress().replace(":", "")),
                        selectedPosition);
                Log.i(TAG, "uuid-string at server side"
                        + ("00001101-0000-1000-8000" + device.getAddress()
                                .replace(":", "")));
                mConnectThread.start();
                setState(STATE_CONNECTING);
            } catch (Exception e) {
            }
        } else {
            Message msg = mHandler
                    .obtainMessage(ServerMainActivity.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString(ServerMainActivity.TOAST,
                    "This device " + device.getName() + " Already Connected");
            msg.setData(bundle);
            mHandler.sendMessage(msg);
        }
    }

    /**
     * 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, int selectedPosition) {
        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
        ConnectedThread mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start();
        // Add each connected thread to an array
        mConnThreads.set(selectedPosition, mConnectedThread);

        // Send the name of the connected device back to the UI Activity
        Message msg = mHandler
                .obtainMessage(ServerMainActivity.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(ServerMainActivity.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;
        }

        for (int i = 0; i < 5; i++) {
            mDeviceNames.set(i, null);
            mDeviceAddresses.set(i, null);
            mSockets.set(i, null);
            if (mConnThreads.get(i) != null) {
                mConnThreads.get(i).cancel();
                mConnThreads.set(i, 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) {
        // When writing, try to write out to all connected threads
        for (int i = 0; i < mConnThreads.size(); i++) {
            try {
                // Create temporary object
                ConnectedThread r;
                // Synchronize a copy of the ConnectedThread
                synchronized (this) {
                    if (mState != STATE_CONNECTED)
                        return;
                    r = mConnThreads.get(i);
                }
                // Perform the write unsynchronized
                r.write(out);
            } catch (Exception e) {
            }
        }
    }

    /**
     * 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(ServerMainActivity.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(ServerMainActivity.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(BluetoothDevice device) {
        // setState(STATE_LISTEN);
        int positionIndex = getPositionIndexOfDevice(device);
        if (positionIndex != -1) {
            Log.i(TAG, "getPositionIndexOfDevice(device) ==="
                    + mDeviceAddresses.get(getPositionIndexOfDevice(device)));
            mDeviceAddresses.set(positionIndex, null);
            mDeviceNames.set(positionIndex, null);
            mConnThreads.set(positionIndex, null);

            Message msg = mHandler
                    .obtainMessage(ServerMainActivity.MESSAGE_TOAST);
            Bundle bundle = new Bundle();
            bundle.putString(ServerMainActivity.TOAST,
                    "Device connection was lost from " + device.getName());
            msg.setData(bundle);
            mHandler.sendMessage(msg);
        }
        // Send a failure message back to the Activity
    }

    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 {

                if (mAdapter.isEnabled()) {
                    BluetoothService.setMY_UUID(UUID
                            .fromString("00001101-0000-1000-8000-"
                                    + mAdapter.getAddress().replace(":", "")));
                }
                Log.i(TAG, "MY_UUID.toString()=="
                        + BluetoothService.getMY_UUID().toString());

                tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME,
                        BluetoothService.getMY_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;
            Log.i(TAG, "mState in acceptThread==" + mState);
            // 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 (BluetoothService.this) {
                        switch (mState) {
                        case STATE_LISTEN:
                        case STATE_CONNECTING:
                            // Situation normal. Start the connected thread.
                            connected(socket, socket.getRemoteDevice(),getAvailablePositionIndexForNewConnection(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 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 {
    // BluetoothServerSocket serverSocket = null;
    //
    // public AcceptThread() {
    // }
    //
    // public void run() {
    // if (D)
    // Log.d(TAG, "BEGIN mAcceptThread" + this);
    // setName("AcceptThread");
    // BluetoothSocket socket = null;
    // try {
    // // Listen for all 7 UUIDs
    // serverSocket = mAdapter.listenUsingRfcommWithServiceRecord(
    // NAME, MY_UUID);
    // socket = serverSocket.accept();
    // if (socket != null) {
    // String address = socket.getRemoteDevice().getAddress();
    // mSockets.add(socket);
    // mDeviceAddresses.add(address);
    // mDeviceNames.add(socket.getRemoteDevice().getName());
    // // connected(socket, socket.getRemoteDevice());
    // }
    // } catch (IOException e) {
    // Log.e(TAG, "accept() failed", e);
    // }
    // if (D)
    // Log.i(TAG, "END mAcceptThread");
    // }
    //
    // public void cancel() {
    // if (D)
    // Log.d(TAG, "cancel " + this);
    // try {
    // serverSocket.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;
        private UUID tempUuid;
        private int selectedPosition;

        public ConnectThread(BluetoothDevice device, UUID uuidToTry,
                int selectedPosition) {
            mmDevice = device;
            BluetoothSocket tmp = null;
            tempUuid = uuidToTry;
            this.selectedPosition = selectedPosition;
            // Get a BluetoothSocket for a connection with the
            // given BluetoothDevice
            try {
                tmp = device.createRfcommSocketToServiceRecord(uuidToTry);
            } 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) {
                // if
                // (tempUuid.toString().contentEquals(mUuids.get(6).toString()))
                // {
                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
                BluetoothService.this.start();
                return;
            }

            // Reset the ConnectThread because we're done
            synchronized (BluetoothService.this) {
                mConnectThread = null;
            }
            mDeviceAddresses.set(selectedPosition, mmDevice.getAddress());
            mDeviceNames.set(selectedPosition, mmDevice.getName());
            // Start the connected thread
            connected(mmSocket, mmDevice, selectedPosition);
        }

        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(
                            ServerMainActivity.MESSAGE_READ,
                            bytes,
                            getPositionIndexOfDevice(mmSocket.getRemoteDevice()),
                            buffer).sendToTarget();
                    Log.i("**********read", "read Called........");
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost(mmSocket.getRemoteDevice());
                    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(ServerMainActivity.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);
            }
        }
    }

    public int getPositionIndexOfDevice(BluetoothDevice device) {
        for (int i = 0; i < mDeviceAddresses.size(); i++) {
            if (mDeviceAddresses.get(i) != null
                    && mDeviceAddresses.get(i).equalsIgnoreCase(
                            device.getAddress()))
                return i;
        }
        return -1;
    }

    public int getAvailablePositionIndexForNewConnection(BluetoothDevice device) {      
        if (getPositionIndexOfDevice(device) == -1) {
            for (int i = 0; i < mDeviceAddresses.size(); i++) {
                if (mDeviceAddresses.get(i) == null) {
                    return i;
                }
            }
        }
        return -1;
    }
}

I implemented that with sucess after a relevant amount of work, during the path i faced some tricky bugs, for instance I had to implement a Handshake protocol(something like that) to avoid timeout problem during a connection attempt.

Well, I started looking at the BluetoothChat example(Android SDK Sample), that implements communication between 2 devices. So, I modified that, to permit multiple connections. As the code became large, i will just tell you the approach I have used.

Basically, all devices running my app can be a server or client. So each one, has always a BluetoothServerSocket(AcceptThread) running, so in this way, each device is always able to receive a request connection.

A device that wants to connect, starts a ConnectThread, this thread it is started after a discovery process or if it choose BluetoothDevice of PairedDevices Using getBondedDevices.

When a connection is established, I create a new Thread(ConnectedThread) that represents that connection. If you want to has different behavior that depends of you device role(master or slave) you can have a ConnectedThread subclass like MasterThread and SlaveThread

The Android Documentation has a good explanation of how to work with Bluetooth at: http://developer.android.com/guide/topics/connectivity/bluetooth.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!