Creating your own launcher for Kotlin Multiplatform (KMP) on Android - a task that combines user interface development, work with KMP modules and a deep dive into operating system architecture. Such a launcher can become a convenient shell for launching cross-platform applications, testing modules, or even the basis for a custom desktop. But where to start if you have never done such projects?

In this article we will analyze the process from idea to implementation: we will select tools, set up the environment, write basic code and optimize performance. Let's pay special attention KMP integration to it - after all, it is what distinguishes such a launcher from standard solutions. You will learn how to avoid common mistakes when working with Gradledeclarations, how to correctly process expect/actualdeclarations, and why some functions may not work on all versions of Android. Are you ready to dive into the details?

Before we get into the technical details, let's answer a key question: why do you need a launcher for KMP at all? First, it makes it easy to run cross-platform modules on mobile devices without having to deploy a full application. Secondly, it allows you to test general logic (common-code) directly to Android, saving time on switching between platforms. Thirdly, such a launcher can become part of a more complex ecosystem - for example, for managing microservices or plugins.

๐Ÿ“Š For what purpose are you creating a KMP launcher?
For testing modules
As the basis of a custom desktop
For training project
For a commercial product
Other

1. Preparing the development environment

The first step is setting up the tools. Without a properly configured environment, even the simplest launcher will not work. You will need:

  • ๐Ÿ“ฑ Android Studio version Giraffe (2022.3.1) or later (check compatibility with the latest versions Kotlin and Gradle).
  • ๐Ÿ”ง Kotlin Multiplatform Plugin (version 1.9.20 or higher). Outdated versions may not support current features. Android.
  • ๐Ÿ“ฆ Java Development Kit (JDK) version 17 is a mandatory requirement for modern projects on Kotlin.
  • ๐Ÿ”„ Gradle 8.2+ with support version catalogs (simplifies dependency management).

Install Android Studio from the official website, then open SDK Manager and download:

  • ๐Ÿ“ฑ Android 13 (API 33) or newer (recommended for testing the latest features).
  • ๐Ÿ› ๏ธ Build tools Android SDK Build-Tools versions 34.0.0.
  • ๐Ÿ” Android Emulator with image Google Play (for testing on a virtual device).

After installation, check the versions in the terminal:

kotlin -version

gradle -v

java -version

โš ๏ธ Attention: If you use MacOS with a chip Apple Silicon (M1/M2), install Rosetta 2 for correct operation Android EmulatorWithout this, the emulator may not start. or work with errors.

2. Creating a basic KMP project

Now let's move on to creating the project. Android Studio select New Project โ†’ Kotlin Multiplatform App. Set up the structure as follows:

Parameter Value Explanation
Project name KMPLauncher Project name (without spaces or special characters).
Package name com.example.kmplauncher Unique application identifier.
Target platforms Android, iOS (optional) Minimum select Android.
Kotlin version 1.9.20 Current stable version.

After generating the project, open the file build.gradle.kts in the root folder and add dependencies for Android:

kotlin {

android {

publishLibraryVariants("release", "debug")

}

sourceSets {

val commonMain by getting {

dependencies {

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")

}

}

val androidMain by getting {

dependencies {

implementation("androidx.activity:activity-compose:1.8.0")

implementation("androidx.compose.material3:material3:1.2.0")

}

}

}

}

Synchronize project with Gradle (button Sync Now in the upper right corner). If errors occur, check:

  • ๐Ÿ”Œ Correct Internet connection (dependencies are downloaded from Maven Central).
  • ๐Ÿ“ No typos in build.gradle.kts.
  • ๐Ÿ”„ Version compatibility Kotlin i Gradle (see official documentation).

- All dependencies installed in build.gradle.kts

- Gradle synchronization completed without errors

- Emulator selected or physical device connected

- Developer mode is enabled on the Android device-->

3. Development of the user interface

The launcher interface should be minimalistic but functional. We will use Jetpack Compose a modern framework for building the UI on Kotlin. Open the file androidApp/src/main/AndroidManifest.xml and add permissions:

<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

Now create a file MainActivity.kt in the package androidApp:

class MainActivity : ComponentActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContent {

KMPLauncherTheme {

Surface(modifier = Modifier.fillMaxSize()) {

LauncherScreen()

}

}

}

}

}

@Composable

fun LauncherScreen() {

val context = LocalContext.current

val packageManager = context.packageManager

val packages = remember { mutableStateListOf<ApplicationInfo>() }

LaunchedEffect(Unit) {

packages.addAll(

packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

.filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 }

)

}

LazyColumn {

items(packages) { app ->

Card(

onClick = { / Launch application / },

modifier = Modifier.padding(8.dp)

) {

Text(app.loadLabel(packageManager).toString())

}

}

}

}

This code:

  • ๐Ÿ“ฑ Gets a list of installed applications (excluding system ones).
  • ๐Ÿ–ผ๏ธ Displays them in the form of cards with the ability to launch.
  • ๐Ÿ”„ Uses LaunchedEffect for asynchronous data loading.
โš ๏ธ Attention: Starting from Android 11 (API 30), access to the list of applications is limited. For the code to work, add to AndroidManifest.xml parameter android:requestLegacyExternalStorage="true" or request permission QUERY_ALL_PACKAGES via Google Play Console (for publication in the store).

4. Integration of KMP modules

The main difference between our launcher and the usual one is support Kotlin Multiplatform. Create a common module (common) for logic that will work on all platforms. For example, let's add a function for checking application compatibility with KMP:

In file common/src/commonMain/kotlin/Common.kt:

expect class Platform() {

fun isKMPCompatible(appPackage: String): Boolean

}

fun checkCompatibility(appPackage: String): Boolean {

return Platform().isKMPCompatible(appPackage)

}

Implement actual-class for Android in androidApp/src/main/kotlin/Platform.android.kt:

actual class Platform actual constructor() {

actual fun isKMPCompatible(appPackage: String): Boolean {

// Check logic (for example, by signature or manifest)

return appPackage.contains("kmp") || appPackage.contains("multiplatform")

}

}

Now in LauncherScreen you can use general logic:

Card(

onClick = {

if (checkCompatibility(app.packageName)) {

// Launching a KMP application

} else {

// Standard launch

}

}

) { ... }

Critical detail: if your launcher will launch KMP modules dynamically (via ClassLoader), ensure that all dependencies match the target application's dependencies. Otherwise, there may be ClassNotFoundException or version conflicts.

๐Ÿ’ก

To debug KMP code, use the plugin Kotlin Multiplatform Mobile in Android Studio. It allows you to run shared code directly in the emulator without building the full project.

5. Handling application launch

To launch applications from the launcher, add the following code to the handler onClick:

val intent = packageManager.getLaunchIntentForPackage(app.packageName)

if (intent != null) {

startActivity(intent)

} else {

Toast.makeText(context, "Could not launch ${app.loadLabel(packageManager)}", Toast.LENGTH_SHORT).show()

}

For KMP applications may require additional logic:

  • ๐Ÿ”— Checking the presence of KMP_MANIFEST in assets target APK.
  • ๐Ÿ“ฆ Loading dynamic libraries via System.loadLibrary.
  • ๐Ÿ”„ Restarting the activity after loading modules (sometimes required for initializing nativecode).

Example of manifest verification:

fun hasKMPManifest(context: Context, packageName: String): Boolean {

return try {

val ai = context.packageManager.getApplicationInfo(packageName, 0)

context.assets.open("${ai.sourceDir}/assets/KMP_MANIFEST").use { true }

} catch (e: Exception) {

false

}

}

โš ๏ธ Attention: The launch of third-party APKs with dynamic code loading may be blocked Google Play Protect as a potentially dangerous action. For commercial use, obtain a certificate Android App Bundle and go through moderation.

6. Optimization and testing

Before release, optimize the launcher:

  • ๐Ÿš€ Performance: Use LazyColumn to display the list of applications (saves memory).
  • ๐Ÿ” Security: Request permissions only when necessary (for example QUERY_ALL_PACKAGES only for Android 11+).
  • ๐Ÿ“ฆ Size APK: Enable minifyEnabled true i shrinkResources true in build.gradle.
  • ๐Ÿ› ๏ธ Errors: Test on devices with different versions of Android (from 9 to 14).

To test KMP functionality:

  1. Build a test KMP application with a simple common-module.
  2. Install it on the device next to the launcher.
  3. Check whether the launcher correctly identifies it as KMP-compatible.
  4. Run the application through the launcher and make sure that the general logic works.

Example of a KMP test module (commonTest):

class CommonTest {

@Test

fun testCompatibilityCheck() {

assertTrue(checkCompatibility("com.example.kmpapp"))

assertFalse(checkCompatibility("com.android.settings"))

}

}

How to test on a physical device?

1. Enable USB debugging in the developer settings.

2. Connect the device to the PC and execute in the terminal:

adb devices

(your device should be displayed).

3. In Android Studio, select the device in the drop-down menu and click Run.

4. If the launcher does not appear in the list of applications, check AndroidManifest.xml for the presence of the tag:

<intent-filter>

<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />

</intent-filter>

7. Publishing on Google Play

If you plan to distribute the launcher via Google Play, prepare the following materials:

  • ๐Ÿ“ Description: Indicate that the launcher supports KMP (this will highlight your application).
  • ๐Ÿ–ผ๏ธ Screenshots: Show the interface with running KMP applications.
  • ๐ŸŽฅ Video: Short demonstration of work (optional, but increases conversion).
  • ๐Ÿ“‹ Privacy Policy: Required for all applications in Google Play.

Features of KMP launcher publication:

  • ๐Ÿ”— In release-build, disable debug logs (isDebuggable = false).
  • ๐Ÿ›ก๏ธ Sign the APK using keytool and jarsigner (or use Android App Bundle).
  • ๐Ÿ“ฆ Make sure that all nativelibraries (.so-files) are included in the assembly.

An example command for generating a key:

keytool -genkey -v -keystore kmp_launcher.keystore -alias KMPLauncher -keyalg RSA -keysize 2048 -validity 10000
โš ๏ธ Attention: If your launcher uses dynamic code loading (for example, it loads KMP modules from the network), Google Play may require additional moderation. In this case, prepare technical support in advance. justification (in English).
๐Ÿ’ก

Before publishing, test the launcher on devices with different architectures (arm64, x86_64) - some KMP modules may not work on emulators with x86.

8. Further development of the project

The basic launcher is ready, but it can be expanded:

  • ๐Ÿ“Š Statistics: Add data collection about launched KMP applications (with the user's consent).
  • ๐Ÿ”„ Updates: Implement update checking for KMP modules via GitHub Releases or Firebase.
  • ๐ŸŽจ Customization: Allow users to change themes, icons and layout of elements.
  • ๐Ÿค– Automation: Integrate CI/CD (for example, GitHub Actions) for automatic build and testing.

Example of adding themes (in androidApp/src/main/kotlin/Theme.kt):

private val DarkColorPalette = darkColors(

primary = Purple200,

secondary = Purple700

)

private val LightColorPalette = lightColors(

primary = Purple500,

secondary = Purple700

)

@Composable

fun KMPLauncherTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {

val colors = if (darkTheme) DarkColorPalette else LightColorPalette

MaterialTheme(colors = colors, content = content)

}

To integrate with GitHub Actions create a file .github/workflows/android.yml:

name: Android CI

on: [push, pull_request]

jobs:

build:

runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- name: Set up JDK 17

uses: actions/setup-java@v3

with:

java-version: '17'

distribution: 'temurin'

- name: Build with Gradle

run: ./gradlew build

This will automatically check the build with each commit.

FAQ: Frequently asked questions

Is it possible to make a launcher without using Kotlin Multiplatform?

Yes, but then you will lose the main advantage - cross-platform. A regular launcher on Android will not be able to interact with the general logic of KMP modules. iOS or other platforms, a standard Android project is sufficient.

Why does the launcher not see installed KMP applications?

Probable reasons:

  1. No permission QUERY_ALL_PACKAGES (for Android 11+).
  2. The KMP application does not have an explicit intent-filter c MAIN/LAUNCHER.
  3. The application is installed in an isolated profile (for example, via Work Profile).

Check the target application manifest and logs Logcat to Android Studio.

How to add support for launching KMP modules from the network?

To do this:

  1. Download the archive with the module (for example, from .kmp-archive with a module (for example, with GitHub Releases).
  2. Unpack it to a temporary directory (cacheDir).
  3. Load classes via URLClassLoader:
val loader = URLClassLoader(

arrayOf(File(moduleDir, "common.jar").toURI().toURL()),

javaClass.classLoader

)

val kmpClass = loader.loadClass("com.example.CommonKt")

val method = kmpClass.getMethod("someFunction")

method.invoke(null)

โš ๏ธ Please note that dynamic loading of code requires permission INTERNET and may be blocked Google Play Protect.

What are the alternatives to Kotlin Multiplatform for the launcher?

If KMP is not suitable, consider:

  • Flutter: Cross-platform framework with plugin support. Suitable if you need a beautiful UI, but the performance is lower than that of native code.
  • React Native: Popular for hybrid applications, but integration with native modules is more difficult than in KMP.
  • Native Android: If cross-platform functionality is not needed, it is easier to use Java/Kotlin s Jetpack Compose.

KMP wins where common logic between platforms is important (for example, algorithms, network requests, data processing).

How to monetize a launcher for KMP?

Options:

  • ๐Ÿ’ฐ Paid version: Post in Google Play with a trial period.
  • ๐Ÿ“ฆ Premium features: Free launcher with paid extensions (for example, cloud backup of settings).
  • ๐Ÿ“ข Advertising: Integrate AdMobbut do not overload the interface.
  • ๐Ÿค Partnership: Collaborate with KMP application developers (for example, place their products at the top of the list for a commission).

For monetization via Google Play register as a developer (one-time payment $25) and set up Google Play Billing.