在 android 中通过 BLE 传输图像
Transfer Image throug BLE in android
我正在使用以下代码传输 1 MB 的图像。
如果在每个数据包之间实施线程延迟,图像将成功传输。
如果未设置线程延迟,则所有数据包都从 BluetoothGattServer 发送,但 BluetoothGattCallback 不会接收所有数据包。
任何人都可以指导在没有线程延迟的情况下发送数据包
在每个数据包之间实现线程
private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
byte[] CHARACTERS) {
boolean isComplete = false;
runOnUiThread(() -> {
tv_status.setText("Sending Data...!!");
startTime = SystemClock.uptimeMillis();
customHandler.postDelayed(updateTimerThread, 0);
});
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] packetNoByte;
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
final int remainingTime = time;
if (!hasDisconnected) {
this.runOnUiThread(new Runnable() {
@Override
public void run() {
mRelativeLayout.setVisibility(View.VISIBLE);
if (totalPackets != 0) {
showProgress(totalPackets, remainingTime);
}
}
});
} else {
runOnUiThread(() -> {
mProgressBar.setProgress(0);
tv_progress.setText(0 + "%");
tv_timer.setText("00:00:00");
tv_imageSize.setText("");
tv_status.setText("");
Toast.makeText(PeripheralRoleActivity.this, "Something went wrong, Please Try again", Toast.LENGTH_SHORT).show();
customHandler.removeCallbacks(updateTimerThread);
});
return;
}
int a;
int b;
/**
* @param THREAD_SLEEP_TIME_FOR_NOTIFICATION
* this delay is placed to give a small pause while sending the data packe
* */
try {
Thread.sleep(Constants.THREAD_SLEEP_TIME_FOR_NOTIFICATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
sentPacket = sentPacket + 1;
byte[] packetArray = Utils.getUtilsClass().toByteArray(sentPacket);
packetNoByte = Arrays.copyOf(packetArray, packetArray.length);
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
if(b-a ==0){
return;
}
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] last_packet =
new byte[packetNoByte.length + character_length];
System.arraycopy(packetNoByte, 0, last_packet,
0, packetNoByte.length);
System.arraycopy(sending_last_hex, 0, last_packet,
packetNoByte.length, sending_last_hex.length);
Log.d("Sending packets", Arrays.toString(last_packet));
// Set value for characteristic
characteristic.setValue(last_packet);
notifyCharacteristicChanged();
isComplete = true;
customHandler.removeCallbacks(updateTimerThread);
currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
Log.d("Collection", "End Time: " + currentDateTimeString);
Utils.getUtilsClass().sendNotification(getApplicationContext(), "Data Transfer", "Transfer Complete");
} else {
Log.i("", "CONTINUE PACKET ");
a = ((sending_continue_hex.length) * time);
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] sending_continue_packet =
new byte[packetNoByte.length + sending_continue_hex.length];
System.arraycopy(packetNoByte, 0, sending_continue_packet,
0, packetNoByte.length);
System.arraycopy(sending_continue_hex, 0, sending_continue_packet,
packetNoByte.length, sending_continue_hex.length);
Log.d("data transfer a", String.valueOf(a));
Log.d("data transfer b", String.valueOf(b));
Log.d("data trans bytes", String.valueOf(sending_continue_hex.length));
if(output == null){
output = new ByteArrayOutputStream();
}
try {
if {
characteristic.setValue(sending_continue_packet);
Log.d("Sending packets", Arrays.toString(sending_continue_packet));
notifyCharacteristicChanged();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d("Data byte", "times " + time);
if (isComplete) {
characteristic.setValue("Completed");
notifyCharacteristicChanged();
}
runOnUiThread(() -> tv_status.setText("Data sent!!"));
}
}
更新代码
//下面的函数用于将图片byte[]打成包,存入arraylist
private void breakPackets(byte[] CHARACTERS) {
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
packetList = new ArrayList<>();
sendingPacket = 0;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
int a;
int b;
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
//packetList is an ArrayList<byte[]>
packetList.add(sending_last_hex);
startSendingPackets(sendingPacket);
} else {
a = (sending_continue_hex.length) * time;
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
packetList.add(sending_continue_hex);
}
Log.d("Data byte", "times " + time);
}
}
//the following function is used to set the byte[] from the arraylist to the characteristics and then notify the characteristics
private void startSendingPackets(int packet) {
isCommand = false;
mSampleCharacteristic.setValue(packetList.get(packet));
notifyCharacteristicChanged();
Log.i("packeting", "Sending ------------> " + packet);
}
/*************************************************/
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
//check if status is success
if (status == BluetoothGatt.GATT_SUCCESS) {
//if status is not successful isExecutable is false and the else loop is executed to resend the same packet that has failed
if (isExecutable) {
// Log.i("packeting", "Sent ------------> " + sendingPacket);
sendingPacket = sendingPacket + 1;
int size = packetList.size();
if (sendingPacket <= size-1) {
startSendingPackets(sendingPacket);
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
} else {
sendCommand("Completed");
}
} else {
startSendingPackets(sendingPacket);
isExecutable = true;
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
}
}else{
//if status is not successful
isExecutable = false;
Log.d(MainActivity.TAG, "Notification sent. fail Status: " + status );
}
}
的文档中阅读
When multiple notifications are to be sent, an application must wait
for this callback to be received before sending additional
notifications.
这意味着在您调用notifyCharacteristicChanged
之后,您不能再次调用notifyCharacteristicChanged
,直到收到回调onNotificationSent
。因此,您需要删除 for
循环并重构代码以遵循 API 规则。
这样做的原因是为了获得流量控制。如果您只是以比 BLE link 的吞吐量更快的速度推送新数据包,则内部缓冲区会变满并且会发生数据包丢失。这就是延迟似乎有效的原因,但这不是一个可靠的解决方案,因此您应该等待 onNotificationSent
回调,因为这意味着 BLE 堆栈已准备好接受新数据包。
我正在使用以下代码传输 1 MB 的图像。 如果在每个数据包之间实施线程延迟,图像将成功传输。 如果未设置线程延迟,则所有数据包都从 BluetoothGattServer 发送,但 BluetoothGattCallback 不会接收所有数据包。
任何人都可以指导在没有线程延迟的情况下发送数据包
在每个数据包之间实现线程
private void sendingContinuePacket(BluetoothGattCharacteristic characteristic,
byte[] CHARACTERS) {
boolean isComplete = false;
runOnUiThread(() -> {
tv_status.setText("Sending Data...!!");
startTime = SystemClock.uptimeMillis();
customHandler.postDelayed(updateTimerThread, 0);
});
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] packetNoByte;
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
final int remainingTime = time;
if (!hasDisconnected) {
this.runOnUiThread(new Runnable() {
@Override
public void run() {
mRelativeLayout.setVisibility(View.VISIBLE);
if (totalPackets != 0) {
showProgress(totalPackets, remainingTime);
}
}
});
} else {
runOnUiThread(() -> {
mProgressBar.setProgress(0);
tv_progress.setText(0 + "%");
tv_timer.setText("00:00:00");
tv_imageSize.setText("");
tv_status.setText("");
Toast.makeText(PeripheralRoleActivity.this, "Something went wrong, Please Try again", Toast.LENGTH_SHORT).show();
customHandler.removeCallbacks(updateTimerThread);
});
return;
}
int a;
int b;
/**
* @param THREAD_SLEEP_TIME_FOR_NOTIFICATION
* this delay is placed to give a small pause while sending the data packe
* */
try {
Thread.sleep(Constants.THREAD_SLEEP_TIME_FOR_NOTIFICATION);
} catch (InterruptedException e) {
e.printStackTrace();
}
sentPacket = sentPacket + 1;
byte[] packetArray = Utils.getUtilsClass().toByteArray(sentPacket);
packetNoByte = Arrays.copyOf(packetArray, packetArray.length);
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
if(b-a ==0){
return;
}
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] last_packet =
new byte[packetNoByte.length + character_length];
System.arraycopy(packetNoByte, 0, last_packet,
0, packetNoByte.length);
System.arraycopy(sending_last_hex, 0, last_packet,
packetNoByte.length, sending_last_hex.length);
Log.d("Sending packets", Arrays.toString(last_packet));
// Set value for characteristic
characteristic.setValue(last_packet);
notifyCharacteristicChanged();
isComplete = true;
customHandler.removeCallbacks(updateTimerThread);
currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
Log.d("Collection", "End Time: " + currentDateTimeString);
Utils.getUtilsClass().sendNotification(getApplicationContext(), "Data Transfer", "Transfer Complete");
} else {
Log.i("", "CONTINUE PACKET ");
a = ((sending_continue_hex.length) * time);
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
byte[] sending_continue_packet =
new byte[packetNoByte.length + sending_continue_hex.length];
System.arraycopy(packetNoByte, 0, sending_continue_packet,
0, packetNoByte.length);
System.arraycopy(sending_continue_hex, 0, sending_continue_packet,
packetNoByte.length, sending_continue_hex.length);
Log.d("data transfer a", String.valueOf(a));
Log.d("data transfer b", String.valueOf(b));
Log.d("data trans bytes", String.valueOf(sending_continue_hex.length));
if(output == null){
output = new ByteArrayOutputStream();
}
try {
if {
characteristic.setValue(sending_continue_packet);
Log.d("Sending packets", Arrays.toString(sending_continue_packet));
notifyCharacteristicChanged();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d("Data byte", "times " + time);
if (isComplete) {
characteristic.setValue("Completed");
notifyCharacteristicChanged();
}
runOnUiThread(() -> tv_status.setText("Data sent!!"));
}
}
更新代码
//下面的函数用于将图片byte[]打成包,存入arraylist
private void breakPackets(byte[] CHARACTERS) {
// Check the data length is large how many times with Default Data (BLE)
int times = CHARACTERS.length / DEFAULT_BYTES_IN_CONTINUE_PACKET;
totalPackets = times;
packetList = new ArrayList<>();
sendingPacket = 0;
Log.i("", "CHARACTERS.length() " + CHARACTERS.length);
byte[] sending_continue_hex = new byte[DEFAULT_BYTES_IN_CONTINUE_PACKET];
for (int time = 0; time <= times; time++) {
int a;
int b;
if (time == times) {
Log.i("", "LAST PACKET ");
int character_length = CHARACTERS.length
- DEFAULT_BYTES_IN_CONTINUE_PACKET * times;
byte[] sending_last_hex = new byte[character_length];
a = (sending_continue_hex.length) * time;
b = a + character_length;
sending_last_hex = Arrays.copyOfRange(CHARACTERS, a, b);
//packetList is an ArrayList<byte[]>
packetList.add(sending_last_hex);
startSendingPackets(sendingPacket);
} else {
a = (sending_continue_hex.length) * time;
b = a + DEFAULT_BYTES_IN_CONTINUE_PACKET;
sending_continue_hex = Arrays.copyOfRange(CHARACTERS, a, b);
packetList.add(sending_continue_hex);
}
Log.d("Data byte", "times " + time);
}
}
//the following function is used to set the byte[] from the arraylist to the characteristics and then notify the characteristics
private void startSendingPackets(int packet) {
isCommand = false;
mSampleCharacteristic.setValue(packetList.get(packet));
notifyCharacteristicChanged();
Log.i("packeting", "Sending ------------> " + packet);
}
/*************************************************/
@Override
public void onNotificationSent(BluetoothDevice device, int status) {
super.onNotificationSent(device, status);
//check if status is success
if (status == BluetoothGatt.GATT_SUCCESS) {
//if status is not successful isExecutable is false and the else loop is executed to resend the same packet that has failed
if (isExecutable) {
// Log.i("packeting", "Sent ------------> " + sendingPacket);
sendingPacket = sendingPacket + 1;
int size = packetList.size();
if (sendingPacket <= size-1) {
startSendingPackets(sendingPacket);
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
} else {
sendCommand("Completed");
}
} else {
startSendingPackets(sendingPacket);
isExecutable = true;
Log.d(MainActivity.TAG, "Notification sent. Status: " + status + " sending packet no --" + sendingPacket);
}
}else{
//if status is not successful
isExecutable = false;
Log.d(MainActivity.TAG, "Notification sent. fail Status: " + status );
}
}
When multiple notifications are to be sent, an application must wait for this callback to be received before sending additional notifications.
这意味着在您调用notifyCharacteristicChanged
之后,您不能再次调用notifyCharacteristicChanged
,直到收到回调onNotificationSent
。因此,您需要删除 for
循环并重构代码以遵循 API 规则。
这样做的原因是为了获得流量控制。如果您只是以比 BLE link 的吞吐量更快的速度推送新数据包,则内部缓冲区会变满并且会发生数据包丢失。这就是延迟似乎有效的原因,但这不是一个可靠的解决方案,因此您应该等待 onNotificationSent
回调,因为这意味着 BLE 堆栈已准备好接受新数据包。