-
-
Notifications
You must be signed in to change notification settings - Fork 693
Usage
- HTTP multipart/form-data upload
- HTTP binary upload
- FTP Upload
- What is the upload ID?
- Notification configuration
- Tune exponential backoff algorithm
- Tune the number of parallel uploads
- Tune service idle timeout
- Upload tasks and service management
- Cookies
- Logging
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
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.
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.
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
):
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
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 byUploadService.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 byUploadService.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.
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.
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
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 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.
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
}
});