0

So, I have that method, that I want to use to get the download url for an image stored in the Firebase Storage, that's the method:

private void  getUrlAsync (final StorageReference ref, Uri file){
        UploadTask uploadTask = ref.putFile(file);

        Task<Uri> urlTask = uploadTask.continueWithTask(new Continuation<UploadTask.TaskSnapshot, Task<Uri>>() {
            @Override
            public Task<Uri> then(@NonNull Task<UploadTask.TaskSnapshot> task) throws Exception {
                // Continue with the task to get the download URL
                return ref.getDownloadUrl();
            }
        }).addOnCompleteListener(new OnCompleteListener<Uri>() {
            @Override
            public void onComplete(@NonNull Task<Uri> task) {
                if (task.isSuccessful()) {
                    mDownloadUri = Objects.requireNonNull(task.getResult()).toString();
                    Log.d(TAG, "Get Url " + mDownloadUri);
                } else {
                    // Handle failures
                    // ...
                }
            }
        });
    }

mDownloadUri is a global variable. Good, the Log.d in this method tells me that mDownloadUri = https://firebasestorage.googleapis.com/v0/b/lapitchat-4c76f.appspot.com/o/profile_images%2FCYUpqZy6AOOhLjvc7PG9JvnoY2p1.jpg?alt=media&token=6bd1b103-209d-4fad-b51e-9907a43f098d, that's ok. If I call this method this way, mDownloadUri is null:

final Uri resultUri = result.getUri();

final String current_user_id = mCurrentUser.getUid();

final StorageReference filepath = mImageStorage.child("profile_images").child(current_user_id + ".jpg");

filepath.putFile(resultUri).addOnCompleteListener(new OnCompleteListener<UploadTask.TaskSnapshot>() {
                    @Override
                    public void onComplete(@NonNull Task<UploadTask.TaskSnapshot> task) {

                        if(task.isSuccessful()) {

                        mProgressDialog.dismiss();
                        getUrlAsync(filepath, resultUri);
                        Log.d(TAG, "Put File " + mDownloadUri);
                        }

}

For context, result is an image that comes back from another activity, where I crop an image. What can I do? Why outside the getUrlAsync method the mDownloadUri is null?

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
Alexandru Stroescu
  • 1,067
  • 1
  • 9
  • 16
  • 1
    My guess would be that because you're setting mDownloadUri inside the completion handler of getUrlAsync, that the line of code that's doing the logging is being executed before the code in the completion handler that sets the global variable, hence it's null when the logging code is invoked. You should restructure your code in such a way that anything that depends on mDownloadUri happens inside the completion handler, then there is no need for the global variable, which is usually a code smell anyway. – Astronought May 21 '19 at 21:28
  • Another reason can be the [Shadowing](https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html), if you would have more mDownloadUri defined in the outer classes. – m4gic May 21 '19 at 21:32
  • You can also check **[this](https://stackoverflow.com/questions/47847694/how-to-return-datasnapshot-value-as-a-result-of-a-method/47853774)** out. – Alex Mamo May 22 '19 at 07:30

1 Answers1

1

this won't log anything:

getUrlAsync(filepath, resultUri);
Log.d(TAG, "Put File " + mDownloadUri);

because Log.d() is being executed synchronously (it won't wait for the OnCompleteListener), while getUrlAsync() will be executed asynchronously (independent from the rest of the execution).

mDownloadUri only has a value once the OnCompleteListener<Uri> delivered the result... that it is a timing-issue based upon synchronous vs. asychronous execution can be easily proven by waiting:

getUrlAsync(filepath, resultUri);
while(this.mDownloadUri == null) {
    Thread.sleep(100); // this obviously is not the answer.
    Log.d(TAG, "mDownloadUri is NULL; awaiting delivery.");
}
Log.d(TAG, "mDownloadUri is: " + this.mDownloadUri);

rather proper would be to add another method, which handles the task's result; for example:

public void OnDownloadUri(Uri uri) {
    ...
}

and then call back from within the OnCompleteListener<Uri> (with 1-2 callbacks this still could be nested, but when it gets more complex, methods alike this can indeed help to reduce callback hell).

Martin Zeitler
  • 1
  • 19
  • 155
  • 216
  • Is there other method to get the download url? I saw in a tutorial that in an older version you needed 1 line, in the onCompleteListener, using the task, but now I feel that I'm doing to much just to get that download url stored in a string. – Alexandru Stroescu May 22 '19 at 12:09
  • @AlexandruStroescu for what you need another method to get the URL? better get yourself familiar with asynchronous execution... else you'll struggle with Android Java, because it is being used over and over. – Martin Zeitler May 22 '19 at 15:43