Skip to content

OData Asynchronous APIs

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

Most OData APIs make network requests to interact with the backend service. Out of the box OData library provides a collection of asynchronous APIs with name ending in "Async" in DataService or generated OData proxy and service classes. The APIs use Android AsyncTask internally to perform network requests. Please refer Android documentation here for further details on AsyncTask. The OData Async 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 entity returned is passed in as a parameter to handler. Failure handler with the relevant exception as parameter is invoked on error. 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 generated proxy classes for a complete list of the asynchronous APIs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Proxy Class
eventService.getEventsAsync(null,
    (events) -> {
        // Executed on main Thread
        // We have a list of events
        for (Event event: events) {
            ...
        }
        // use events to update UI controls
        ...
    },
    (error) -> {
        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
);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Proxy Class
eventService.getEventsAsync(null,
    { events -> 
        // Execute 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);
val events = EntityValueList()
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 its own.

Aysnchronous invocations can be cancelled through the use of a cancel token. 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:

  1. Processing
  2. online - request is in-flight i.e. server has received and is processing it
  3. offline - request is being processed against local store
  4. Queued in Dispatcher
  5. online - request is queued by HTTP handler whenever maximum concurrent request limit is reached
  6. offline - N/A
  7. Queued in AsyncTask executor - request is queued by executor until a thread is available

The outcome of cancellation depends on which state the request is in. In general, request in processing state is usually not cancellable. While it may be possible to kill off an online request pending server response, server will continue processing until it is completed. Queued request will be cancelled either immediately or when it starts. Cancelled request will invoke the failure handler with relevant excpetion.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// No difference between proxy class and dynamic API
// All require a success and failure handler as parameter
CancelToken token = new CancelToken();
RequestOptions options = new RequestOptions();
options.setCancelToken(token);

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

// Cancel asynchronous query
token.cancel();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// No difference between proxy class and dynamic API
// All require a success and failure handler as parameter
val token = CancelToken()
val options = RequestOptions()
options.setCancelToken(token)

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

// Cancel asynchronous query
token.cancel()