{"id":45712,"date":"2023-10-04T07:32:31","date_gmt":"2023-10-04T05:32:31","guid":{"rendered":"https:\/\/www.inovex.de\/?p=45712"},"modified":"2023-10-05T19:17:53","modified_gmt":"2023-10-05T17:17:53","slug":"concurrent-kotlin-apis-in-android-services","status":"publish","type":"post","link":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/","title":{"rendered":"Using Kotlin APIs for Android Services with AIDLs"},"content":{"rendered":"<p>How can we design modern and concurrent Kotlin APIs for our Android-bound service which come with its Binder and AIDL constraints?<!--more--><\/p>\n<p>When implementing services in Android it often comes to the point where we need to create an API for other apps and processes which consume our service. This is often the case in Android embedded or automotive projects, whenever we have hardware abstractions or provide headless functionality. But also in regular app development, we might want to provide an interface for other apps to consume.<\/p>\n<p>The practical approach to implementing an API across applications with potentially multiple consumers calling concurrently is an Android-bound<a href=\"https:\/\/developer.android.com\/guide\/components\/bound-services\" target=\"_blank\" rel=\"noopener\">\u00a0service<\/a> with an <a href=\"https:\/\/developer.android.com\/guide\/components\/aidl\" target=\"_blank\" rel=\"noopener\">AIDL<\/a> interface. However, this RPC-style interface bears a couple of challenges that need to be considered when implementing our API. The most important ones are:<\/p>\n<ul>\n<li>You can only pass <strong>Primitives<\/strong>, <strong>Bundles<\/strong> (Parcelable objects), or <strong>IInterfaces<\/strong> in your AIDL interface<\/li>\n<li>You should avoid long-running (blocking) calls to prevent running out of Binder threads in your service<\/li>\n<\/ul>\n<p>This is especially important when we want to wrap this into a nice Kotlin API with suspending calls and flows. Let me show you how to make these concurrent calls possible. But first, we need an example use case to work on.<\/p>\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-custom ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\"><p class=\"ez-toc-title\" style=\"cursor:inherit\"><\/p>\n<\/div><nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#Building-a-demo\" >Building a demo<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#Trigger-calls-across-Binder\" >Trigger calls across Binder<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#Suspending-calls-across-Binder\" >Suspending calls across Binder<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#Flow-across-Binder\" >Flow across Binder<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#Service-Connection-with-Kotlin-Flows\" >Service Connection with Kotlin Flows<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#Conclusion\" >Conclusion<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"Building-a-demo\"><\/span>Building a demo<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Let&#8217;s imagine our app being a general-purpose chatbot AI you can chat with in a standalone application. In addition, our app provides an API with a lib for third-party apps to offer our service to their customers, like for example in an E-Commerce app or a travel booking app to ask the bot something in their context. For this, I implemented a basic demo with the following architecture:<\/p>\n<p><a href=\"https:\/\/www.inovex.de\/wp-content\/uploads\/Untitled-Diagram2.drawio.svg\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-45713\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/Untitled-Diagram2.drawio.svg\" alt=\"Shows the basic architecture of the demo application with three gradle modules, :service, :lib and :demo.\" width=\"682\" height=\"482\" \/><\/a>We have these three modules in our demo:<\/p>\n<ul>\n<li>:<strong>service<\/strong>\u00a0\u2013 which contains the Service Application with UI and the BotService to provide the functionality to others<\/li>\n<li>:<strong>lib<\/strong>\u00a0\u2013 helps the client side for easy connection to the BotService and defines the common API<\/li>\n<li>:<strong>client<\/strong>\u00a0\u2013 contains the Client Application with UI and utilizes the lib to connect to the Service Application<\/li>\n<\/ul>\n<p>You can find the example in our <a href=\"https:\/\/github.com\/inovex\/android-service-kotlin-api-demo\" target=\"_blank\" rel=\"noopener\">GitHub repository<\/a>. For demonstration purposes, I basically put all messages into a datastore and the bot answers a predefined answer after two seconds. Both apps look alike, except for a headline for easy identification and the client application has the ability to clear the whole chat:<\/p>\n<figure id=\"attachment_46867\" aria-describedby=\"caption-attachment-46867\" style=\"width: 973px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-46867 size-full\" src=\"https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1.png\" alt=\"Shows both apps for our demo, both with a basic chat dialog containing the same messages.\" width=\"973\" height=\"1080\" srcset=\"https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1.png 973w, https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1-270x300.png 270w, https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1-923x1024.png 923w, https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1-768x852.png 768w, https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1-400x444.png 400w, https:\/\/www.inovex.de\/wp-content\/uploads\/demo-screenshot-1-360x400.png 360w\" sizes=\"auto, (max-width: 973px) 100vw, 973px\" \/><figcaption id=\"caption-attachment-46867\" class=\"wp-caption-text\">Shows both apps for our demo, both with a basic chat dialog containing the same messages.<\/figcaption><\/figure>\n<p>For the API let&#8217;s say we can start a new chat session, query the bot details, send a message and observe incoming messages. To make that comfortable to use we decide upon the following Kotlin API with suspending calls and Flows:<\/p>\n<pre class=\"lang:kotlin decode:true \" title=\"Client facing Kotlin API to use our Service.\">interface IBot {\r\n    \/**\r\n     * Start a new chat session.\r\n     *\/\r\n    fun newSession()\r\n\r\n    \/**\r\n     * Send a message to the bot service.\r\n     *\/\r\n    fun sendMessage(message: Message)\r\n\r\n    \/**\r\n     * Asynchronous get of bot details\r\n     *\/\r\n    suspend fun getBotDetails(): BotDetails\r\n\r\n    \/**\r\n     * Asynchronous get of messages as flow.\r\n     *\/\r\n    fun getMessages(): Flow&lt;List&lt;Message&gt;&gt;\r\n}<\/pre>\n<p>Unfortunately, these concurrent Kotlin features are not available in the Android Interface Definition Language (AIDL), so how can we do that across AIDL? Let&#8217;s take a look at each method at a time.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Trigger-calls-across-Binder\"><\/span>Trigger calls across Binder<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>A regular function like newSession() or sendMessage() without a result can basically be declared and called in a coroutine, since we do not need to wait for any result to return. This ensures that the binder thread of the service will not be blocked. The only thing required here is that Message needs to implement Parcelable, which can be easily done with the <a href=\"https:\/\/developer.android.com\/kotlin\/parcelize\" target=\"_blank\" rel=\"noopener\">parcelize gradle plugin<\/a>.<\/p>\n<pre class=\"lang:AIDL decode:true\" title=\"in IBotService.aidl\">void sendMessage(in Message message);<\/pre>\n<pre class=\"lang:kotlin decode:true \" title=\"Client Bot.kt Interface implementation\">fun sendMessage(message: Message) = service.newSession(message)<\/pre>\n<pre class=\"lang:kotlin decode:true \" title=\"Service BotService.kt implementation\">override fun sendMessage(message: Message) {\r\n\tcoroutineScope.launch {\r\n    \t\tlocalBotRepository.sendMessage(message)\r\n\t}\r\n}\r\n<\/pre>\n<p>Sidenote: Using the oneway declaration here does not solve the binder thread problem and may introduce race conditions when calling these methods directly after another, since they may be executed in a different order.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Suspending-calls-across-Binder\"><\/span>Suspending calls across Binder<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Concurrent calls which return only one result can be implemented with binder callbacks. For getBotDetails() we introduced a Parcelable data class BotDetails and an additional callback aidl, BotDetailsCallback.aidl. With suspendCoroutine we can nicely cover that callback mechanism in a clean suspending function.<\/p>\n<pre class=\"lang:AIDL decode:true \" title=\"BotDetailsCallback.aidl implementation\">\/**\r\n* Callback inteface which passes the BotDetails to the consumer.\r\n*\/\r\ninterface BotDetailsCallback {\r\n    oneway void valueChanged(in BotDetails botDetails);\r\n}\r\n<\/pre>\n<pre class=\"lang:AIDL decode:true \" title=\"in IBotService.aidl\">void getBotDetails(in BotDetailsCallback callback);<\/pre>\n<pre class=\"lang:kotlin decode:true\" title=\"Client Bot.kt Interface implementation\">override suspend fun getBotDetails() = suspendCoroutine { continuation -&gt;\r\n\tservice.getBotDetails(object : BotDetailsCallback.Stub() {\r\n    \t\toverride fun valueChanged(botDetails: BotDetails) {\r\n        \t\t\tcontinuation.resume(botDetails)\r\n    \t\t}\r\n\t})\r\n}\r\n<\/pre>\n<pre class=\"lang:kotlin decode:true\" title=\"Service BotService.kt implementation\">override fun getBotDetails(callback: BotDetailsCallback) {\r\n\tcoroutineScope.launch {\r\n    \t\tcallback.valueChanged(localBotRepository.getBotDetails())\r\n\t}\r\n}\r\n<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"Flow-across-Binder\"><\/span>Flow across Binder<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To pass a continuous flow of elements across the Binder interface, like in getMessages(), we use the same approach as for suspending calls, but now with registering and unregistering calls in our AIDL. We also define a Parcelable class Message and its callback Interface, MessagesCallback.aidl. As for suspending functions, we can hide these callback mechanics with a callbackFlow Flow builder. And with awaitClose we ensure a clean unregistration when the flow is not active anymore.<\/p>\n<pre class=\"lang:AIDL decode:true \" title=\"in IBotService.aidl\">void registerForMessages(in MessagesCallback callback);\r\nvoid unregisterForMessages(in MessagesCallback callback);\r\n<\/pre>\n<pre class=\"lang:kotlin decode:true \" title=\"Client Bot.kt Interface implementation\">override fun getMessages() = callbackFlow {\r\n\tval callback = object : MessagesCallback.Stub() {\r\n    \t\toverride fun valueChanged(messages: Array) {\r\n        \t\ttrySend(messages.asList())\r\n\t    \t}\r\n\t}\r\n\tservice.registerForMessages(callback)\r\n\tawaitClose {\r\n    \t\tservice.unregisterForMessages(callback)\r\n\t}\r\n}\r\n<\/pre>\n<pre class=\"lang:kotlin decode:true \" title=\"Service BotService.kt implementation\">override fun registerForMessages(callback: MessagesCallback) {\r\n\tcoroutineScope.launch {\r\n    \t\tmessagesCallbackList.registerAndCollect(callback)\r\n\t}\r\n}\r\n\r\noverride fun unregisterForMessages(callback: MessagesCallback) {\r\n\tcoroutineScope.launch {\r\n    \t\tmessagesCallbackList.unregisterAndCancel(callback)\r\n\t}\r\n}\r\n<\/pre>\n<p>As you can see in the service implementation we have not yet published any data into the callbacks, but instead, we use a <a href=\"https:\/\/developer.android.com\/reference\/android\/os\/RemoteCallbackList\" target=\"_blank\" rel=\"noopener\">RemoteCallbackList<\/a> to handle the \u201cgrunt work of maintaining a list of remote interfaces\u201c. It&#8217;s worth taking a look at that documentation to understand the underlying work. But in short, there are a couple of edge cases, like dying clients, which need to be handled to not send values to already dead bindings.<\/p>\n<p>In this implementation messagesCallbackList is a self-implemented extension of RemoteCallbackList called SubscribingRemoteCallbackList which also keeps track of collecting from a given flow and broadcasting the data to all registered callbacks. The declaration looks like this:<\/p>\n<pre class=\"lang:kotlin decode:true \" title=\"Service BotService.kt Implementation\">private val messagesCallbackList =\r\n\tSubscribingRemoteCallbackList&lt;MessagesCallback, List&gt;(\r\n    \t\tlocalBotRepository.getMessages(),\r\n    \t\tcoroutineScope\r\n\t) { callback, value -&gt; callback.valueChanged(value.toTypedArray()) }\r\n<\/pre>\n<p>And <a href=\"https:\/\/github.com\/inovex\/android-service-kotlin-api-demo\/blob\/main\/service\/src\/main\/java\/de\/inovex\/blog\/aidldemo\/chatbot\/service\/SubscribingRemoteCallbackList.kt\" target=\"_blank\" rel=\"noopener\">here<\/a> you can find the implementation of it. This ensures that the given flow, getMessages() in our case, is only hot when one or more callbacks are registered.<\/p>\n<p>One side note for all the shown examples above: Be aware that we still need to cover and expect RemoteExceptions on every single Binder call!<\/p>\n<h2><span class=\"ez-toc-section\" id=\"Service-Connection-with-Kotlin-Flows\"><\/span>Service Connection with Kotlin Flows<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Although one typically has <a href=\"https:\/\/github.com\/inovex\/blog-android-service-kotlin-api-demo\/blob\/main\/lib\/src\/main\/java\/de\/inovex\/blog\/aidldemo\/chatbot\/lib\/BotManager.kt\" target=\"_blank\" rel=\"noopener\">Manager classes<\/a> in place to provide an easy interface for service connections, it sometimes is tricky to handle all potential edge cases, like occasionally connection loss and automatic Binder re-connections. Also, we want to avoid new service connections on every single call and we want to have something like a re-connection mechanic when a binder connection fails due to flaky HAL implementations. To tackle all these requirements, I often suggest the following pattern:<\/p>\n<pre class=\"lang:kotlin decode:true\" title=\"RemoteBotRepository.kt implementation of service connection\">private val sharedConnectedBot = callbackFlow {\r\n    BotManager.connect(context, object : BotManager.BotServiceCallback {\r\n        override fun onConnected(service: IBotService) {\r\n            trySend(Bot(service))\r\n        }\r\n\r\n        override fun onConnectionLost() {\r\n            cancel(\"connection to bot service lost\")\r\n        }\r\n\r\n        override fun onConnectionFailed(e: Exception) {\r\n            cancel(\"connection to bot service failed\", e)\r\n        }\r\n    })\r\n    awaitClose {\r\n        BotManager.disconnect(context)\r\n    }\r\n}.retry(3) {\r\n    delay(1_000L)\r\n    return@retry true\r\n}.catch { \r\n    emit(null) \r\n}.shareIn(\r\n    coroutineScope, SharingStarted.WhileSubscribed(10_000L, 0), 1\r\n)\r\n<\/pre>\n<p>You can find the full repository implementation and how to use it <a href=\"https:\/\/github.com\/inovex\/blog-android-service-kotlin-api-demo\/blob\/main\/client\/src\/main\/java\/de\/inovex\/blog\/aidldemo\/chatbot\/client\/RemoteBotRepository.kt\" target=\"_blank\" rel=\"noopener\">here<\/a>.<\/p>\n<p>Let&#8217;s go through this snipped step by step:<\/p>\n<ol>\n<li>We initialize a flow with <strong>callbackFlow<\/strong> to connect to the service using the manager.<\/li>\n<li>We pass the interface instance, or a success indicator (depending on the manager implementation) on a successful connection.<br \/>\nAnd raise exceptions or cancel the flow when the connection failed or got lost.<\/li>\n<li>With <strong>awaitClose<\/strong> we ensure that the service connection is closed whenever the flow has completed.<\/li>\n<li>With <strong>retry(3)<\/strong> we want to wait and retry the service connection on failure three times.<\/li>\n<li>Even if the retry failed three times we need to handle and materialize the error case with <strong>catch<\/strong>, since we <a href=\"https:\/\/kotlinlang.org\/api\/kotlinx.coroutines\/kotlinx-coroutines-core\/kotlinx.coroutines.flow\/-shared-flow\/\" target=\"_blank\" rel=\"noopener\">cannot pass error cases in shared flows<\/a>.<\/li>\n<li>We convert the flow into a <strong>sharedFlow<\/strong> with the following characteristics:\n<ul>\n<li>The flow is shared with the <strong>WhileSubscribed <\/strong>strategy: While we have consumers, we want to keep the connection alive and shared.<\/li>\n<li>Even when the last subscriber has unsubscribed, it keeps it alive for 10 more seconds, to avoid re-connections on every single call.<\/li>\n<li>Replay is not cached to avoid calls on unconnected instances.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n<p>There might be one edge case in this pattern when a connection is lost in between and cannot be reestablished by retry. Then it might happen that AIDL calls are made on an unconnected instance during the retry, which needs to be cached. Nevertheless, this pattern could be a resilient starting point for your service connection.<\/p>\n<p>In combination with the getMessages() flow we now can have a flow, which establishes a connection, subscribes to messages via AIDL, and is capable of surviving service re-connections under the hood:<\/p>\n<pre class=\"lang:kotlin decode:true\" title=\"RemoteBotRepository.kt implementation of getMessages()\">fun getMessages(): Flow&lt;List&gt; =\r\n    sharedConnectedBot.flatMapLatest { it?.getMessages() ?: emptyFlow() }<\/pre>\n<h2><span class=\"ez-toc-section\" id=\"Conclusion\"><\/span>Conclusion<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>With basic usage of Kotlin builders like <strong>suspendedCoroutine<\/strong> and <strong>callbackFlow<\/strong> we are able to map AIDLs legacy callback APIs into modern and clean Kotlin APIs. We can also streamline and simplify the use of such APIs with <strong>sharedFlow <\/strong>and basic flow mechanics.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How can we design modern and concurrent Kotlin APIs for our Android-bound service which come with its Binder and AIDL constraints?<\/p>\n","protected":false},"author":46,"featured_media":47391,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"ep_exclude_from_search":false,"footnotes":""},"tags":[149,510,724],"service":[420],"coauthors":[{"id":46,"display_name":"Steffen Tr\u00f6ster","user_nicename":"stroester"}],"class_list":["post-45712","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","tag-android","tag-apps-2","tag-kotlin","service-apps"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Using Kotlin APIs for Android Services with AIDLs - inovex GmbH<\/title>\n<meta name=\"description\" content=\"How can we design modern and concurrent Kotlin APIs for our Android bound service which come with its Binder and AIDL limitations?\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using Kotlin APIs for Android Services with AIDLs - inovex GmbH\" \/>\n<meta property=\"og:description\" content=\"How can we design modern and concurrent Kotlin APIs for our Android bound service which come with its Binder and AIDL limitations?\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/\" \/>\n<meta property=\"og:site_name\" content=\"inovex GmbH\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/inovexde\" \/>\n<meta property=\"article:published_time\" content=\"2023-10-04T05:32:31+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-10-05T17:17:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1920\" \/>\n\t<meta property=\"og:image:height\" content=\"1080\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Steffen Tr\u00f6ster\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls-1024x576.png\" \/>\n<meta name=\"twitter:creator\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:site\" content=\"@inovexgmbh\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Steffen Tr\u00f6ster\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"8\u00a0Minuten\" \/>\n\t<meta name=\"twitter:label3\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data3\" content=\"Steffen Tr\u00f6ster\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/\"},\"author\":{\"name\":\"Steffen Tr\u00f6ster\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/a17457870d27e043851b5174001fc519\"},\"headline\":\"Using Kotlin APIs for Android Services with AIDLs\",\"datePublished\":\"2023-10-04T05:32:31+00:00\",\"dateModified\":\"2023-10-05T17:17:53+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/\"},\"wordCount\":1289,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/artikelbild-kotlin-apis-android-services-aidls.png\",\"keywords\":[\"Android\",\"Apps\",\"Kotlin\"],\"articleSection\":[\"English Content\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/\",\"name\":\"Using Kotlin APIs for Android Services with AIDLs - inovex GmbH\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/artikelbild-kotlin-apis-android-services-aidls.png\",\"datePublished\":\"2023-10-04T05:32:31+00:00\",\"dateModified\":\"2023-10-05T17:17:53+00:00\",\"description\":\"How can we design modern and concurrent Kotlin APIs for our Android bound service which come with its Binder and AIDL limitations?\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/artikelbild-kotlin-apis-android-services-aidls.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/artikelbild-kotlin-apis-android-services-aidls.png\",\"width\":1920,\"height\":1080,\"caption\":\"header artikelbild illustration blog\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/concurrent-kotlin-apis-in-android-services\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Using Kotlin APIs for Android Services with AIDLs\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#website\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"name\":\"inovex GmbH\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#organization\",\"name\":\"inovex GmbH\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"contentUrl\":\"https:\\\/\\\/www.inovex.de\\\/wp-content\\\/uploads\\\/2021\\\/03\\\/inovex-logo-16-9-1.png\",\"width\":1921,\"height\":1081,\"caption\":\"inovex GmbH\"},\"image\":{\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/inovexde\",\"https:\\\/\\\/x.com\\\/inovexgmbh\",\"https:\\\/\\\/www.instagram.com\\\/inovexlife\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/inovex\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UC7r66GT14hROB_RQsQBAQUQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/#\\\/schema\\\/person\\\/a17457870d27e043851b5174001fc519\",\"name\":\"Steffen Tr\u00f6ster\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/281b95b612ce597f33c96f1d0f16daf5b71bd06a6b4a5df5043ca540756fecaa?s=96&d=retro&r=g30d54dbb594c7c7f7b535c0ea049d804\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/281b95b612ce597f33c96f1d0f16daf5b71bd06a6b4a5df5043ca540756fecaa?s=96&d=retro&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/281b95b612ce597f33c96f1d0f16daf5b71bd06a6b4a5df5043ca540756fecaa?s=96&d=retro&r=g\",\"caption\":\"Steffen Tr\u00f6ster\"},\"url\":\"https:\\\/\\\/www.inovex.de\\\/de\\\/blog\\\/author\\\/stroester\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Using Kotlin APIs for Android Services with AIDLs - inovex GmbH","description":"How can we design modern and concurrent Kotlin APIs for our Android bound service which come with its Binder and AIDL limitations?","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/","og_locale":"de_DE","og_type":"article","og_title":"Using Kotlin APIs for Android Services with AIDLs - inovex GmbH","og_description":"How can we design modern and concurrent Kotlin APIs for our Android bound service which come with its Binder and AIDL limitations?","og_url":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/","og_site_name":"inovex GmbH","article_publisher":"https:\/\/www.facebook.com\/inovexde","article_published_time":"2023-10-04T05:32:31+00:00","article_modified_time":"2023-10-05T17:17:53+00:00","og_image":[{"width":1920,"height":1080,"url":"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls.png","type":"image\/png"}],"author":"Steffen Tr\u00f6ster","twitter_card":"summary_large_image","twitter_image":"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls-1024x576.png","twitter_creator":"@inovexgmbh","twitter_site":"@inovexgmbh","twitter_misc":{"Verfasst von":"Steffen Tr\u00f6ster","Gesch\u00e4tzte Lesezeit":"8\u00a0Minuten","Written by":"Steffen Tr\u00f6ster"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#article","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/"},"author":{"name":"Steffen Tr\u00f6ster","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/a17457870d27e043851b5174001fc519"},"headline":"Using Kotlin APIs for Android Services with AIDLs","datePublished":"2023-10-04T05:32:31+00:00","dateModified":"2023-10-05T17:17:53+00:00","mainEntityOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/"},"wordCount":1289,"commentCount":0,"publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls.png","keywords":["Android","Apps","Kotlin"],"articleSection":["English Content"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/","url":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/","name":"Using Kotlin APIs for Android Services with AIDLs - inovex GmbH","isPartOf":{"@id":"https:\/\/www.inovex.de\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#primaryimage"},"image":{"@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#primaryimage"},"thumbnailUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls.png","datePublished":"2023-10-04T05:32:31+00:00","dateModified":"2023-10-05T17:17:53+00:00","description":"How can we design modern and concurrent Kotlin APIs for our Android bound service which come with its Binder and AIDL limitations?","breadcrumb":{"@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#primaryimage","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/artikelbild-kotlin-apis-android-services-aidls.png","width":1920,"height":1080,"caption":"header artikelbild illustration blog"},{"@type":"BreadcrumbList","@id":"https:\/\/www.inovex.de\/de\/blog\/concurrent-kotlin-apis-in-android-services\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.inovex.de\/de\/"},{"@type":"ListItem","position":2,"name":"Using Kotlin APIs for Android Services with AIDLs"}]},{"@type":"WebSite","@id":"https:\/\/www.inovex.de\/de\/#website","url":"https:\/\/www.inovex.de\/de\/","name":"inovex GmbH","description":"","publisher":{"@id":"https:\/\/www.inovex.de\/de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.inovex.de\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/www.inovex.de\/de\/#organization","name":"inovex GmbH","url":"https:\/\/www.inovex.de\/de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/","url":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","contentUrl":"https:\/\/www.inovex.de\/wp-content\/uploads\/2021\/03\/inovex-logo-16-9-1.png","width":1921,"height":1081,"caption":"inovex GmbH"},"image":{"@id":"https:\/\/www.inovex.de\/de\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/inovexde","https:\/\/x.com\/inovexgmbh","https:\/\/www.instagram.com\/inovexlife\/","https:\/\/www.linkedin.com\/company\/inovex","https:\/\/www.youtube.com\/channel\/UC7r66GT14hROB_RQsQBAQUQ"]},{"@type":"Person","@id":"https:\/\/www.inovex.de\/de\/#\/schema\/person\/a17457870d27e043851b5174001fc519","name":"Steffen Tr\u00f6ster","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/281b95b612ce597f33c96f1d0f16daf5b71bd06a6b4a5df5043ca540756fecaa?s=96&d=retro&r=g30d54dbb594c7c7f7b535c0ea049d804","url":"https:\/\/secure.gravatar.com\/avatar\/281b95b612ce597f33c96f1d0f16daf5b71bd06a6b4a5df5043ca540756fecaa?s=96&d=retro&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/281b95b612ce597f33c96f1d0f16daf5b71bd06a6b4a5df5043ca540756fecaa?s=96&d=retro&r=g","caption":"Steffen Tr\u00f6ster"},"url":"https:\/\/www.inovex.de\/de\/blog\/author\/stroester\/"}]}},"_links":{"self":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/45712","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/users\/46"}],"replies":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/comments?post=45712"}],"version-history":[{"count":6,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/45712\/revisions"}],"predecessor-version":[{"id":50999,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/posts\/45712\/revisions\/50999"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media\/47391"}],"wp:attachment":[{"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/media?parent=45712"}],"wp:term":[{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/tags?post=45712"},{"taxonomy":"service","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/service?post=45712"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.inovex.de\/de\/wp-json\/wp\/v2\/coauthors?post=45712"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}