首先声明,这是从官方给出的demo修改而来的。

第一:官方demo的注解

1.三个用到的Thread的意义:
(1)AcceptThread
服务器端,起监听作用。(accept函数)
(2)ConnectThread
This thread runs while attempting to make an outgoing connection with a device.(正在试图连接)
(3)ConnectedThread
This thread runs during a connection with a remote device. It handles all incoming and outgoing transmissions.(已经连接,准备进行数据交换)

2.流程
BluetoothChat加载的同时启动BluetoothChatService。
(1)点击右上角的“瞄准镜”图标,调用ensureDiscoverable,使自己可被周围的蓝牙检测到
(2)点击右上角的“搜索”图标,进入DeviceListActivity。它其实是一个对话框,显示了已经配对的蓝牙和一个button。点击button调用doDiscovery搜索周围的蓝牙设备,同时使自己不可见。
点击蓝牙列表中的任意一项,响应OnItemClickListener,将需要的信息通过intent回传给BluetoothChat。同时,DeviceListActivity注册一个BroadcastReceiver检测是否四周有新打开的BluetoothDevice
(3)BluetoothChat响应onActivityResult,调用connectDevice。获取传过来的intent数据,马上再调用BluetoothChatService的connect方法。
在这个方法里面取消正在试图连接或者已经连接的线程。然后重启一个试图连接的线程(ConnectThread),并设置状态位,在BluetoothChat的mHandler处获得响应
4.ConnectThread里面mmSocket.connect();表示已连接。如果失败,调用connectionFailed然后return。如果成功,调用connected(mmSocket, mmDevice, mSocketType)。
connectionFailed函数把“失败”的消息传给handler交给BluetoothChat来显示,然后调用BluetoothChatService.this.start()取消正在试图连接或者已经连接的线程,然后重启一个AcceptThread线程继续监听(accept)。
connected(mmSocket, mmDevice, mSocketType)取消所有线程,开启ConnectedThread进行数据传输,同时通过handler向BluetoothChat发送“连接成功”消息。
5.ConnectedThread里面就是io和数据传输。传输过程中可能会有蓝牙掉线。调用connectionLost,重启BluetoothChatService

第二:新增功能

(1)通过点击edittext之外的部分使软键盘隐藏
setupUI(findViewById(R.id.root));//定义main.xml里面最大的LinearLayout的id=root
函数功能:遍历所有控件,包括子控件,只要点击的view不是Edittext就隐藏软键盘

(2)表情传输

initFaceView();


利用反射机制实现“根据文件名加载图片“
再将图片信息以二进制数据的形式传给另一端,另一端得到数据后用正则表达式来判断消息内是否有表情

SpannableString spannableString = ExpressionUtil.getExpressionString(context, str, zhengze);

(3)语音传输
数据传输部分,官方给的方法对于语音不适合。

byte[] buffer = new byte[1024];
bytes = mmInStream.read(buffer);

官方给的1024缓存对于字符而言够了,但是对于语音不够。
蓝牙传输速度有限,一个几秒的语音就要分好几次,所以要全部读完再来判断
所以要修改ConnectedThread中的run方法。同时记住,获取完所有数据以后不能结束run方法。因为还有数据的传输

public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes = 0;
            String source = "";

            // 获得所有数据
            // 官方给的方法对于语音不适合。蓝牙传输速度有限,一个几秒的语音就要分好几次
            // 所以要全部读完再来判断
            while (true) {
                try {
                    bytes = mmInStream.read(buffer);
                    String temp = new String(buffer, 0, bytes);
                    source += temp;
                    Log.i("bytes", bytes + "");
                    // 这个989是通过后台log查看到的,具体为啥是这个值我不知道
                    if (bytes < 989) {
                        // voice
                        if (source.contains("Recorder")) {
                            // 构造Recorder
                            int end = source.lastIndexOf("]");
                            int start = source.indexOf("[") + 1;
                            String[] sub = source.substring(start, end).split(
                                    ", ");
                            int tempInteger = sub[0].indexOf("=") + 1;
                            String content = sub[0].substring(tempInteger,
                                    sub[0].length());
                            float audioLength = Float.parseFloat(sub[1]
                                    .split("=")[1]);
                            String filePath = sub[2].split("=")[1];

                            Recorder recorder = new Recorder(content,
                                    audioLength, filePath,
                                    BluetoothChat.VOICE_READ);
                            mHandler.obtainMessage(BluetoothChat.VOICE_READ,
                                    bytes, -1, recorder).sendToTarget();
                        } else {
                            // message
                            mHandler.obtainMessage(BluetoothChat.MESSAGE_READ,
                                    bytes, -1, buffer).sendToTarget();
                        }
                        source = "";
                    }
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    // Start the service over to restart listening mode
                    BluetoothChatService.this.start();
                    return;
                }
            }
        }

注意,上面的代码有bug。语音传输以后声音不对,因为没有对语音进行编码解码。只是用了二进制数据。string类型通过二进制编码解码还可以还原,语音不行
附上源码:

最后再加个Thread用于传输voice也不行

private class TransformThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private String filePath;

        public TransformThread(BluetoothSocket socket, String filePath) {
            mmSocket = socket;
            this.filePath = filePath;
            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;
        }

        @Override
        public void run() {
            try {
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                int len = -1;
                byte[] buff = new byte[1024];
                while ((len = mmInStream.read(buff)) != -1) {
                    outputStream.write(buff, 0, len);
                }
                File file = new File(Environment.getExternalStorageDirectory()
                        + filePath);
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(outputStream.toByteArray());
                fos.close();
                mmSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void write(byte[] buffer) {
            try {
                mmOutStream.write(buffer);
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }
    }
}

后台查看是乱码,我猜的原因可能是
ConnectedThread和TransformThread都有如下代码

socket.getInputStream()

获得的是同一个stream对象所以传输的时候搞混了。但是又不能用FileInputStream,毕竟这是一个蓝牙应用。所有的努力宣告破产