Предположим, у вас есть 150 изображений, хранящихся в папке изображений в вашем хранилище Firebase. Каждое изображение должно иметь название, которое будет вставлено в процессе загрузки. Теперь предположим, что вы хотите отобразить все эти изображения в RecyclerView
. В этом сценарии вам потребуется создать 151 отдельное подключение к серверу вместо одного. Это может показаться нелогичным, поэтому позвольте мне объяснить.
Первое соединение — получение всех изображений с помощью следующей строки кода: FirebaseStorage.getInstance().getReference().child("Images/").listAll()
. Это вернет список всех изображений в папке.
Для каждого изображения вам потребуется выполнить еще одно подключение к серверу, чтобы получить метаданные для этого изображения. Это означает, что для каждого отдельного изображения вам нужно будет сделать отдельный вызов на сервер, в результате чего будет установлено 151 соединение.
Вот предварительный просмотр кода для этого сценария:
FirebaseStorage.getInstance().getReference().child("Images/").listAll().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (int i = 0; i < 150; i++) {
task.getResult().getItems().get(0).getMetadata().addOnCompleteListener(task1 -> {
if (task1.isSuccessful()) {
task1.getResult().getCustomMetadata("title"); //Finally I got the image title that stored inside image, it is cumbersome process.
} else {
//Handle error
}
});
}
} else {
//Handle error
}
});
Впрочем, вполне возможно, что я что-то упускаю. Это действительно так, или есть лучшее решение? Я не хочу называть изображение непосредственно именем файла, потому что я пробовал это раньше и обнаружил, что длины недостаточно.
Буду очень признателен за любые предложения или альтернативные решения этой проблемы. Заранее спасибо!
Кроме того, я хотел бы выразить благодарность OpenAI GPT-3 за помощь в написании этого четкого и лаконичного вопроса.
@Dharmaraj Firebase Storage (в настоящее время без правил)
Вы можете создать внутренний метод API, который возвращает список заголовков за один вызов.
Теперь я знаю, что ваш код был предназначен только для демонстрации, но....
ваш код говорит task.getResult().getItems().get(0)
, что означает, что только первый элемент используется 150 раз
Вызов task.getResult().getItems()
внутри for
означает, что вы вызываете что-то, что нужно вызвать только один раз 150 раз.
Использование harcoded 150 вместо items.length не является хорошей идеей.
Когда вы видите, что ваш код громоздкий или трудный для чтения, следуйте «Единой ответственности», чтобы разделить его на более простые методы.
В приведенном ниже коде я использую тип «Item», потому что теперь я не знаю имя типа, возвращаемое task.getResult().getItems()
void getAllImagesMetadata() {
FirebaseStorage.getInstance().getReference().child("Images/").listAll().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
Item []items = task.getResult().getItems() ;
for (int i = 0; i < items.length ; i++) {
title[i] = getTitle(items[i] ;
}
} else {
//Handle error
}
// Make sure the connection to get list of all items is closed here
}
string getTitle(Item item) {
string title ;
item.getMetadata().addOnCompleteListener(task -> {
if (task.isSuccessful()) {
title = task.getResult().getCustomMetadata("title");
} else {
//Handle error
}
// Make sure the connection to get the metada is closed here
return title ;
}
Это не окончательный код, просто демонстрация общей идеи. Благодарю за ваш ответ.
Демонстрационный код не должен отвлекаться, в любом случае вам нужно разделить код на методы и убедиться, что после завершения задачи соединение будет закрыто.
Прежде всего, FirebaseStorage.getInstance()
— это синглтон, который всегда будет создавать один экземпляр.
Кроме того, хотя решение Mauricio Gracia Gutierrez будет работать, см. ниже решение, которое использует задачи Tasks#whenAllSuccess(Collection>):
StorageReference imagesRef = FirebaseStorage.getInstance().getReference().child("Images/");
imagesRef.listAll().addOnCompleteListener(new OnCompleteListener<ListResult>() {
@Override
public void onComplete(@NonNull Task<ListResult> task) {
if (task.isSuccessful()) {
List<StorageReference> storageReferenceList = task.getResult().getItems();
List<Task<StorageMetadata>> tasks = new ArrayList<>();
storageReferenceList.forEach(item ->
tasks.add(item.getMetadata())
);
Tasks.whenAllSuccess(tasks).addOnSuccessListener(new OnSuccessListener<List<Object>>() {
@Override
public void onSuccess(List<Object> objects) {
for (Object object : objects) {
String title = ((StorageMetadata) object).getCustomMetadata("title");
Log.d("TAG", title);
}
}
});
} else {
Log.d(TAG, task.getException().getMessage()); //Never ignore potential errors!
}
}
});
Таким образом, вы будете ждать, пока у вас не будет полного списка заголовков.
Я проверю ваш код завтра после того, как закончу свою работу. Если это сработает, я выберу ваш ответ как правильный. Также, пожалуйста, расскажите мне больше о whenAllSuccess. Спасибо.
Хорошо, попробуй и скажи мне, работает ли это. Tasks#whenAllSuccess(Collection> tasks)
, на мой взгляд, лучший способ решить эту проблему, когда вам приходится иметь дело с несколькими объектами Task.
Я выберу ваш ответ как правильный, потому что я верю в ваше решение. Я проверю код позже, спасибо.
Я никогда не писал код для FirebaseStorage
, рад видеть, что был рядом ;-)
Используете ли вы Firebase Storage (поддерживающее правило безопасности) или хранилище Google Cloud напрямую только для подтверждения?