-
-
Notifications
You must be signed in to change notification settings - Fork 693
Configuration
- Notifications
- HTTP Stack
- Cookies
- Retry Policy
- Idle timeout
- Upload buffer size
- Number of parallel uploads
- Logging
- Scheme Handlers (use your custom scheme other than file and content://)
It's advised to do all the following configurations on App startup, before everything else gets started. For example, you can do it in the Application subclass, like shown in the example apps.
All the code examples are in Kotlin, but everything is 100% interoperable with Java and so you can use Android Upload Service without any concern even if your entire codebase is 100% Java.
When you create a new upload request and you start it, Upload Service asks UploadServiceConfig.notificationConfigFactory
to create a notification for it. You can set your own notificationConfigFactory
and also override the default per single upload using request.setNotificationConfig
depending on what you need.
Both UploadServiceConfig.notificationConfigFactory
and request.setNotificationConfig
have the same interface and expect you to return an UploadNotificationConfig
:
// Global default configuration for all uploads
// It's advised you do this one time and right after you have
// initialized the library in your Application
UploadServiceConfig.notificationConfigFactory = { context, uploadId ->
// your implementation
}
// Custom configuration for a single upload
// You can do this in any place you find convenient
MultipartUploadRequest(context, "https://my.secure.server")
.setNotificationConfig { context, uploadId ->
// your implementation
}
Minimal Upload Notification Config looks like this:
UploadNotificationConfig(
notificationChannelId = UploadServiceConfig.defaultNotificationChannel!!,
isRingToneEnabled = true,
progress = UploadNotificationStatusConfig(
title = "progress",
message = "some progress message"
),
success = UploadNotificationStatusConfig(
title = "success",
message = "some success message"
),
error = UploadNotificationStatusConfig(
title = "error",
message = "some error message"
),
cancelled = UploadNotificationStatusConfig(
title = "cancelled",
message = "some cancelled message"
)
)
It specifies:
-
the notification channel
on which to operate. You can safely useUploadServiceConfig.defaultNotificationChannel!!
which is guaranteed non-null after you performed Upload Service initialization. You can also use another custom notification channel, but remember to create it first and configure it, like you've done with the default one! -
isRingToneEnabled
. This affects the behaviour on only Android < API 26 and sets whether you want the notification sound. On Android API >= 26 this setting is done when you create the notification channel. -
progress
. Configuration for the notification when the upload is in progress. -
success
. Configuration for the notification when the upload has been completed successfully. -
error
. Configuration for the notification when the upload has failed. -
cancelled
. Configuration for the notification when the upload has been cancelled by the user.
UploadServiceConfig.namespace
will also be automatically used as the group ID for your notifications.
Take also a look to the default which is richer, but keep reading below to understand it fully.
Minimal:
UploadNotificationStatusConfig(
title = "progress",
message = "message"
)
Take a look to the docs in the code to discover all the available options.
You can use your own localised strings and drawable resources, but keep in mind to only use the context which is given to you in the lambda where you set the notification configuration, to prevent memory leaks and unwanted crashes!
You can make your title
and message
richer by using Placeholders which gets replaced at runtime by Upload Service with information about the upload. You can use them from the code:
UploadNotificationStatusConfig(
title = "progress: ${Placeholder.Progress}",
message = "message"
)
or add the equivalent literal in your strings.xml
<string name="progress">progress: [[PROGRESS]]</string>
and use it:
UploadNotificationStatusConfig(
title = context.getString(R.string.progress),
message = "message"
)
You can safely use many placeholders in a single string 😉
By default you can include the following placeholders in your notification title and messages, but if you need more customization, you can add your own placeholders by extending DefaultPlaceholdersProcessor or by implementing the PlaceholdersProcessor interface. Extend DefaultPlaceholdersProcessor
if you still want to use default placeholders + your own or implement PlaceholdersProcessor
if you want to use only your custom set of placeholders.
To set your custom placeholders processor:
UploadServiceConfig.placeholdersProcessor = YourCustomImplementation()
Do you think your implementation may be useful to other developers? Propose a PR!
For each UploadNotificationStatusConfig
you can specify one or more actions (Google advises a max of 3):
UploadNotificationAction(
icon = android.R.drawable.ic_menu_close_clear_cancel,
title = "Cancel",
intent = context.getCancelUploadIntent(uploadId)
)
The action has an icon (applied only on Android older than 7), a title (you can use localized strings) and a PendingIntent to be performed when the user taps on the action. The PendingIntent
can be whatever you need. There are no limitations. To ease you adding an upload cancel option which aborts the upload, you can use context.getCancelUploadIntent(uploadId)
. This is also the default set in the bundled UploadServiceConfig.notificationConfigFactory
.
By default, Android Upload Service creates one notification for each upload request you make, honoring all the configurations described in the previous paragraphs. The default behaviour may not be what you had in mind for your app, for that reason you can implement your own Notification Handler.
To make things easier for your custom implementation:
- Look at NotificationHandler for a handler which creates one notification for each task.
- Look at AbstractSingleNotificationHandler and ExampleSingleNotificationHandler for a handler which manages a single notification for all the tasks. Bear in mind AbstractSingleNotificationHandler gives you only the primitives on purpose, to allow maximum personalization and flexibility, but there's more work to do to achieve your desired result if compared to NotificationHandler.
ExampleSingleNotificationHandler
is a proof of concept to demonstrate one possible and minimal implementation and it's not meant to be production grade. There are many ways to display progress for multiple uploads in a single notification. Choosing one predefined and fully out of the box path would have limited other possible implementations. InupdateNotification
method you will get aMap<String, TaskData>
where the key is theuploadID
andTaskData
the last known information about that upload task, including the correspondingUploadNotificationStatusConfig
. It's entirely up to you to decide which settings to honor and which to ignore. Remember to callremoveTask(uploadID)
for the either failed or succeeded tasks you don't want to display anymore to free memory. No automatic garbage collection has been added to allow maximum flexibility.
To set your custom Notification Handler:
UploadServiceConfig.notificationHandlerFactory = { service ->
// instantiate your implementation
}
Interval between progress notifications in milliseconds. If the upload tasks report more frequently than this value, upload service will automatically apply throttling. Default is 3 updates per second. This is done to not pollute the main thread with too many notification updates, which may cause the device to lag.
You can change this value by setting:
UploadServiceConfig.uploadProgressNotificationIntervalMillis = 1000 / 3
By default, Android Upload Service uses system's HttpURLConnection
network stack, referred to as HurlStack
. It supports OkHttp
and has interfaces which allows to implement a custom HTTP Stack.
You don't have to do anything. This is the default configuration:
-
userAgent
: Android Upload Service User Agent -
followRedirects
: true -
useCaches
: false -
connectTimeoutMillis
: 15000 -
readTimeoutMillis
: 30000
Set it into your Application subclass, right after UploadServiceConfig.initialize
. Example:
UploadServiceConfig.httpStack = HurlStack(
userAgent = "MyCustomUserAgent",
followRedirects = true,
useCaches = false,
connectTimeoutMillis = 20000,
readTimeoutMillis = 60000
)
This is the recommended configuration.
starting from Android 4.4 KitKat, OkHttp is used internally to provide
HttpURLConnection
implementation, as reported here. The version used changes across different Android releases, so you may experience different behaviours and encounter some known bugs on older releases. To avoid having to deal with that, I suggest you to switch to theOkHttp
stack implementation, to be sure to have a consistent behaviour across all Android versions and to have the most updated version ofOkHttp
as well.
Add this to your build.gradle
:
implementation "net.gotev:uploadservice-okhttp:$uploadServiceVersion"
Then sync the project and set the following into your Application subclass, right after UploadServiceConfig.initialize
:
UploadServiceConfig.httpStack = OkHttpStack()
This will create a new OkHttpClient
with the same default settings as HurlStack
. If you're already using OkHttp
in your project, you can pass your own client instance to Upload Service like this:
UploadServiceConfig.httpStack = OkHttpStack(yourOkHttpClient)
Not finding the HTTP stack you want to use? No problem, you can use your own network stack, too. You have to implement the following interfaces and base classes:
-
HttpStack
: Connection factory which creates new requests -
HttpRequest
: HTTP request implementation -
BodyWriter
:HttpRequest
delegate which handles writing the request body payload
ServerResponse
is the Parcelable data class
which holds a response got from the server.
See HurlStack and OkHttp stack implementations as a guideline.
Use 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.
Retry mechanism is triggered when there's a communication failure to/from the server due to connectivity problems (e.g. timeout, broken pipe, server unreachable, no network connection on the device)
If your server sends a response (either success or failure), the retry mechanism will not be triggered. You will receive the response in onSuccess or onError (check the Monitoring Wiki page) depending on the status sent by the server. In case of a failure (4xx or 5xx) received from the server, it means something was wrong on your server or the way you made the request, so to safeguard both the device and the server against an unwanted flow of requests, no automatic retries are made.
By default, when an upload fails, Upload Service will try again for a max of three times before signaling an error, applying a backoff multiplier of 2 seconds between each retry. You can customize this setting with:
UploadServiceConfig.retryPolicy = RetryPolicyConfig(
initialWaitTimeSeconds = 1,
maxWaitTimeSeconds = 100,
multiplier = 2,
defaultMaxRetries = 3
)
According to what you need. It's recommended to set this configuration where you initialize the library. Refer to RetryPolicyConfig for details about each parameter.
When you use the retry policy, upload service handles the retries transparently for you, throwing an error only once after all the attempts have failed. You can see this by checking the library debug logs. Your error handler will be called only once, after all the attempts have failed, giving you the last error which occurred.
If you don't want any retry policy, set this:
UploadServiceConfig.retryPolicy = RetryPolicyConfig(
initialWaitTimeSeconds = 1,
maxWaitTimeSeconds = 1,
multiplier = 1,
defaultMaxRetries = 0
)
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 10 seconds of inactivity
, but you can tune that value with:
UploadServiceConfig.idleTimeoutSeconds = 60
Buffer size in bytes used for data transfer by the upload tasks. By default it's 4096
bytes. You can set it with:
UploadServiceConfig.bufferSizeBytes = 4096
By default, UploadService uses its own thread pool, with max threads equal to the number of processors on your device. Every upload takes a thread. So, for example, if you have a quad-core device, the number of maximum parallel uploads will be 4.
You can customize the thread pool initialization or also assign an already existing thread pool. Example:
UploadServiceConfig.threadPool = ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(), // Initial pool size
Runtime.getRuntime().availableProcessors(), // Max pool size
5.toLong(), // Keep Alive Time
TimeUnit.SECONDS,
LinkedBlockingQueue()
)
So for example if you want to serialize upload tasks (only one upload at a time):
UploadServiceConfig.threadPool = ThreadPoolExecutor(
1, // Initial pool size
1, // Max pool size
5.toLong(), // Keep Alive Time
TimeUnit.SECONDS,
LinkedBlockingQueue()
)
If you followed the getting started guide, the library logging will be enabled for debug builds and disabled for production builds.
you can also explicitly set a log level:
UploadServiceLogger.setLogLevel(UploadServiceLogger.LogLevel.Debug)
or development mode:
UploadServiceLogger.setDevelopmentMode(devModeOn = true)
In development mode, if you pass true
, the library will log at debug level, otherwise the logging will be disabled.
By default, the logger will write to your LogCat, but you can also set your own delegate:
UploadServiceLogger.setDelegate(object : UploadServiceLogger.Delegate {
override fun error(
component: String,
uploadId: String,
message: String,
exception: Throwable?
) {
TODO("Implement your own logic")
}
override fun debug(component: String, uploadId: String, message: String) {
TODO("Implement your own logic")
}
override fun info(component: String, uploadId: String, message: String) {
TODO("Implement your own logic")
}
})
So, for example, if you are logging using Timber or other library, you can send log outputs where you need.
A scheme handler allows you to implement custom scheme management and to use it when specifying the paths of the files to upload. For example FileSchemeHandler
allows you to use absolute paths which refers to your device file system:
BinaryUploadRequest(context, serverUrl = "https://my.server.com")
.setMethod("POST")
.setFileToUpload("/path/to/your/file")
.startUpload()
and ContentResolverSchemeHandler
allows you to use content://
URIs such as:
BinaryUploadRequest(context, serverUrl = "https://my.server.com")
.setMethod("POST")
.setFileToUpload("content://myprovider/12343532")
.startUpload()
Scheme Handlers work with all the supported upload request types in the same way.
To prevent issues and unwanted behaviors, it's not possible to replace existing FileSchemeHandler
and ContentResolverSchemeHandler
implementations, but you can add your own.
For example, let's say you want to implement the scheme foobar://
. Create your class FoobarSchemeHandler
which implements the methods of the SchemeHandler
interface and then register your implementation:
UploadServiceConfig.addSchemeHandler("foobar://", FoobarSchemeHandler())
You can use your scheme when specifying file paths in your uploads:
BinaryUploadRequest(context, serverUrl = "https://my.server.com")
.setMethod("POST")
.setFileToUpload("foobar://mytopapp/myfile")
.startUpload()