user3546621
user3546621

Reputation: 377

Firebase Uploads freeze UI

How to prevent the main UI thread from blocking?

I am trying to upload a list of documents with FirebaseAPI. Right before uploads start, I want to update the UI and show a progress view - that will stay visible until all uploads are completed. The following code blocks the main UI thread and the progress view is not shown immediately.

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    if (id == android.R.id.home) {
        onBackPressed();
    }
    if (id == R.id.action_add) {
        uploadDocuments();
    }
    return super.onOptionsItemSelected(item);
}

private void uploadDocuments(){
    final ProgressDialog dialog = new ProgressDialog(this);
    dialog.setIndeterminate(true);
    dialog.setMessage("Uploading Docs");
    dialog.show();

    final ArrayList<Task<?>> tasks = new ArrayList<>();
    for(Uri document: documents){
        StorageReference filesRef = mStorageRef.child("files/" + document.toString());
        UploadTask uploadTask = filesRef.putFile(document);
        uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
            @Override
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                StorageMetadata storageMetadata = taskSnapshot.getMetadata();

                Uri downloadFileUrl = null;
                if (storageMetadata != null){
                    downloadFileUrl = taskSnapshot.getMetadata().getDownloadUrl();
                }
                if (downloadFileUrl != null){
                    downloadFileUrls.add(downloadFileUrl);
                    Log.d(TAG, "Download Url: " + downloadFileUrl.toString());
                }
            }
        });
        tasks.add(uploadTask);
    }
    Tasks.whenAll(tasks).addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            Log.d(TAG, "ALL TASKS HAVE BEEN UPLOADED");
            dialog.cancel();
        }
    });
    Log.d(TAG, "After Tasks.whenAll");
}

UPDATE

I have noticed that the main UI thread blocks when 'external' content is uploaded, i.e with document retrieved from google drive and associated uri of type 'content://com.google.android.apps.docs.storage/document/...'

This doesn't happen when I upload images from 'content://media/external/images/media/...'

Upvotes: 1

Views: 984

Answers (2)

Bob Snyder
Bob Snyder

Reputation: 38319

Your update makes clear that the blocking occurs when uploading "external" content. I observed the delays you described when uploading a Google Drive file. I bracketed the call to putFile() with log statements. They showed that creation of the upload task for a local file required less than 100 msec, and a Google Drive file was multiple seconds.

Although the Firebase upload of the file occurs on a worker thread, there is apparently some initial processing of the file that occurs on the main thread. That processing takes a surprisingly long time for a Google Drive file. Perhaps there is some network I/O.

The code below is a basic example of how you could use an AsyncTask to perform the upload task creation in a background thread. This is offered only as a basic demonstration. A complete implementation requires addressing issues like allowing the user to cancel a long-running upload, and handling configuration changes that cause the app to restart.

if (id == R.id.action_add) {
    //uploadDocuments();
    new UploadDocsAsyncTask(this, progressView).execute(documents);
}

public class UploadDocsAsyncTask extends AsyncTask<ArrayList<Uri>, Void, ArrayList<Uri>> {
    private static final String TAG = "UploadDocsAsyncTask";

    private final ProgressDialog mDialog;
    private final View mProgView;
    private final StorageReference mStorageRef;

    public UploadDocsAsyncTask(Context cxt, View view) {
        mProgView = view;
        mDialog = new ProgressDialog(cxt);
        mDialog.setIndeterminate(true);
        mDialog.setMessage("Uploading Docs");
        mStorageRef = FirebaseStorage.getInstance().getReference();
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        Log.d(TAG, "Pre-Execute");
        //mProgView.setVisibility(View.VISIBLE);
        mDialog.show();
    }

    @Override
    protected ArrayList<Uri> doInBackground(ArrayList<Uri>... documents) {
        final ArrayList<UploadTask> tasks = new ArrayList<>();
        final ArrayList<Uri> downloadUrls = new ArrayList<>();

        for (Uri document : documents[0]) {
            StorageReference filesRef = mStorageRef.child("files/" + document.toString());
            Log.d(TAG, "Uploading: " + document.toString());
            UploadTask uploadTask = filesRef.putFile(document);
            Log.d(TAG, "Upload task created");
            uploadTask.addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
                @Override
                public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
                    Log.d(TAG, "Upload Success: " + taskSnapshot.getUploadSessionUri());
                    StorageMetadata storageMetadata = taskSnapshot.getMetadata();
                    //Log.d(TAG, storageMetadata.getDownloadUrl().toString());
                    if (storageMetadata != null){
                        Uri downloadFileUrl = taskSnapshot.getMetadata().getDownloadUrl();
                        Log.d(TAG, "DownloadUrl= " + downloadFileUrl);
                        downloadUrls.add(downloadFileUrl);
                    }
                }
            });
            tasks.add(uploadTask);
        }

        Log.d(TAG, "All upload tasks created");

        try {
            Log.d(TAG, "Waiting...");
            Tasks.await(Tasks.whenAll(tasks));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        Log.d(TAG, "End of background processing");
        return downloadUrls;
    }

    @Override
    protected void onPostExecute(ArrayList<Uri> downloadUrls) {
        super.onPostExecute(downloadUrls);
        Log.d(TAG, "Post-Execute: Size=" + downloadUrls.size());

        // TODO: do something with the downloadUrls
        //mProgView.setVisibility(View.GONE);
        mDialog.cancel();
    }
}

Upvotes: 2

Ivan
Ivan

Reputation: 782

You can maybe create a thread to make the upload so not everything done in main thread. This will make your progress view not to freeze.

Upvotes: 0

Related Questions