首先声明,这是从官方给出的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,毕竟这是一个蓝牙应用。所有的努力宣告破产