Reputation: 377
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");
}
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
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
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