Skip to content
Alex Gotev edited this page Oct 18, 2020 · 7 revisions

Contents

Check JavaDocs for full API reference.

HTTP multipart/form-data upload (RFC2388)

This is the most common way to upload files on a server. It's the same kind of request that browsers do when you use the <form> tag with one or more files. Here's a minimal example:

public void uploadMultipart(final Context context) {
    try {
        String uploadId =
          new MultipartUploadRequest(context, "http://upload.server.com/path")
            // starting from 3.1+, you can also use content:// URI string instead of absolute file
            .addFileToUpload("/absolute/path/to/your/file", "your-param-name")
            .setNotificationConfig(new UploadNotificationConfig())
            .setMaxRetries(2)
            .startUpload();
    } catch (Exception exc) {
        Log.e("AndroidUploadService", exc.getMessage(), exc);
    }
}
feature description
multiple files in a single request yes. There are many ways to do it. Example.
support for HTTP Basic Auth yes. Add .setBasicAuth("username", "password") when you build the request
add custom request headers yes. You can also set them with request interceptors if you are using OkHttp stack.
add custom request parameters yes
default charset US-ASCII. Add .setUtf8Charset() when you build the request to use UTF-8, to be able to use non-latin file names and parameters
resuming uploads no. If an upload fails at some point, the temporary data will be discarded automatically by your server and the upload will start from the beginning on the next request. The server sees a multipart request as a single stream of data, as per RFC specs.

To discover all the available options, check MultipartUploadRequest JavaDocs

HTTP binary upload

The binary upload uses a single file as the raw body of the upload request. Here's a minimal example:

public void uploadBinary(final Context context) {
    try {
        // starting from 3.1+, you can also use content:// URI string instead of absolute file
        String filePath = "/absolute/path/to/file";
        String uploadId =
          new BinaryUploadRequest(context, "http://upload.server.com/path")
            .setFileToUpload(filePath)
            .addHeader("file-name", new File(filePath).getName())
            .setNotificationConfig(new UploadNotificationConfig())
            .setMaxRetries(2)
            .startUpload();
    } catch (Exception exc) {
        Log.e("AndroidUploadService", exc.getMessage(), exc);
    }
}
feature
multiple files in a single request no. Only one file per request, which will be in the request body. It is more bandwidth efficient than HTTP/Multipart if all you need is to upload only one file to a server, without passing additional data in the request. If you want to pass the original file name to your server using this kind of request, the only way you can do it is by setting a custom header. Bear in mind that HTTP headers are encoded in US-ASCII, so don't use non-latin values.
support for HTTP Basic Auth yes. Add .setBasicAuth("username", "password") when you build the request
add custom request headers yes. You can also set them with request interceptors if you are using OkHttp stack.
add custom request parameters no
default charset US-ASCII. UTF-8 is not supported because you can only set request headers, which must be in US-ASCII encoding
resuming uploads no. If an upload fails at some point, the temporary data will be discarded automatically by your server and the upload will start from the beginning on the next request. The server sees the request as a single stream of data.

To discover all the available options, check BinaryUploadRequest JavaDocs

Starting from release 3.0, you can also make FTP uploads by adding the FTP upload module. Refer to the link for installation instructions end examples.

What is the upload ID?

When you create an upload request (like shown above), you will get a unique string which identifies the upload task. Save that string somewhere as you will need it to monitor upload status and to be able to stop the upload task.
If you need it, you can generate an uploadID yourself and pass it to the constructor of the upload request that you are creating. When doing that, be careful to always generate unique strings to avoid problems.

Notification configuration

To create an upload request with a notification, you have to add the following to your request builder:

.setNotificationConfig(new UploadNotificationConfig())

If you do not add this line to your builder on Android < 8.0 Oreo, the upload will be processed in the background without the user noticing it, however, you may encounter problems when in standby or in Doze mode, because by Google design, Android needs a notification in order to give the upload service more execution time and prevent killing it or entering in Doze or Standby mode.

If you are on Android >= 8.0 Oreo, the Upload Service will throw an exception telling you to do so if you've not a set a notification configuration. This is due to the policy introduced by Google which forces you to display a notification when a background service is run. Check Android official docs for more detailed information.

A notification for an upload task can have 4 states:

  • Progress when the upload is in progress
  • Completed when the upload has been completed successfully
  • Error when the upload failed due to networking problems or an error response from the server (HTTP status code >= 400)
  • Cancelled when the user cancelled the upload task

And you can see how they look here (example on Android 6):

notification-statuses

You can finely tune every status of the notification. Refer to UploadNotificationConfig for all the available configuration options.

You can also use placeholders in notification titles and messages to display some useful data regarding the upload in the notification.

As of release 3.4 Android notification channels are supported. Refer to the provided issue link for all the details.

Here's a little example taken from the demo app (you can find it in the examples), which produces the notifications shown in the screenshot above:

import static net.gotev.uploadservice.Placeholders.*;

protected UploadNotificationConfig getNotificationConfig(final String uploadId, @StringRes int title) {
    UploadNotificationConfig config = new UploadNotificationConfig();

    PendingIntent clickIntent = PendingIntent.getActivity(
            this, 1, new Intent(this, MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);

    config.setTitleForAllStatuses(getString(title))
            .setRingToneEnabled(true)
            .setClickIntentForAllStatuses(clickIntent)
            .setClearOnActionForAllStatuses(true);

    config.getProgress().message = "Uploaded " + UPLOADED_FILES + " of " + TOTAL_FILES 
                                   + " at " + UPLOAD_RATE + " - " + PROGRESS;
    config.getProgress().iconResourceID = R.drawable.ic_upload;
    config.getProgress().iconColorResourceID = Color.BLUE;

    config.getCompleted().message = "Upload completed successfully in " + ELAPSED_TIME;
    config.getCompleted().iconResourceID = R.drawable.ic_upload_success;
    config.getCompleted().iconColorResourceID = Color.GREEN;

    config.getError().message = "Error while uploading";
    config.getError().iconResourceID = R.drawable.ic_upload_error;
    config.getError().iconColorResourceID = Color.RED;

    config.getCancelled().message = "Upload has been cancelled";
    config.getCancelled().iconResourceID = R.drawable.ic_cancelled;
    config.getCancelled().iconColorResourceID = Color.YELLOW;

    return config;
}

To add actions for a single notification state:

config.getProgress().actions.add(new UploadNotificationAction(iconResourceId, label, pendingIntent));

e.g.

config.getProgress().actions.add(new UploadNotificationAction(
                R.drawable.ic_cancelled,
                getString(R.string.cancel_upload),
                NotificationActions.getCancelUploadAction(this, 1, uploadId)));

or the same action for all the states:

config.addActionForAllStatuses(new UploadNotificationAction(iconResourceId, label, pendingIntent));

If you're not familiar with PendingIntents, read here.

You can also (and you are encouraged to) use your localized strings in titles and messages (e.g.):

config.setTitleForAllStatuses(getString(R.string.title));

config.getProgress().message = getString(R.string.uploading);
config.getCompleted().message = getString(R.string.upload_success);
config.getError().message = getString(R.string.upload_error);
config.getCancelled().message = getString(R.string.upload_cancelled);

By default, the library sends a progress update once every 166ms for each upload. You can tune that throttle by setting:

UploadService.PROGRESS_REPORT_INTERVAL = 1000;

where 1000 is the number of milliseconds between a progress update and the next one. I recommend you to do that in the Application class

Tune exponential backoff algorithm

When an upload fails, Android Upload Service will automatically retry it if you have invoked setMaxRetries on the upload request object. To optimize network usage, every retry will be made some time after each failure. If a subsequent failure happens, the next attempt will be furtherly delayed, and so on. In this way if your network drops at some time, you will not experience a burst in subsequent upload attempts, which drains your battery.

Backoff algorithm is ruled by the following constants, which you can tune as you wish starting from release 3.0. I recommend you to do that in the Application class

  • UploadService.INITIAL_RETRY_WAIT_TIME: sets the time to wait in milliseconds before the next attempt when an upload fails for the first time. From the second time onwards, this value will be multiplied by UploadService.BACKOFF_MULTIPLIER to get the time to wait before the next attempt. By default is set to 1s.
  • UploadService.BACKOFF_MULTIPLIER: sets the backoff timer multiplier. By default is set to 2, so every time that an upload fails, the time to wait between retries will be multiplied by 2. E.g. if the first time the wait time is 1s, the second time it will be 2s and the third time it will be 4s.
  • UploadService.MAX_RETRY_WAIT_TIME: Sets the maximum time to wait in milliseconds between two upload attempts. This is useful because every time an upload fails, the wait time gets multiplied by UploadService.BACKOFF_MULTIPLIER and it's not convenient that the value grows indefinitely. By default it's set to 100s.

Refer to the code for further reference.

backoff chart

Tune the number of parallel uploads

By default, UploadService gets initialized with a pool of threads equal to the number of processors on your device, as you can see here. So, for example, if you have a quad-core device, the number of maximum parallel uploads will be 4. You can tune that by setting:

UploadService.UPLOAD_POOL_SIZE

I recommend you to do that in the Application class. If you set this value to 1, you will have only one upload task at a time.

Idle timeout

To preserve battery as much as possible, when the upload service finishes all the upload tasks, it immediately stops foreground execution, and remains sitting in the background waiting for new tasks to come, until the idle timeout is reached. By default, the service will auto shutdown after 10s of inactivity, but you can tune that value (starting from 3.3) with:

UploadService.IDLE_TIMEOUT = 10 * 1000; //the value has to be expressed in milliseconds

I recommend you to do that in the Application class

Upload tasks and service management

You can call those methods from anywhere you want in your code.

Method Description
UploadService.getTaskList() Gets all the currently active upload tasks
UploadService.stopAllUploads(); Stops all the active upload tasks. After that each upload task is stopped, your broadcast receiver's or delegate's onCancelled method will be called.
UploadService.stopUpload(uploadId); Stops a specific upload task. The broadcast receiver's or or delegate's onCancelled method will be called
UploadService.stop(context); Stops UploadService if no tasks are currently running. It returns a boolean which indicates if the service is shutting down.
UploadService.stop(context, true); Force stops UploadService aborting any currently running tasks. It returns a boolean which indicates if the service is shutting down.

Cookies

Cookies management may be needed for uploads depending on your server, but it's out of scope in this library. Refer to Android Cookie Store which provides a persistent cookie storage that can be used in conjunction with Android Upload Service, for both HttpURLConnection and OkHttp stacks.

Logging

By default the library logging is disabled. Starting from release 2.1+, you can enable debug log by invoking:

Logger.setLogLevel(LogLevel.DEBUG);

You can adjust the level of detail from DEBUG to OFF.

The library logger uses android.util.Log by default, so you will get the output in LogCat. If you want to redirect logs to different output or use a different logger, you can provide your own delegate implementation like the following. I recommend you to do that in the Application class

Logger.setLoggerDelegate(new Logger.LoggerDelegate() {
    @Override
    public void error(String tag, String message) {
        //your own implementation here
    }

    @Override
    public void error(String tag, String message, Throwable exception) {
        //your own implementation here
    }

    @Override
    public void debug(String tag, String message) {
        //your own implementation here
    }

    @Override
    public void info(String tag, String message) {
        //your own implementation here
    }
});