IviService
An annotation required for IVI service interfaces.
The annotation processor will generate various interfaces and classes for each interface annotated with this annotation, namely:
<Interface>Api
: Frontend interface for the IVI service.<Interface>Base
: Abstract base class for the IVI service implementation.Various interfaces and classes to achieve the communication between the
<Interface>Api
and the IVI service implementation.
IVI service interfaces can contain:
Inner data classes. For each inner data class the serialization and deserialization will be provided.
Properties. Each property is mirrored by the
<Interface>Api
class as aLiveData<T>
property, whereT
is the type of the property being mirrored. For more information onLiveData
see the Android documentation
All properties mirrored by the <Interface>Api
are read-only.
Properties can be annotated with the IviSetting annotation. This exposes the property as a setting. See IviSetting for details.
Functions. When a function is annotated with the IviServiceFun annotation, the function is exposed in the
<Interface>Api
class. Each function is exposed in the<Interface>Api
class in two variants:
Asynchronous, like
fun <name>Async(..., onResult = null)
. WhereonResult
, when notnull
, is invoked when the service function execution completes with the result or when the execution fails.A suspendable function, like
suspend fun co<Name>(...): <ReturnType>
. This allows the function to be called from a Kotlin coroutine. Theco<Name>
function throws an exception when the execution fails.
Event listener interfaces. This is provided by means of annotating an inner interface with the IviServiceEventListener annotation. Each function in this class is an event callback. Each event callback is propagated to event listeners registered to the
<Interface>Api
class.
Note: The annotated inner interface class name needs to have the 'Listener' suffix and all event callback member functions need to be prefixed with on
. The annotation processor will fail otherwise.
To support inter-process communication between the IVI service and the <Interface>Api
, all used types in the IVI service interface need to be supported by the android.os.Parcel
or the androidx.versionedparcelable.VersionedParcelable
classes, with the following additions:
Maps can use any supported type as the key type. So it is not limited to String.
Classes inheriting
android.os.Parcelable
can be optionally annotated with the@Parcelize
annotation.Set collections. Do not use them when the ordering of the collection items is important.
Enum types. All enum types defined in the same packages as the IVI service interface are automatically registered. This includes inner enum types. All other imported enum types need to be registered as part of the IviService annotation.
MirrorableMap
. Only the changes to the map are sent to all clients.IviDataSource
. An IVI data source can be used to expose a data set to clients without requiring the full data set to be in memory. It also allows data to be queried and sorted at the service side.
The generated code will assume that any imported type implements the android.os.Parcelable
or the androidx.versionedparcelable.VersionedParcelable
interface. A strong preference should be given towards regular Parcelable
, as the use of VersionedParcelable
is much slower.
Custom types may require code to opt-in to experimental API if the type is annotated with a RequiresOptIn annotation. The service interface must forward the opt-in requirement to its clients by annotating properties, functions or the interface with the same annotation. Do not use OptIn in this case. The only valid use case for an OptIn annotation in a service interface is when the interface uses an experimental IVI service annotation.
For consistent naming, an interface that is annotated with the IviService annotation must have a name that has the Service
suffix. It also needs to have a companion object
, which can be empty. The companion object
is required to allow extension functions, such as <Interface>.createApi()
, to be generated.
The service interface, properties, functions, event listener interfaces and event listener interface functions can be annotated with annotation types that are annotated with RequiresOptIn
. Any other custom annotation types are not supported.
Example
@IviService(
serviceId = "org.example.services.foo"
)
interface FooService {
@IviServiceEventListener
interface GroupedEventsListener {
fun onSomethingHappened()
fun onSomethingHappenedWithArguments(int: Int, data: Data)
}
@IviServiceEventListener
interface SingleEventListener {
fun onEpoch()
}
@Parcelize
data class Data(val foo: Int, val bar: String) : Parcelable
val intProp: Int
val dataProp: Data
@IviServiceFun
suspend fun doSomethingWithReturnValue(): Int
@IviServiceFun
suspend fun returnSomeData(): Data
@IviServiceFun
suspend fun doSomethingWithoutReturnValue()
@IviServiceFun
suspend fun testDefaultArgument(argument: Int = 42)
companion object
}
The <Interface>Api
class in the example has the following public members:
class FooService<Api> {
val serviceAvailable: LiveData<Boolean>
val intProp: LiveData<Int>
val dataProp: LiveData<FooService.Data>
fun doSomethingWithReturnValueAsync(
onResult: ((result: IviServiceFunResult<Int?>) -> Unit)? = null
)
suspend fun coDoSomethingWithReturnValue(): Int
fun returnSomeDataAsync(
argument: Int,
onResult: ((result: IviServiceFunResult<ExampleService.Data?>) -> Unit)? = null
)
suspend fun coReturnSomeData(argument: Int): ExampleService.Data
fun doSomethingWithoutReturnValueAsync(
onResult: ((result: IviServiceFunResult<Unit?>) -> Unit)? = null
)
suspend fun coDoSomethingWithoutReturnValue() {
fun testDefaultArgumentAsync(
argument: Int,
onResult: ((result: IviServiceFunResult<Unit?>) -> Unit)? = null
)
suspend fun coTestDefaultArgument(argument: Int)
fun testDefaultArgumentAsync(
onResult: ((result: IviServiceFunResult<Unit?>) -> Unit)? = null
)
suspend fun coTestDefaultArgument()
fun addGroupedEventListener(
lifecycleOwner: LifecycleOwner,
listener: FooService.GroupedEventsListener
)
fun removeGroupedEventListener(listener: FooService.GroupedEventsListener)
fun addSingleEventListener(
lifecycleOwner: LifecycleOwner,
listener: FooService.SingleEventListener
)
fun removeSingleEventListener(listener: FooService.SingleEventListener)
}
The <Interface>Api
class instance can be created by calling the <Interface>.createApi()
extension function. The service can be optionally registered in the application; then <Interface>.createApiOrNull()
can be used instead.