I want to download my all files from the firebase folder on android.
this is my code
try {
StorageReference reference = storage.getReference().child("Ringtone");
reference.listAll().addOnCompleteListener(new OnCompleteListener<ListResult>() {
@Override
public void onComplete(@NonNull Task<ListResult> task) {
if (task.isComplete()){
task.addOnSuccessListener(new OnSuccessListener<ListResult>() { /////line 82 == 1st line where i am getting error
@Override
public void onSuccess(ListResult listResult) {
for (StorageReference item : listResult.getItems()) {
RingtoneModel ringtoneModel = new RingtoneModel();
ringtoneModel.setUri(item.getDownloadUrl().getResult()); /////line 88 == 2nd line where i am getting error here
item.getMetadata().addOnSuccessListener(new OnSuccessListener<StorageMetadata>() {
@Override
public void onSuccess(StorageMetadata storageMetadata) {
ringtoneModel.setSongName(storageMetadata.getName());
Toast.makeText(MainActivity.this, "ring name saved", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Toast.makeText(MainActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
Log.e("metadata error",e.getMessage());
}
});
}
}
});}else {
Toast.makeText(MainActivity.this, (CharSequence) task.getException(), Toast.LENGTH_SHORT).show();
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e("list all error",e.getMessage());
}
}); }catch (Exception e){
Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
Log.e("all errors",e.getMessage());}
}
Logcat error message
/com.example.alarm E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.alarm, PID: 433
java.lang.IllegalStateException: Task is not yet complete
at com.google.android.gms.common.internal.Preconditions.checkState(Unknown Source)
at com.google.android.gms.tasks.zzu.zzb(Unknown Source)
at com.google.android.gms.tasks.zzu.getResult(Unknown Source)
at com.example.alarm.MainActivity$3.onSuccess(MainActivity.java:88)
at com.example.alarm.MainActivity$3.onSuccess(MainActivity.java:82)
my folders and files path in firebase database
Firebase storage >Rington<my files
<--what i am tried --> i tried adding addonsuuccesslistner , addoncompletelitner, task.isComplete(), task.issuccessfull() and may more things
While addOnSuccessListener
and addOnFailureListener
are useful for handling the results of an asynchronous task, they aren't very useful when you need to chain things together like you are here.
Looking at your code, you call the following line:
StorageReference item;
item.getDownloadUrl().getResult()
The exception you get is thrown because you aren't waiting for this task to finish before calling getResult()
.
Task<String> downloadUrlTask = item.getDownloadUrl();
downloadUrlTask
.addOnSuccessListener(result -> {
// now do something with result
})
.addOnFailureListener(e -> {
// now do something with the exception
});
While the above code is helpful if that's all you needed, what you are trying to do is wait for it to finish along with getMetadata()
so you can create an instance of your RingtoneModel
class.
This is where the Tasks
utility class comes in.
Tasks.whenAll(Collection<Task<?>> tasks)
will succeed only when all of the tasks in tasks
successfully finish.
Task<String> downloadUrlTask = item.getDownloadUrl();
Task<StorageMetadata> metadataTask = item.getMetadata();
return Tasks
.whenAll([ // wait for these tasks to finish successfully
downloadUrlTask,
metadataTask
])
.addOnSuccessListener(() -> { // then assemble its RingtoneModel
RingtoneModel ringtoneModel = new RingtoneModel();
ringtoneModel.setUri(downloadUrlTask.getResult());
ringtoneModel.setSongName(metadataTask.getResult().getName());
// we've now got a RingtoneModel! but what to do with it?
});
Instead of using addOnSuccessListener()
in the above code, if we use onSuccessTask()
we can return the RingtoneModel
back so that the Task<void>
from Tasks.whenAll()
is now a Task<RingtoneModel>
. To do this, we need to use Tasks.forResult()
which will take our RingtoneModel
and turn it into a Task<RingtoneModel>
.
// function that takes a StorageReference and
// returns a task that resolves to a RingtoneModel
StorageReference item -> {
Task<String> downloadUrlTask = item.getDownloadUrl();
Task<StorageMetadata> metadataTask = item.getMetadata();
return Tasks
.whenAll([ // wait for these tasks to finish successfully
downloadUrlTask,
metadataTask
])
.onSuccessTask(() -> { // then assemble its RingtoneModel
RingtoneModel ringtoneModel = new RingtoneModel();
// because these task have finished by now,
// you can safely use getResult() here
ringtoneModel.setUri(downloadUrlTask.getResult());
ringtoneModel.setSongName(metadataTask.getResult().getName());
return Tasks.forResult(ringtoneModel);
});
}
Now that we have a RingtoneModel
for one StorageReference
, we want to turn the List<StorageReference>
into a List<RingtoneModel>
. We can do this using
List<Task<RingtoneModel>> getRingtoneTasks = listResult.getItems()
.stream()
.map(convertToRingtoneModel); // the function above
Note here that we have List<Task<RingtoneModel>>
instead of Task<List<RingtoneModel>>
. To flip this around, we make use of Tasks.whenAll()
, onSuccessTask()
, stream().map()
and Tasks.forResult()
again.
return Tasks
.whenAll(getRingtoneTasks)
.onSuccessTask(() -> {
return Tasks.forResult(
getRingtoneTasks
.stream()
.map(task -> task.getResult());
);
});
Now you might look at that block above and think that's a pretty neat method to have as a utility method, and you are right, because you can just use this to do the same thing:
return Tasks.whenAllSuccess(getRingtoneTasks);
Mashing all of this together, gives:
public Task<List<RingtoneModel>> getRingtones() {
StorageReference reference = storage.getReference().child("Ringtone");
return reference.listAll() // find all objects under /Ringtone
.onSuccessTask(listResult -> {
// for each item found, assemble a RingtoneModel
List<Task<RingtoneModel>> getRingtoneTasks = listResult.getItems()
.stream()
.map((item) -> {
// A RingtoneModel needs the download URL and the object's metadata
Task<String> downloadUrlTask = item.getDownloadUrl();
Task<StorageMetadata> metadataTask = item.getMetadata();
return Tasks
.whenAll([ // wait for these tasks to finish successfully
downloadUrlTask,
metadataTask
])
.onSuccessTask(() -> { // then assemble the RingtoneModel and return it
RingtoneModel ringtoneModel = new RingtoneModel();
ringtoneModel.setUri(downloadUrlTask.getResult());
ringtoneModel.setSongName(metadataTask.getResult().getName());
return Tasks.forResult(ringtoneModel);
});
});
return Tasks.whenAllSuccess(getRingtoneTasks);
});
}
Then when you want to get all the ringtones in your Cloud Storage you just need:
getRingtones()
.addOnSuccessListener(ringtoneModelList -> {
Toast.makeText(MainActivity.this, "Found " + ringtoneModelList.size() + " ringtones.", Toast.LENGTH_SHORT).show();
// I recommend storing ringtoneModelList in your database of choice
})
.addOnFailureListener(e -> {
Toast.makeText(MainActivity.this, "Failed to get any ringtones.", Toast.LENGTH_SHORT).show();
Log.e("getRingtones error", e); // <-- log the full error to help with debugging
});