Android蓝牙socket实现视频实时传输,以及图片和文本传输

Android蓝牙socket实现视频实时传输,以及图片和文本传输

目标两台手机设备之间能够正常进行蓝牙配对(蓝牙模块儿和硬件挂钩,所以需要两台真机)socket实现蓝牙文本传输实现图片传输实现实时视频传输

代码下载:https://download.csdn.net/download/m0_37781149/10434336

蓝牙传输注意事项Bluetooth的MTU(最大传输单元)是20字节,即一次最多能发送20个字节,若超过20个字节,建议采用分包传输的方式。在传输图片和视频的时候处理方式是将图片转换成字节byte,并在图片前面添加一个头,加入相应的标志位,例如图片大小。然后在接受端进行整合,获取图片大小(字节长度),进行相应判断,整合成图片再显示。下面分模块儿讲解:蓝牙扫描 通过注册广播来获取蓝牙列表,蓝牙连接状态发生改变时候系统都会发送相应广播,获取相应的蓝牙列表并添加到list中,显示出来 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); //当发现蓝牙设备 if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (device.getBondState() != BluetoothDevice.BOND_BONDED) { String strNoFound = getIntent().getStringExtra("no_devices_found"); if (strNoFound == null) strNoFound = "No devices found"; if (mPairedDevicesArrayAdapter.getItem(0).equals(strNoFound)) { mPairedDevicesArrayAdapter.remove(strNoFound); } mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); String strSelectDevice = getIntent().getStringExtra("select_device"); if (strSelectDevice == null) strSelectDevice = "Select a device to connect"; setTitle(strSelectDevice); } } }; //扫描蓝牙设备 private void doDiscovery() { mPairedDevicesArrayAdapter.clear(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } } else { String strNoFound = getIntent().getStringExtra("no_devices_found"); if (strNoFound == null) strNoFound = "No devices found"; mPairedDevicesArrayAdapter.add(strNoFound); } String strScanning = getIntent().getStringExtra("scanning"); if (strScanning == null) strScanning = "Scanning for devices..."; setProgressBarIndeterminateVisibility(true); setTitle(strScanning); if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } mBtAdapter.startDiscovery(); }点击对应的条目可以回退到MainActivity,并将对应的mac地址携带回来,用于蓝牙配对连接 //携带蓝牙地址返回主界面 private AdapterView.OnItemClickListener mDeviceClickListener = new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView av, View v, int arg2, long arg3) { if (mBtAdapter.isDiscovering()) mBtAdapter.cancelDiscovery(); String strNoFound = getIntent().getStringExtra("no_devices_found"); if (strNoFound == null) strNoFound = "No devices found"; if (!((TextView) v).getText().toString().equals(strNoFound)) { String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); Intent intent = new Intent(); intent.putExtra(BluetoothState.EXTRA_DEVICE_ADDRESS, address); setResult(Activity.RESULT_OK, intent); finish(); } } };蓝牙连接核心工具类BluetoothUtil,蓝牙状态BluetoothState和BluetoothService蓝牙配对基本过程:在APP启动的时候,开启一个线程一直监听蓝牙的连接状态,这个子线程是启动时是死循环状态,在蓝牙连接成功时会停止,在蓝牙意外断开时候会重新处于监听状态 public void onStart() { super.onStart(); if (!mBt.isBluetoothEnabled()) { //打开蓝牙 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent, BluetoothState.REQUEST_ENABLE_BT); } else { if (!mBt.isServiceAvailable()) { //开启监听 mBt.setupService(); mBt.startService(BluetoothState.DEVICE_ANDROID); } } } public void setupService() { mChatService = new BluetoothService(mContext, mHandler); } public BluetoothService(Context context, Handler handler) { mAdapter = BluetoothAdapter.getDefaultAdapter(); mState = BluetoothState.STATE_NONE; mHandler = handler; } //开启蓝牙一直监听是否连接的状态 public void startService(boolean isAndroid) { if (mChatService != null) { if (mChatService.getState() == BluetoothState.STATE_NONE) { isServiceRunning = true; mChatService.start(isAndroid); BluetoothUtil.this.isAndroid = isAndroid; } } } //开启子线程 public synchronized void start(boolean isAndroid) { // Cancel any thread attempting to make a connection if (mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; } if (mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; } setState(BluetoothState.STATE_LISTEN); //开启子线程 if (mSecureAcceptThread == null) { mSecureAcceptThread = new AcceptThread(isAndroid); mSecureAcceptThread.start(); BluetoothService.this.isAndroid = isAndroid; } } //监听蓝牙连接的线程 private class AcceptThread extends Thread { private BluetoothServerSocket mmServerSocket; private String mSocketType; boolean isRunning = true; public AcceptThread(boolean isAndroid) { BluetoothServerSocket tmp = null; try { if (isAndroid) //获取蓝牙socket tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_ANDROID_DEVICE); else tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_OTHER_DEVICE); } catch (IOException e) { } mmServerSocket = tmp; } public void run() { setName("AcceptThread" + mSocketType); BluetoothSocket socket = null; //死循环监听蓝牙连接状态,首次进入一定满足条件,蓝牙连上后,循环停止 while (mState != BluetoothState.STATE_CONNECTED && isRunning) { try { socket = mmServerSocket.accept(); } catch (IOException e) { break; } if (socket != null) { synchronized (BluetoothService.this) { switch (mState) { case BluetoothState.STATE_LISTEN: case BluetoothState.STATE_CONNECTING: connected(socket, socket.getRemoteDevice(), mSocketType); break; case BluetoothState.STATE_NONE: case BluetoothState.STATE_CONNECTED: try { socket.close(); } catch (IOException e) { } break; } } } } } public void cancel() { try { mmServerSocket.close(); mmServerSocket = null; } catch (IOException e) { } } public void kill() { isRunning = false; } }核心部分:蓝牙发送和接受数据,蓝牙发送数据处理过程,比如发送一张图片,肯定是大于20字节的,蓝牙是采用分包发送机制,在处理的时候,我们把图片转换成字节,并在图片前面添加一个头,这个头是固定字节的长度,不能太小,因为拍摄图片的时候,图片有大有小,避免和图面装换成字节后产生冲突。这个头里面主要携带两个信息,当然也可以是多个,自由定义。两个信息分别是,图片的大小,即字节长度length,还有一个是动作,例如传照片,传文本,实时视频传输,代码如下: //添加头发送数据 public void send(byte[] data, String str) { int length = data.length; byte[] length_b = null; try { length_b = intToByteArray(length); } catch (Exception e) { e.printStackTrace(); } if (length_b == null) return; //获得一个字节长度为14的byte数组 headInfoLength为14 byte[] headerInfo = new byte[headInfoLength]; //前六位添加012345的标志位 for (int i = 0; i < headInfoLength - 8; i++) { headerInfo[i] = (byte) i; } //7到10位添加图片大小的字节长度 for (int i = 0; i < 4; i++) { headerInfo[6 + i] = length_b[i]; } //11到14位添加动作信息 if (str.equals("text")) { for (int i = 0; i < 4; i++) { headerInfo[10 + i] = (byte) 0; } } else if (str.equals("photo")) { for (int i = 0; i < 4; i++) { headerInfo[10 + i] = (byte) 1; } } else if (str.equals("video")) { for (int i = 0; i < 4; i++) { headerInfo[10 + i] = (byte) 2; } } //将对应信息添加到图片前面 byte[] sendMsg = new byte[length + headInfoLength]; for (int i = 0; i < sendMsg.length; i++) { if (i < headInfoLength) { sendMsg[i] = headerInfo[i]; } else { sendMsg[i] = data[i - headInfoLength]; } } mChatService.write(sendMsg); } //蓝牙socket发送数据 public void write(byte[] out) { ConnectedThread r; synchronized (this) { if (mState != BluetoothState.STATE_CONNECTED) return; r = mConnectedThread; } r.write(out); } public void write(byte[] buffer) { try { mmOutStream.write(buffer); } catch (IOException e) { } }蓝牙接收数据:接收到的数据都是字节数据,我们需要把数据进行整合成对应的图片或视频信息,因为发送时分包机制,所以整合的时候要确保整张图片发送完毕才开始整合,具体流程是先获取前六位标志位,然后获取第7到10位的图片大小,再获取第11位到14位的动作信息,具体代码如下: public void run() { byte[] buffer; ArrayList arr_byte = new ArrayList(); while (true) { try { boolean valid = true; //判断前六位是不是012345 for (int i = 0; i < 6; i++) { int t = mmInStream.read(); if (t != i) { valid = false; //前六位判断完了跳出循环 break; } } if (valid) { //获取图片大小 byte[] bufLength = new byte[4]; for (int i = 0; i < 4; i++) { bufLength[i] = ((Integer) mmInStream.read()).byteValue(); } int TextCount = 0; int PhotoCount = 0; int VideoCount = 0; //获取动作信息 for (int i = 0; i < 4; i++) { int read = mmInStream.read(); if (read == 0) { TextCount++; } else if (read == 1) { PhotoCount++; } else if (read == 2) { VideoCount++; } } //获取图片的字节 int length = ByteArrayToInt(bufLength); buffer = new byte[length]; for (int i = 0; i < length; i++) { buffer[i] = ((Integer) mmInStream.read()).byteValue(); } //通过handler发出去 Message msg = Message.obtain(); msg.what = BluetoothState.MESSAGE_READ; msg.obj = buffer; if (TextCount == 4) { msg.arg1 = 0; mHandler.sendMessage(msg); } else if (PhotoCount == 4) { msg.arg1 = 1; mHandler.sendMessage(msg); } else if (VideoCount == 4) { msg.arg1 = 2; mHandler.sendMessage(msg); } } } catch (IOException e) { connectionLost(); BluetoothService.this.start(BluetoothService.this.isAndroid); break; } catch (Exception e) { e.printStackTrace(); } } }

下面分别是发送端和接受端的完整代码:发送端SendClient代码部分:layout文件:

相关推荐

揭秘英雄杀经典日赛积分算法:如何轻松解锁竞技巅峰?
部落冲突弓箭女皇升级表-弓箭女王升级数据解析
365BET体育投注官网

部落冲突弓箭女皇升级表-弓箭女王升级数据解析

📅 08-07 👁️ 872
FiguartsZERO
hse365平台

FiguartsZERO

📅 06-30 👁️ 6496
如何在 Photoshop 中放大图像 ▷➡️
365BET体育投注官网

如何在 Photoshop 中放大图像 ▷➡️

📅 07-25 👁️ 5280