我在我的后端使用Firebase
,而在客户端(Android)方面,我尝试按顺序下载一堆图像。我将迭代器包装在synchronized
块中,等待每个图像下载。
private Object mLock = new Object();
private void downloadImages() {
List<StorageReference> storageReferences = getStorageReferences();
synchronized (mLock) {
// Iterate trough all image references
for (StorageReference sr : storageReferences) {
sr.getBytes(ONE_MB_BUFFER).addOnCompleteListener(new OnCompleteListener<byte[]>() {
@Override
public void onComplete(Task<byte[]> task) {
if (task.isSuccessful()) {
// Success, image downloaded
}
// Notify, that we have downloaded the image
synchronized (mLock) {
mLock.notify();
}
}
});
// Await until we acquire the lock
try {
mLock.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
addOnCompleteListener
中的回调未被调用。实际上,整个线程都被锁定了。
还有其他替代方法可以排队下载任务吗?像单线程执行器服务一样?
答案 0 :(得分:1)
另一种方法是使用BlockingQueue。
总体思路是:
所需代码更少:
final BlockingQueue<URL> queue = new LinkedBlockingQueue<>();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
URL url = queue.poll();
// Download the image and notify the listener
}
}
}).start();
此线程可以由服务启动,因此它可以继续运行并且不依赖于UI。然后,活动可以绑定服务以与其进行交互。
答案 1 :(得分:0)
我最终将ExecutorService
与newSingleThreadExecutor
一起使用。如果您想要其他自定义,例如,超时,您可能需要使用newScheduledThreadPool
。您可以创建一个线程池并同时执行多个线程。
public class ImageDownloadService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
downloadImages();
}
private void downloadImages() {
ExecutorService executor = Executors.newSingleThreadExecutor();
List<StorageReference> storageReferences = getStorageReferences();
for (StorageReference sr : storageReferences) {
Future<byte[]> future = executor.submit(new FutureImageResult(sr));
byte[] data = null;
try {
data = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
if (data != null && data.length > 0) {
// Image downloaded successfully
}
}
}
}
提交给执行者服务的future
。
public class FutureImageResult implements Callable<byte[]> {
private StorageReference mStorageReference;
private boolean mIsFailure;
public FutureImageResult(StorageReference storageReference) {
mStorageReference = storageReference;
}
@Override
public byte[] call() throws Exception {
Task<byte[]> task = mStorageReference.getBytes(1024 * 1024);
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
mIsFailure = true;
}
});
while (!task.isComplete() || mIsFailure);
byte[] data = task.getResult();
return data;
}
}
答案 2 :(得分:0)
您也可以使用CountDownLatch
来锁定您的工作线程,直到操作完成。像这样:
private void downloadImages() {
List<StorageReference> storageReferences = getStorageReferences();
CountDownLatch waitForDownload = new CountDownLatch(storageReferences.size());
// Iterate trough all image references
for (StorageReference sr : storageReferences) {
sr.getBytes(ONE_MB_BUFFER).addOnCompleteListener(new OnCompleteListener<byte[]>() {
@Override
public void onComplete(Task<byte[]> task) {
// Notify, that we have downloaded the image and continue
waitForDownload.countDown();
}
});
}
// Lock until we download all images
waitForDownload.await();
// Continue with the rest of your serialized work having all images downloaded
...
}
参考文献:CountDownLatch javadoc。