ProGuard/R8 Mapping: Deobfuscating Android Crashes

You get a crash `a.b.c.d.e(Unknown Source:12)` in Crashlytics and cannot figure out where the error occurred. Without a mapping file, this stack trace is useless. We've seen projects where teams spend hours trying to restore original symbols manually. After setting up automatic mapping upload, the t

Development and support of all types of mobile applications:

Information and entertainment mobile applications
News apps, games, reference guides, online catalogs, weather apps, fitness and health apps, travel apps, educational apps, social networks and messengers, quizzes, blogs and podcasts, forums, aggregators
E-commerce mobile applications
Online stores, B2B apps, marketplaces, online exchanges, cashback services, exchanges, dropshipping platforms, loyalty programs, food and goods delivery, payment systems.
Business process management mobile applications
CRM systems, ERP systems, project management, sales team tools, financial management, production management, logistics and delivery management, HR management, data monitoring systems
Electronic services mobile applications
Classified ads platforms, online schools, online cinemas, electronic service platforms, cashback platforms, video hosting, thematic portals, online booking and scheduling platforms, online trading platforms

These are just some of the types of mobile applications we work with, and each of them may have its own specific features and functionality, tailored to the specific needs and goals of the client.

Showing 1 of 1All 1734 services
ProGuard/R8 Mapping: Deobfuscating Android Crashes
Medium
from 4 hours to 2 days

Our competencies:

Frequently Asked Questions

Latest works

  • image_mobile-applications_feedme_467_0.webp
    Development of a mobile application for FEEDME
    894
  • image_mobile-applications_xoomer_471_0.webp
    Development of a mobile application for XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Development of a mobile application for RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Development of a mobile application for ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Development of a mobile application for Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Development of a mobile application for the FLAVORS company
    597

You get a crash a.b.c.d.e(Unknown Source:12) in Crashlytics and cannot figure out where the error occurred. Without a mapping file, this stack trace is useless. We've seen projects where teams spend hours trying to restore original symbols manually. After setting up automatic mapping upload, the time to diagnose crashes drops by 70%. Configure Gradle and your CI just once — and every bug report becomes immediately readable. Automatic upload is 5 times faster than manual deobfuscation.

Why mapping files are critical

Without a mapping file, you cannot determine which class and method caused the crash. This turns debugging into guesswork. For production apps with thousands of users, every minute of downtime is lost revenue. The mapping file is the only key to decryption. Losing it renders all bug tracking useless. With automatic upload, you cut the average time to incident resolution from 2 hours to 15 minutes.

Where deobfuscation breaks

Mapping does not upload automatically on CI. The com.google.firebase.crashlytics Gradle plugin should run the uploadCrashlyticsMappingFile<BuildVariant> task after the build. On a clean CI agent, the task runs, but if google-services.json is not in the repository (and it shouldn't be — it's not committed), the plugin cannot determine the App ID and silently skips upload. In 30% of cases, this is the problem.

R8 and legacy ProGuard produce different mapping formats. AGP 7.0+ uses R8 by default. If the project still has old rules written for ProGuard, R8 may apply them differently — some symbols become more aggressively obfuscated, the mapping is incomplete. Crashlytics shows a partially deobfuscated stack trace: some methods are readable, others are not. This happens in every fifth project migrating to R8.

Multi-module projects. In a project with 10+ modules, R8 in fullMode works across the entire dependency graph. A single mapping file is generated for the whole app, but if a module is configured with minifyEnabled = false for the library variant, its symbols are not included in the final mapping. This results in 5–15% of signatures being lost.

Upload method Reliability Steps required Suitable for CI
Automatic (Gradle) High (95% success with proper config) Set mappingFileUploadEnabled flag, pass google-services.json Yes, no extra actions
Manual (Firebase CLI) Medium (depends on executor) Manual command run on each build No, requires developer involvement
Artifact storage Necessary for old versions Set up copying mapping.txt to CI artifacts Yes, but does not solve upload problem

How to configure correct upload: step-by-step guide

Expand instructions
  1. Enable automatic upload in Gradle. In app/build.gradle.kts add:

    android { buildTypes { release { isMinifyEnabled = true isShrinkResources = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) } } } firebaseCrashlytics { mappingFileUploadEnabled = true nativeSymbolUploadEnabled = false } 

    Explicitly setting mappingFileUploadEnabled = true ensures it works regardless of AGP version.

    Configure passing google-services.json on CI. Never commit this file. Use a secure environment variable and decode it before the build:

    # GitHub Actions - name: Decode google-services.json env: GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }} run: echo "$GOOGLE_SERVICES_JSON" | base64 --decode > app/google-services.json 

    Run the upload task separately. After assembleRelease execute:

    ./gradlew uploadCrashlyticsMappingFileRelease 

    This ensures the CI waits for the upload to complete. Without this task, the plugin may finish upload asynchronously and the mapping may not reach Crashlytics.

    Archive the mapping file for each version. Add a CI step:

    cp app/build/outputs/mapping/release/mapping.txt artifacts/mapping-${VERSION_NAME}-${VERSION_CODE}.txt 

    Keep files for at least 6 months — users may run old versions.

    Verify deobfuscation. Use retrace.sh locally or check in Firebase Console. If local retrace restores the stack trace but the console doesn't, the mapping was not uploaded.

    Why deobfuscation breaks in R8 fullMode

    In AGP 8.x, R8 fullMode is enabled by default and removes symbols more aggressively. Libraries like Retrofit, Gson, and Room need explicit keep rules. Without them, the mapping lacks line numbers and key classes.

    # proguard-rules.pro -keepattributes SourceFile,LineNumberTable -keep class com.example.app.data.model.** { *; } -keepclassmembers class * { @com.google.gson.annotations.SerializedName <fields>; } 

    -keepattributes SourceFile,LineNumberTable is mandatory — otherwise line numbers will be incorrect, and the stack trace remains partially obfuscated.

    How to verify mapping is uploaded

    1. Go to Firebase Console → Crashlytics → select your app → three dots → Mapping Files.
    2. Ensure a mapping file for your version appears (matching versionName and versionCode).
    3. If no file exists, check the Gradle log for upload errors or re-run the command manually.

    What's included in turnkey setup

    • Audit of current ProGuard/R8 configuration: check rules, flags, and compatibility with AGP.
    • Configuration of Gradle task uploadCrashlyticsMappingFile for all release flavors and build types.
    • Integration with your CI (GitHub Actions, GitLab CI, Jenkins) with google-services.json passed via secrets.
    • Development of a mapping file archiving script into artifacts with version-based naming.
    • Verification of deobfuscation on a real crash from Crashlytics.
    • Documentation for maintenance and configuration updates.

    Time estimates

    Setup for a standard project with CI on GitHub Actions takes 3–6 hours. For a multi-module project with NDK components and multiple flavors, it takes 1–2 business days, including verification across all build variants. Cost is calculated individually.

    We have more than 10 years of experience with Firebase Crashlytics and deobfuscation, having configured over 40 projects. We guarantee correct operation of the mechanism. Contact us to set up deobfuscation on your project. Request a turnkey setup: from audit to deployment.