#java #android #firebase #firebase-storage
# #java #Android #firebase #firebase-хранилище
Вопрос:
Я использую 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
does не вызывается. На самом деле, весь поток заблокирован.
Есть ли какая-либо другая альтернатива постановке задач загрузки в очередь? Как однопоточная служба исполнителя?
Ответ №1:
В итоге я использовал ExecutorService
with 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 amp;amp; 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:
Другой подход заключается в использовании BlockingQueue .
Общая идея такова:
- вы запускаете поток, который непрерывно опрашивает из очереди, загружает заданное изображение и начинает все сначала
- вы помещаете все URL-адреса в очередь
- прослушиватель связан либо с потоком, либо с каждым изображением
Более менее требуемый код:
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();
Этот поток может быть запущен службой, чтобы он мог продолжать работать и не привязан к пользовательскому интерфейсу. Затем действия могут привязать Службу к взаимодействию с ней.
Ответ №3:
Вы также можете использовать 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.