概述
微信发送图片、视频的功能很多人都用过,不知道是否有人注意到了它是无序的。从相册中选择图片、视频时有序号,但是发送出去却是乱序。对社交app来说这无可厚非,但是办公软件无序发送并不友好
现状
发送消息到服务端没有提供批量接口,批量发送功能采用发送单个消息的接口实现
模拟代码如下:
// 设核心线程数 = 终端内核数
ExecutorService executor = Executors.newCacheThreadExecutor();
// 富媒体类型
enum MEDIA_TYPE {
Photo, Video;
}
// 接受用户选择的文件,多线程异步发送
public void sendMediaMessages(ArrayList<Media> meidas) {
// 此时接受到的数据是用户选择的顺序
executor.submit(new Runnable() {
@Override
public void run() {
// 遍历选中的文件发送
for (int i = 0; i < meidas.size(); i++) {
Media media = meidas.get(i);
Upload.upload(media);
}
}
});
}
class Upload implements UploadListener {
public static void upload(Media media) {
// 发送到服务端
}
@Override
public void onFailed(Media media) {}
@Override
public void onSuccess(Media media) {}
@Override
public void onFirstFrameSuccess(Media media) {}
}
现状
- 服务端不支持批量接口以及排序功能,需要客户端处理
- 尽量小的改动不影响其他功能,接受性能损耗
需求
- 图片和视频批量发送时保证各自类型相对有序
- 多个视频需要发送时先占位,避免给用户歧义
- 该应用一次只能选择9个文件,上传过程中再次选中文件发送依然要保证相对有序
// 新增三个队列,用于维护图片、视频
private ConcurrentLinkedDeque<Media> photos = new ConcurrentLinkedDeque<>();
private ConcurrentLinkedDeque<Media> videos = new ConcurrentLinkedDeque<>();
private ConcurrentLinkedDeque<Media> videoFirstFrames = new ConcurrentLinkedDeque<>();
...
public void sendMediaMessages(ArrayList<Media> meidas) {
for (Media media: medias) {
if (media.type == photo) {
photos.add(media);
} else {
videoFirstFrames.add(media);
}
}
// 省略图片的,比较简单
// 判断Upload.firstFrameListener == null,二次上传加入队列即可
if (Upload.firstFrameListener == null && !photos.isEmpty()) {
Upload.setFirstFrameListener(firstFrameListener);
uploadNext(videoFirstFrames.poll());
}
}
private UploadListener videoListener = (media) -> {
if (videos.isEmpty()) {
Upload.setVideoListener(null);
return;
}
uploadNext(videos.poll());
}
private FirstFrameListener firstFrameListener = (media) -> {
// 占位后,放到videos队列等待上传
videos.add(media);
if (Upload.videoListener == null && !videos.isEmpty()) {
Upload.setVideoListener(videoListener);
uploadNext(videos.poll());
}
if (videoFirstFrames.isEmpty()) {
Upload.setFirstFrameListener(null);
return;
}
uploadNext(videoFirstFrames.poll());
}
private void uploadNext(Media media) {
executor.submit(() -> {
Upload.upload(media);
});
}
class Upload implements UploadCallback {
public UploadListener photoListener;
public UploadListener videoListener;
public FirstFrameListener firstFrameListener;
...
}