Skip to content

Asynchronous API

Android developers are aware that network requests cannot be made from the main (also known as UI) thread. The Android OS terminates network requests performed on the main thread and an exception is thrown. Android application developers can choose to use one of the Android-provided mechanisms, such as AsyncTask, Loader, or Service, to invoke APIs that perform network requests.

Most OData APIs make network requests to interact with the back-end service. The OData library provides a collection of asynchronous APIs with names ending in "Async" in DataService, or generated OData proxy and service classes. The APIs use Android AsyncTask internally to perform network requests. Refer to the Android documentation for further details on AsyncTask. The OData asynchronous APIs must be invoked from the main thread. By default, queries are executed in parallel on Async.THREAD_POOL_EXECUTOR, while CUD requests are executed serially on AsyncTask.SERIAL_EXECUTOR. This behavior can be changed by passing in your own executor.

If the asynchronous request is successful, the success handler will be invoked. For a query, the list of entities returned is passed in as a parameter to the handler. A failure handler is invoked on error with the relevant exception as a parameter. While the success handler for batch asynchronous API has no input parameter, the result of the batch execution can be obtained from the batch variable after completion.

Here is an example code snippet using executeQueryAsync API. Review the generated proxy classes for a complete list of the asynchronous APIs.

// Proxy Class
eventService.getEventsAsync(null,
    (events) -> {
        // Executed on main thread
        for (Event event: events) {
            ...
        }
        // Use events to update UI controls
        ...
    },
    (error) -> {
        // Executed on main thread
        logger.error("getEventAsync error: " + error.getMessage());
        // Error handling
    },
    HttpHeaders.empty
);

// Dynamic API
DataQuery query = new DataQuery().from(eventSet);
dataService.executeQueryAsync(query,
    (results) -> {
        // Executed on main thread
        for (EntityValue event: results) {
            ...
        }
        // Use events to update UI controls
        ...
    },
    (error) -> {
        // Executed on main thread
        logger.error("executeQueryAsync error: " + error.getMessage());
        // Error handling
    },
    HttpHeaders.empty
);
// Proxy Class
eventService.getEventsAsync(null,
    { events ->
        // Executed on main thread
        for (event in events) {
            ...
        }
        // Use events to update UI controls
        ...
    },
    { error ->
        // Executed on main thread
        logger.error("getEventAsync error: " + error.message)
        // Error handling
    },
    HttpHeaders.empty
)

// Dynamic API
val query = DataQuery().from(eventSet)
dataService.executeQueryAsync(query,
    { results ->
        // Executed on main thread
        for (event in results) {
            ...
        }
        // Use events to update UI controls
        ...
    },
    { error ->
        // Executed on main thread
        logger.error("executeQueryAsync error: " + error.message)
        // Error handling
    },
    HttpHeaders.empty
)

Note

Asynchronous APIs are not life-cycle aware and will not handle configuration changes on their own.

Aysnchronous invocations can be canceled using a cancel token. The OData SDK will make a best-effort attempt to cancel asynchronous requests associated with the cancel token. An asynchronous request can be in one of the following states after invocation:

  • Processing
    • Online - the request is in-flight, i.e. the server has received and is processing it
    • Offline - the request is being processed against the local store
  • Queued in Dispatcher
    • Online - the request is queued by an HTTP handler whenever the maximum concurrent request limit is reached
    • Offline - N/A
  • Queued in AsyncTask executor - the request is queued by the executor until a thread is available

The outcome of the cancellation depends on which state the request is in. In general, a request in the processing state is usually not cancellable. While it may be possible to cancel an online request pending a server response, the server will continue processing until it is completed. A queued request will be canceled either immediately or when it starts. A canceled request will invoke the failure handler with the relevant exception.

A cancel token may be associated with one or multiple requests depending on cancellation granularity. It is usually not a good practice to cancel asynchronous CUD requests due to the uncertainty of the outcome.

// No difference between proxy class and dynamic API
// All require success and failure handler as parameters
CancelToken token = new CancelToken();
RequestOptions options = new RequestOptions();
options.setCancelToken(token);

// Multiple asyncrhronous requests can be associated with the same cancel token by
// passing in a RequestOptions with a cancel token during invocation
dataService.executeQueryAsync(query,
    (results) -> {
        // Completed OK with entity list in results
    },
    (error) -> {
        // Failure: com.sap.cloud.mobile.odata.RequestCancelledException
    },
    HttpHeaders.empty,
    options
);
...

// Cancel asynchronous request
token.cancel();
// No difference between proxy class and dynamic API
// All require success and failure handler as parameters
val token = CancelToken()
val options = RequestOptions()
options.setCancelToken(token)

// Multiple asyncrhronous requests can be associated with the same cancel token by
// passing in a RequestOptions with a cancel token during invocation
dataService.executeQueryAsync(query,
    { results ->
        // Completed OK with entity list in results
    },
    { error ->
        // Failure: com.sap.cloud.mobile.odata.RequestCancelledException
    },
    HttpHeaders.empty,
    options
)
...

// Cancel asynchronous query
token.cancel()

Last update: September 29, 2020