Dev
November 3, 2026
1 views
2 min read

Build intelligent Android apps: In-app agentic workflows

Curated by Patrick
Source: Android Developers
Build intelligent Android apps: In-app agentic workflows
Tech Daily Byte Analysis

In the latest “Build intelligent Android apps” post, senior developer‑relations engineer Jolanda Verhoef walks developers through wiring a custom backend that hosts autonomous agents using Google’s ADK. The sample defines a “Flight Booker” agent written in Python, powered by the Gemini‑3.1‑flash‑lite model, and equipped with function tools for searching and reserving flights. The agent runs inside an InMemoryRunner, streams events through Server‑Sent Events, and communicates with the Android client via the AG‑UI protocol, while UI components are described on the fly with the A2UI protocol. By delegating the heavy lifting to a cloud backend, the Jetpacker demo shows how the Android app can stay lightweight, display real‑time progress cards, and request user input only when necessary, eliminating the risk of losing state when the app is backgrounded or offline.

This move aligns Google with a broader shift toward “agentic” AI where large language models act as orchestrators of specialized tools rather than simple chatbots. Competitors such as Microsoft’s Azure OpenAI orchestration and Amazon Bedrock’s agents already expose similar capabilities, but Google’s approach differentiates itself by tightly coupling the backend protocols (AG‑UI/A2UI) with the Android ecosystem and Jetpack Compose. The use of a self‑hosted backend also sidesteps the limitations of on‑device execution, enabling complex, multi‑agent workflows—like coordinating flights, hotels, museum tickets, and restaurant reservations—without forcing developers to ship new client binaries for UI changes. By exposing a type‑safe Kotlin SDK that maps SSE payloads to composable UI events, Google lowers the barrier for Android teams to embed sophisticated, stateful AI assistants directly into their apps.

Looking ahead, the success of this framework will hinge on developer adoption of ADK and the robustness of the AG‑UI/A2UI contracts in production environments. Security and credential management for third‑party bookings remain a concern, especially when agents invoke external APIs from a shared backend. Monitoring latency and cost of Gemini‑3.1‑flash‑lite calls will be critical as workloads scale. Finally, the open‑source nature of the sample suggests Google may iterate quickly; watching for updates to the Kotlin client library, support for additional languages beyond Python, and tighter integration with Google Cloud’s AI Platform will indicate how aggressively Google intends to position this as the default stack for agentic Android experiences.

Key Takeaways

Google introduced ADK, a Python‑friendly SDK that lets Android developers define LLM‑driven agents with function tools and run them in a cloud‑hosted session.

The AG‑UI and A2UI protocols standardize bidirectional communication and dynamic UI rendering, enabling real‑time, multi‑step workflows without app updates.

By offloading orchestration to the cloud, the Jetpacker booking assistant avoids state loss on mobile devices and supports complex coordination among specialized sub‑agents.

Developers must evaluate credential safety, latency, and Gemini model costs as they adopt the framework for production‑grade, multi‑agent Android applications.

About the Source

This analysis is based on reporting by Android Developers. Here is a short excerpt for context:

Posted by Jolanda Verhoef, Senior Developer Relations Engineer, Android Developer Relations Welcome back to the blog post series "Build intelligent Android apps" where you take a basic Android app and transform it into a personalized, intelligent, and agentic experience. In our previous post you learned how to connect to the intelligence system using AppFunctions. In this post, you will learn how to build autonomous in-app agentic workflows running in the cloud. Sometimes a task is too complex for a single device session. For example, booking a complete holiday itinerary involves coordinating flight times, selecting hotel rooms, reserving museum tickets, and planning restaurant reservations. If you run this multi-step process directly on a mobile device, the app might get closed and lose your progress. Managing all these steps and API credentials on a phone also gets complicated quickly. For these long-running, multi-step workflows, you can use a custom self-hosted backend. The backend executes the booking agents in the background, while the Android app connects to the session, visualizes the progress, and requests user input only when necessary. Using a cloud-hosted agentic backend offers a few advantages: Background execution: Booking agents run autonomously in the cloud, so progress is never lost if the mobile app goes to the background or loses internet connectivity. Complex multi-agent orchestration: A coordinator agent can delegate bookings to specialized subagents and handle dependencies between them. Client-agnostic UI rendering: The backend describes the interface structure dynamically, letting you update the UI layout without releasing a new client version. The booking assistant shows all booking progress, organized by event type. With these benefits in mind, we added a Booking Assistant to Jetpacker that coordinates flights, hotels, museums, and restaurant reservations. Let's look at how we orchestrated a multi-agent system powered by the Agent Development Kit (ADK), with the Agent-User Interaction protocol(AG-UI) and Agent-to-User Interface protocol (A2UI) to send and display interactive cards natively in Jetpack Compose. Powering complex workflows with ADK agents Agent Development Kit (ADK). With ADK, you can define agents and equip them with python function tools to query databases and execute bookings. The Android app sends the current trip itinerary data to the server. The coordinator agent chooses which subagents to trigger. Each subagent provides its results to a shared session queue that streams the results back to the Android app. Here is how to define a simple agent and run it using ADK: # android/booking-server/booking_server.py # Note: ADK supports many different coding languages. For now, use the Python version as it includes support for A2UI which we'll use later in this blog post. from google.adk import Agent from google.adk.runners import InMemoryRunner from google.adk.tools import FunctionTool # Define custom tools to interact with database def search_flights(destination: str, date: str) -> list[str]: # In production, here you would query our flight database and return dynamic results return ["10:00 AM", "2:00 PM"] def reserve_flight(flight_time: str) -> str: # In production, here you would save the reservation transaction return "Reserved flight at " + flight_time # Instantiate the booking agent with specialized tools flight_agent = Agent( name="Flight Booker", model="gemini-3.1-flash-lite", instruction="Help the user search for flights and book a reservation.", tools=[ FunctionTool(search_flights), FunctionTool(reserve_flight, require_confirmation=True) ] ) # Run the agent in memory using a session ID runner = InMemoryRunner(flight_agent) async for event in runner.run_async(user_id=user_id, session_id=session_id): if event.content: print("Agent said:", event.content) When you run an agent using this setup, ADK manages the execution steps for you. It automatically tracks the conversation context, routes messages between the user and the model, and executes the registered tools when the model requests them. This allows you to focus on writing clean procedural logic while the framework handles the orchestration in the background. The ADK web interface shows how you can have a conversation with the multi-agent booking system. To connect this backend agent to our Jetpacker app, the server needs a way to stream updates in real time to the device, which is handled using the AG-UI protocol. The agent also needs a structured way to describe and update interactive components (like option selectors and seating grids) dynamically on the phone, which is where the A2UI protocol comes in. Standardizing agent-client communication with AG-UI AG-UI protocol. AG-UI is a bidirectional transport layer protocol that standardizes message types between agents and UI clients. The agent can inform the client of lifecycle events, text messages, tool calls, and state management. The client, in turn, can send user text messages, tool call results, and custom action events back to the agent. On the server side, it yields updates formatted as standard Server-Sent Events (like event: TEXT_MESSAGE_CONTENT containing the JSON delta). On Android, the Kotlin SDK listens to this stream and automatically maps the payloads to type-safe client events: // https://github.com/android/ai-samples/tree/main/jetpacker/android/feature/trip/booking_assistant/src/main/kotlin/com/example/jetpacker/feature/booking_assistant/BookingAssistantViewModel.kt import com.agui.client.agent.HttpAgent import com.agui.client.agent.HttpAgentConfig import com.agui.core.types.RunAgentInput import com.agui.core.types.UserMessage import com.agui.core.types.TextMessageStartEvent import com.agui.core.types.TextMessageContentEvent import com.agui.core.types.TextMessageEndEvent val config = HttpAgentConfig( agentId = "booking-assistant", threadId = threadId, url = "https://" ) val agent = HttpAgent(config, httpClient) // Set up the input with the session thread and user instruction val input = RunAgentInput( threadId = threadId, runId = runId, messages = listOf(UserMessage("Book a flight to Paris")) ) // Run the agent flow and collect lifecycle events agent.runAgentObservable(input) .collect { event -> when (event) { is TextMessageStartEvent -> { /* ... */ } is TextMessageContentEvent -> { /* ... */ } is TextMessageEndEvent -> { // Handle the completed message } } } You can then write a UI to render these different types of standardized AG-UI events. This will give you the prototypical "Chatbot" experience: A basic interface that lets a user chat with an assistant. Letting the agent speak UI with A2UI To solve this, use the A2UI protocol. A2UI allows agents to describe the UI components to render on the client dynamically. The client app declares a catalog of components it supports, and the server sends a JSON payload specifying the component layout and properties: { "version": "v0.9", "updateComponents": { "surfaceId": "Flight Reservation", "components": [ { "id": "flight_option_picker", "component": "InteractiveOptionPicker", "properties": { "prompt": "Select a flight time:", "options": [ "10:00 AM", "2:00 PM" ], "selectedIdx": null, "confirmBtnText": "Confirm Flight" } } ] } } The server specifies which catalog components to render along with their active property values, cleanly decoupling the client's visual implementation details from the agent's workflow state. Designing the backend UI schema To do this, use the ADK A2UI integration. Instead of manually writing prompt instructions for every component in our catalog, the A2uiSchemaManager compiles their JSON schemas and layout instructions directly into the system prompt. This ensures the model learns the exact structure and formatting rules it must follow to generate valid A2UI payloads: # android/booking-server/booking_server.py from a2ui.schema.manager import A2uiSchemaManager from a2ui.schema.constants import VERSION_0_9 from a2ui.schema.catalog import CatalogConfig from a2ui.basic_catalog.provider import BasicCatalog # Initialize A2UI Schema Manager with custom booking component catalog schema_manager = A2uiSchemaManager( version=VERSION_0_9, catalogs=[ BasicCatalog.get_config(version=VERSION_0_9), CatalogConfig.from_path( name="https://example.com/catalogs/booking_assistant/v1/catalog.json", catalog_path="booking_catalog.json" ) ] ) # Compile prompt instructions including the A2UI JSON schema A2UI_SYSTEM_INSTRUCTION = schema_manager.generate_system_prompt( role_description="You are a helpful travel booking assistant.", ui_description="Use InteractiveOptionPicker for choices, SeatSelectionPicker for seat selection...", include_schema=True, include_examples=True, allowed_components=["InteractiveOptionPicker", "SeatSelectionPicker", "BookingStatus"] ) With this generated system instruction, the LLM is grounded in the schema layout and knows exactly how to formulate component updates that the Android client is capable of rendering. Natively rendering A2UI with Jetpack Compose First, add the dependencies to our module's build.gradle.kts file: // android/feature/trip/booking_assistant/build.gradle.kts dependencies { implementation("androidx.a2ui:a2ui-model:1.0.0-alpha01") implementation("androidx.a2ui.compose:compose-runtime:1.0.0-alpha01") implementation("androidx.a2ui.compose:compose-ui:1.0.0-alpha01") implementation("androidx.compose.material3:material3-a2ui:1.0.0-alpha01") } Each component class in the catalog (such as BookingStatusComponent) defines how to map the properties received from the JSON payload into a Jetpack Compose composable function. Register the custom components in a catalog: // android/feature/trip/booking_assistant/src/main/kotlin/com/example/jetpacker/feature/booking_assistant/CustomBookingAssistantCatalog.kt import androidx.a2ui.compose.ui.A2uiCatalog fun bookingAssistantCatalog(): A2uiCatalog { return A2uiCatalog( catalogId = "https://example.com/catalogs/booking_assistant/v1/catalog.json", components = listOf( InteractiveOptionPickerComponent(), SeatSelectionPickerComponent(), BookingStatusComponent() ) ) } Tip: Jetpacker implements custom components (InteractiveOptionPicker, SeatSelectionPicker, BookingStatus) tailored for booking flows. If your agent uses standard elements (such as text, cards, buttons, rows, columns, checkboxes, and date-time pickers), material3-a2ui also provides materialA2uiBasicCatalogV1(...), giving you ready-to-use Material 3 implementations without writing any custom components. Note: To keep the backend and mobile client aligned, both rely on the same catalog definition ID (https://example.com/catalogs/booking_assistant/v1/catalog.json). If you add or modify properties on the backend catalog, you must increase the version number, and update the matching Kotlin component class to prevent parsing errors. In the BookingAssistantViewModel, process the A2UI messages using the A2uiMessageProcessor and update our active surfaces: // android/feature/trip/booking_assistant/src/main/kotlin/com/example/jetpacker/feature/booking_assistant/BookingAssistantViewModel.kt import androidx.lifecycle.ViewModel import androidx.a2ui.model.processor.A2uiSurfaceModel import androidx.a2ui.compose.ui.A2uiMessageProcessor import kotlinx.coroutines.flow.StateFlow class BookingAssistantViewModel : ViewModel() { private val messageProcessor = A2uiMessageProcessor( catalogs = listOf(bookingAssistantCatalog()) ) val activeSurfaces: StateFlow> = messageProcessor.activeSurfaces init { viewModelScope.launch(Dispatchers.Default) { processor.collectMessages() } } // ... } In the Compose screen, collect these surfaces and render each one using the official A2uiSurface composable from androidx.compose.material3:material3-a2ui. A2uiSurface automatically handles reactive component state observation, Material 3 loading indicators, error fallbacks, and animated transitions between updates: // android/feature/trip/booking_assistant/src/main/kotlin/com/example/jetpacker/feature/booking_assistant/BookingAssistantScreen.kt import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.a2ui.A2uiSurface @Composable fun BookingAssistantScreen( viewModel: BookingAssistantViewModel, modifier: Modifier = Modifier ) { val activeSurfaces by viewModel.activeSurfaces.collectAsState() LazyColumn( modifier = modifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(16.dp) ) { items(activeSurfaces) { surfaceModel -> Card(modifier = Modifier.fillMaxWidth()) { A2uiSurface( surfaceModel = surfaceModel, modifier = Modifier.fillMaxWidth().wrapContentHeight() ) } } } } You will now see several UI surfaces generated by our agent running in the backend: A well-designed booking assistant that relies on UI instead of text to interact with the user. Check out the full source code for Jetpacker on GitHub, and watch the video Build Intelligent Android apps with Google’s AI to learn more about how to integrate agentic workflows directly into your app. Check out the other parts of this blog post series: Part 1: Introduction of the app and a high-level overview. Part 2: On-device intelligence. Dive deep into ML Kit’s GenAI APIs and Gemini Nano to build privacy-first features like itinerary summarization, receipt parsing, and local audio processing. Part 3: Hybrid and cloud reasoning. Explore how to use Firebase AI Logic to ground LLM answers in real-world data like Google Maps and web context. Part 4: System integration. Integrating with the Android intelligence system using AppFunctions. Interested in more on Android Development? Follow Android Developers on YouTube or LinkedIn! All code snippets in this blog post follow the following copyright notice: Copyright 2026 Google LLC.
Read the original at Android Developers

More in Dev