REST services

Rest server module

To start, we'll create a simple REST server module in our movies project. Normally this would not go in your Android project, but for convenience we'll add it here.

Use File > New Module > Java or Kotlin Library to create a module called restserver. Use RunServer as the class name.

We'll use the Apache Jersey server for our simple REST server, so we'll add its libraries to the version catalog. To make things a bit more convenient, we'll use the version catalog [bundles] support, allowing us to define a single name to represent a set of libraries we're using together.

show in full file gradle/libs.versions.toml
[versions]
// ...
glance="1.1.1"
jetbrainsKotlinJvm = "2.0.21"
jersey="3.1.9"
activation="2.1.3"

[libraries]
jersey-grizzly2 = { group = "org.glassfish.jersey.containers", name = "jersey-container-grizzly2-http", version.ref = "jersey" }
jersey-jetty = { group = "org.glassfish.jersey.containers", name = "jersey-container-jetty-http", version.ref = "jersey" }
jersey-servlet = { group = "org.glassfish.jersey.containers", name = "jersey-container-servlet-core", version.ref = "jersey" }
jersey-jackson = { group = "org.glassfish.jersey.media", name = "jersey-media-json-jackson", version.ref = "jersey" }
jersey-server = { group = "org.glassfish.jersey.core", name = "jersey-server", version.ref = "jersey" }
jersey-hk2 = { group = "org.glassfish.jersey.inject", name = "jersey-hk2", version.ref = "jersey" }
activation = { group = "jakarta.activation", name = "jakarta.activation-api", version.ref = "activation" }
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" }
glance-material3 = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" }
// ...
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }

[bundles]
server = [
    "jersey-grizzly2",
    "jersey-jetty",
    "jersey-servlet",
    "jersey-jackson",
    "jersey-server",
    "jersey-hk2",
    "activation"
]

[plugins]
// ...

We add these to the new module's build.gradle.kts using the version catalog bundle.

To run the server, we need to add the application plugin. We don't need to add this plugin to the version catalog as it's a standard part of the Gradle distribution. We specify the application to run using mainClass

show in full file restserver/build.gradle.kts
plugins {
    id("java-library")
    alias(libs.plugins.jetbrains.kotlin.jvm)
    application
}

// ...
}

dependencies {
    implementation(libs.bundles.server)
}

kotlin {
    // ...
}

application {
    mainClass = "com.androidbyexample.compose.movies.restserver.RunServer"
}

To test that everything is set up ok to run, we'll add a simple main to our RunServer class

show in full file restserver/src/main/java/com/androidbyexample/compose/movies/restserver/RunServer.kt
// ...

class RunServer {
    companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            println("It runs!")
        }
    }
}

and then we can run it via

./gradlew run

Note

Because we're running this on a Java Virtual Machine (JVM), the main class is expected to have a public static void main(String[] args) method. TO accomplish this, we:

  • Add a companion object to RunServer. Any function defined inside it belongs to the RunServer class, and you won't need an instance of RunServer to execute it.
  • Add a fun main(args: Array<String>) function inside the companion object
  • Mark the main function with the @JvmStatic annotation to define the function as a static method when run on the JVM.

All code changes

CHANGED: build.gradle.kts
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false
    alias(libs.plugins.kotlin.compose) apply false
    alias(libs.plugins.android.library) apply false
    alias(libs.plugins.ksp) apply false
    alias(libs.plugins.jetbrains.kotlin.jvm) apply false
}
CHANGED: gradle/libs.versions.toml
[versions]
agp = "8.7.3"
kotlin = "2.0.21"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.9.3"
composeBom = "2024.12.01"
appcompat = "1.7.0"
material = "1.12.0"
room = "2.6.1"
ksp = "2.0.21-1.0.28"
compileSdk = "35"
targetSdk = "35"
minSdk = "24"
jvmTarget = "11"
javaVersion = "VERSION_11"
lifecycle-compose = "2.8.7"
icons-extended = "1.7.6"
glance="1.1.1"
jetbrainsKotlinJvm = "2.0.21"
jersey="3.1.9" activation="2.1.3" [libraries] jersey-grizzly2 = { group = "org.glassfish.jersey.containers", name = "jersey-container-grizzly2-http", version.ref = "jersey" } jersey-jetty = { group = "org.glassfish.jersey.containers", name = "jersey-container-jetty-http", version.ref = "jersey" } jersey-servlet = { group = "org.glassfish.jersey.containers", name = "jersey-container-servlet-core", version.ref = "jersey" } jersey-jackson = { group = "org.glassfish.jersey.media", name = "jersey-media-json-jackson", version.ref = "jersey" } jersey-server = { group = "org.glassfish.jersey.core", name = "jersey-server", version.ref = "jersey" } jersey-hk2 = { group = "org.glassfish.jersey.inject", name = "jersey-hk2", version.ref = "jersey" } activation = { group = "jakarta.activation", name = "jakarta.activation-api", version.ref = "activation" }
glance-appwidget = { group = "androidx.glance", name = "glance-appwidget", version.ref = "glance" } glance-material3 = { group = "androidx.glance", name = "glance-material3", version.ref = "glance" } icons-extended = { group = "androidx.compose.material", name = "material-icons-extended-android", version.ref = "icons-extended"} lifecycle-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle-compose" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" } material = { group = "com.google.android.material", name = "material", version.ref = "material" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
[bundles] server = [ "jersey-grizzly2", "jersey-jetty", "jersey-servlet", "jersey-jackson", "jersey-server", "jersey-hk2", "activation" ]
[plugins] android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } android-library = { id = "com.android.library", version.ref = "agp" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } jetbrains-kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "jetbrainsKotlinJvm" }
ADDED: restserver/.gitignore
/build
ADDED: restserver/build.gradle.kts
plugins {
    id("java-library")
    alias(libs.plugins.jetbrains.kotlin.jvm)
application
} java { sourceCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11 }
dependencies { implementation(libs.bundles.server) }
kotlin { compilerOptions { jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11 } }
application { mainClass = "com.androidbyexample.compose.movies.restserver.RunServer" }
ADDED: restserver/src/main/java/com/androidbyexample/compose/movies/restserver/RunServer.kt
package com.androidbyexample.compose.movies.restserver

class RunServer {
companion object { @JvmStatic fun main(args: Array<String>) { println("It runs!") } }
}
CHANGED: settings.gradle.kts
pluginManagement {
    repositories {
        google {
            content {
                includeGroupByRegex("com\\.android.*")
                includeGroupByRegex("com\\.google.*")
                includeGroupByRegex("androidx.*")
            }
        }
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "Movies"
include(":app")
include(":data")
include(":repository")
include(":restserver")