Android APK Compatibility Check: Diagnosing Builds That Won't Install or Won't Run
Compatibility is not one property. An APK can fail at install time, at launch, or minutes into use, and each failure maps to a different attribute. A decision table for ABI, SDK level, split APKs, signatures, Play Services, and storage.
Nothing is more frustrating than a download that completes, installs, and then doesn't work — with no explanation. The mistake people make is treating this as one problem. It's at least five separate ones, and they fail at different moments.
Where the failure happens tells you what to check.
The failure-point map
- Before install starts— “App not installed” or “Package appears to be invalid”. Check signature and
minSdkVersion. - During install — an
INSTALL_FAILED_*code. Check signature mismatch, storage, downgrade. - At launch — crash or black screen immediately. Check ABI, split bundle, native libs.
- After a few seconds — frozen, then an ANR dialog. Check ABI fallback, memory, storage.
- On specific features — camera, maps, or payments fail. Check Play Services and target API level.
Read the row that matches, then work only that row's column. Guessing across categories is what produces the twenty-step forum advice that never resolves anything.
Attribute 1: Architecture (ABI)
The most common cause of “installs but crashes immediately”. Apps can ship native code compiled per CPU architecture: arm64-v8a for every modern phone, armeabi-v7a as a 32-bit fallback, and x86_64 / x86 for emulators.
adb shell getprop ro.product.cpu.abi
unzip -l app.apk | grep "lib/" | awk -F/ '{print $2}' | sort -uIf the APK contains only armeabi-v7a and the device is arm64-v8a, it installs and runs via 32-bit compatibility, but is slower and crashes on devices where the vendor dropped the 32-bit layer — recent flagship builds have begun removing it entirely. If the APK contains only x86, it will not run on ARM at all. INSTALL_FAILED_NO_MATCHING_ABIS is the explicit version of this failure. Fix by getting the right build, not by fighting the installer.
Attribute 2: Minimum and target SDK
minSdkVersion is a hard gate: lower device version, no install. Check it with aapt dump badging app.apk | grep -E "sdkVersion|targetSdkVersion", and compare against ro.build.version.sdk. Clean failures produce a “not compatible with your device” message.
Insidious failures come from targetSdkVersion. Since Android 14, apps targeting below API 23 are refused outright. Below 33, the system shows extra confirmation dialogs and increasingly restricts background behavior. On Android 15 and later, very old targets can fail to install with nothing more than a generic error.
Attribute 3: Split APKs
A .apks, .xapk, or .apkmfile is a bundle, not an app: a base APK plus configuration splits per ABI, density, and language. The symptom of getting this wrong is an install that “succeeds” and an app that crashes on launch, because the base is present but the arm64 native libraries live in a split that was never installed.
unzip -l bundle.apks | head -30
# base.apk + split_config.arm64_v8a.apk + split_config.xxhdpi.apk
adb install-multiple base.apk split_config.arm64_v8a.apk split_config.xxhdpi.apkThe one file you can install directly is a universalbuild, which packages all splits into a single APK. Larger, but self-contained — and when a bundle misbehaves and you don't need the size savings, switching to universal is the fastest fix.
Attribute 4: Signature and upgrade path
INSTALL_FAILED_UPDATE_INCOMPATIBLE and INSTALL_FAILED_ALREADY_EXISTS usually mean a version of this package with a different signing keyis already installed. This happens constantly with sideloading, because the same app may come from Play (Play App Signing), from the developer's site (upload key), and from a mirror (possibly repackaged).
adb shell dumpsys package com.example.app | grep -A3 "signatures"
apksigner verify --print-certs new.apkIf they differ there is no upgrade path — you must uninstall first, which deletes app data. Back up before doing that. The other one is INSTALL_FAILED_VERSION_DOWNGRADE: the installed version is newer, so either uninstall or use adb install -r -d old-version.apk.
Attribute 5: Play Services and framework dependencies
An app that installs and opens but fails on sign-in, maps, push, or in-app purchase is usually missing Google Play Services, not broken. Check with adb shell pm list packages | grep -E "com.google.android.gms|com.android.vending". Both listed means GMS is present; missing means many apps boot but silently fail on anything touching Google APIs, and some crash on launch during the availability check.
A subtler variant: GMS is present but outdated. The app calls a newer Play Services API that does not exist on the installed version and fails on first use. Update GMS before blaming the app.
Attribute 6: Storage and internal constraints
INSTALL_FAILED_INSUFFICIENT_STORAGE is honest, but Android needs more room than the APK size: the APK itself, extracted native libraries, dex optimization output (often 1.5–2× the APK), and working space during the install. Keep 2.5× the APK size free — a 400 MB APK can fail on a device showing 700 MB free. Check with adb shell df -h /data; if /data is separate from /storage/emulated, the number in Settings may not be the relevant one.
A fast triage sequence
aapt dump badging app.apk | grep -E "sdkVersion|native-code"— minSdk and supported ABIs in one line- Compare against
ro.build.version.sdkandro.product.cpu.abi apksigner verify --print-certs app.apkversus the installed package's signatureadb shell df -h /data— free space at least 2.5× the APK size- Check whether the file is
.apks/.xapkand needs a split-aware install
That covers well over 90% of real-world cases. If all five pass and the app still fails, run adb logcat during the install and read the actual error — at that point it is no longer a compatibility problem, it is a specific bug with a specific message. More troubleshooting guides are on the gptoapk blog.