Skip to main content

Fixing Start.io Unity Build Errors: Gradle & NoClassDefFoundError

 There is rarely a frustration more acute in game development than a "Build Successful" notification followed immediately by a runtime crash.

If you are integrating the Start.io (formerly StartApp) SDK into Unity, you have likely encountered the infamous java.lang.NoClassDefFoundError or cryptic Gradle build failures. These errors often appear during the final stages of the build process or, worse, strictly in Release builds while Debug builds run perfectly.

This guide provides a definitive technical solution to resolve Start.io dependencies in Unity, specifically addressing Android Gradle conflicts and R8/ProGuard stripping issues.

The Root Cause: Why NoClassDefFoundError Happens

To fix the error permanently, you must understand the architecture failure. Unity runs on C# (Mono/IL2CPP), but Android plugins run on Java/Kotlin. When you call the Start.io SDK from C#, you are bridging two different runtime environments.

The NoClassDefFoundError occurs when the Java Virtual Machine (JVM) attempts to load a class definition at runtime that was present during the compile phase but is missing from the final APK/AAB package.

In the context of Start.io and Unity, this usually happens for three specific reasons:

  1. R8/ProGuard Aggression: The Android build system creates a "Release" build and attempts to shrink the code size. It incorrectly identifies the Start.io classes as "unused" because they are called via reflection or JNI from Unity, not directly from other Java code. Consequently, it strips them out.
  2. EDM4U Failure: The External Dependency Manager for Unity (EDM4U) failed to inject the dependencies into the mainTemplate.gradle file.
  3. Duplicate Classes: A conflict exists between the Start.io SDK and another plugin (like AdMob mediation), leading Gradle to fail during the merging process.

Solution 1: Preventing Code Stripping (ProGuard/R8)

If your app runs in "Development Build" mode but crashes in "Release" mode, R8 is stripping the Start.io SDK. You must explicitly tell the build system to keep these classes.

Step 1: Enable Custom ProGuard File

In the Unity Editor:

  1. Go to Edit > Project Settings > Player.
  2. Select the Android tab.
  3. Scroll down to Publishing Settings.
  4. Check the box for Custom ProGuard File (or Custom proguard-user.txt in newer Unity versions).

Step 2: Add Preservation Rules

Navigate to Assets/Plugins/Android/proguard-user.txt and open it in your code editor. Append the following rules. These instructions force R8 to retain the Start.io namespace and its required interfaces.

# Keep Start.io (StartApp) SDK classes
-keep class com.startapp.** { *; }
-keep interface com.startapp.** { *; }

# Keep Android X annotations often used by modern SDKs
-keepattributes *Annotation*

# Prevent obfuscation of generic types (required for some ad parsers)
-keepattributes Signature

# If you use AdMob Mediation with Start.io, keep the adapter
-keep class com.startapp.sdk.mediators.** { *; }

Note: Without these lines, the Java byte-code optimizer sees no direct references to the SDK (since Unity calls it via JNI) and deletes the library to save space.

Solution 2: Forcing Dependency Injection in Gradle

If the build fails before producing an APK, or if the class is missing even in Debug builds, the dependency is likely not being resolved by Gradle.

We will bypass Unity's auto-resolution and manually enforce the dependency in the build script.

Step 1: Export the Gradle Template

In Project Settings > Player > Android > Publishing Settings, ensure Custom Main Gradle Template (mainTemplate.gradle) is checked.

Step 2: Edit mainTemplate.gradle

Open Assets/Plugins/Android/mainTemplate.gradle. Look for the dependencies block.

You must ensure the Start.io SDK is declared. However, simply adding the line isn't enough; you must also ensure mavenCentral() is in the repositories block.

Correct Repository Configuration:

allprojects {
    repositories {
        google()
        mavenCentral() // Critical: Start.io is hosted here
        flatDir {
            dirs 'libs'
        }
    }
}

Correct Dependency Injection: Inside the dependencies { ... } block at the bottom of the file, add the implementation line manually if EDM4U keeps removing it.

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    
    // Explicitly add Start.io SDK
    // Check Start.io docs for the latest version, 4.10.x+ recommended
    implementation 'com.startapp:inapp-sdk:4.10.12' 
    
    // ... other dependencies
}

Solution 3: Resolving Duplicate Class Errors

A common scenario involves integrating Start.io alongside Google Mobile Ads (AdMob). Both SDKs might depend on common Android libraries, or you might have the Start.io adapter attempting to pull in a different version of the SDK than what you manually installed.

If you see Duplicate class com.startapp... found in modules..., you are pulling the SDK twice.

The Fix: Exclude Transitive Dependencies

Modify your mainTemplate.gradle to include the adapter but exclude the internal SDK so you can manage the version manually.

dependencies {
    // Example: Integrating AdMob Mediation for Start.io
    implementation ('com.google.ads.mediation:startapp:4.10.12.0') {
        // Exclude the SDK bundled with the adapter to avoid duplicates
        exclude group: 'com.startapp', module: 'inapp-sdk'
    }

    // Now include the SDK version YOU want explicitly
    implementation 'com.startapp:inapp-sdk:4.10.12'
}

Deep Dive: Handling Android 11+ Visibility (<queries>)

Even if the class loads, Start.io will fail to serve ads on Android 11 (API 30) and above if package visibility is not configured. This is a security feature in Android that prevents apps from scanning other installed apps.

Start.io needs to detect specific packages to deliver targeted ads. You must modify your AndroidManifest.xml.

Step 1: Enable Custom Manifest

In Project Settings > Player > Android > Publishing Settings, check Custom Main Manifest.

Step 2: Add Query Permissions

Open Assets/Plugins/Android/AndroidManifest.xml and add the <queries> element inside the <manifest> tag, but outside the <application> tag.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yourcompany.gamename">

    <!-- REQUIRED for Start.io on Android 11+ -->
    <queries>
        <intent>
            <action android:name="android.intent.action.MAIN" />
        </intent>
        <package android:name="com.facebook.katana" /> <!-- Example if needed -->
    </queries>

    <application>
        <!-- Application nodes -->
    </application>
</manifest>

Note: The Start.io SDK usually merges these automatically, but if you have "Minify" enabled heavily, the manifest merger might strip them. Adding them manually ensures compliance.

Verification Checklist

Before rebuilding your project, run through this checklist to ensure the environment is clean:

  1. Force Resolve: Go to Assets > External Dependency Manager > Android Resolver > Force Resolve. Watch the console for "Resolution Succeeded".
  2. Clean Build Cache: In Unity, Edit > Preferences > External Tools. Uncheck "JDK Installed" and "Android SDK Installed", wait a moment, and recheck them. This clears stuck Gradle daemons.
  3. Invalidate Caches: If using a custom gradle factory, delete the .gradle folder in your project root (not Assets) to force a fresh dependency pull.

Conclusion

The NoClassDefFoundError in Unity Android builds is rarely a bug in the code you wrote; it is almost always a configuration desynchronization between Unity's build pipeline and Android's dependency management.

By enforcing ProGuard rules to prevent stripping and manually declaring dependencies in mainTemplate.gradle, you bypass the fragility of auto-resolvers and ensure a stable, production-ready build.