A shrinker such as ProGuard or R8 makes an Android app smaller and faster. It removes unused code, optimises what remains and renames classes, methods and fields to short names. That renaming makes decompiled output less readable, but it is a side effect of size reduction, not a security design. Strings, native libraries, resources and framework bundles stay as they were, and nothing checks the app while it runs. For apps that handle money, identity or valuable logic, hardening and runtime protection are the extra work. This article covers Android, with a note on iOS.
What a shrinker is for
- 2002
ProGuard first released
An open-source shrinker, optimiser and renamer for Java bytecode.
- 2010
ProGuard ships with the Android SDK tools
Release builds gain a standard way to shrink and rename Java code.
- 2019
R8 becomes the default
The Android Gradle plugin switches from ProGuard to R8, which reads the same keep rules.
ProGuard
ProGuard is an open-source tool that shrinks, optimises and renames Java bytecode. For years it was the standard way to reduce Android release builds, and its configuration format, the keep rule file, is still how Android developers tell the build what must not be removed or renamed.
R8
R8 is the compiler in the Android build toolchain that replaced ProGuard as the default. It performs shrinking, optimisation and renaming while it converts bytecode to DEX, and it reads ProGuard-compatible keep rules. When a release build type has minification enabled, R8 runs and writes a mapping file that translates renamed stack traces back to the original names.
A typical release configuration in a Kotlin build script looks like this:
android {
buildTypes {
release {
isMinifyEnabled = true // run R8: shrink, optimise, rename
isShrinkResources = true // drop unused resources
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
What that renaming leaves in place
- Kept names. Classes and members that are looked up by name, through reflection, serialisation or some SDKs, must keep their names through a keep rule.
- String literals. API endpoints, error messages and lookup keys are readable in the decompiled app.
- Native code. Libraries written in C or C++ are outside what R8 processes.
- Resources and assets. Resource shrinking can remove unused resources, but configuration files, bundled scripts and images that remain are packaged in readable form.
- Framework code. Dart code in Flutter apps and JavaScript or Hermes bytecode in React Native apps are separate artefacts.
- The running app. A shrinker has no runtime presence, so it cannot notice a debugger, a hook or a re-signed copy.
Keep rules deserve a closer look, because they decide how much of the renaming survives. A broad rule copied from a template can keep an entire package readable:
# Broad: keeps every class and member in the package readable
-keep class com.example.payments.** { *; }
# Narrow: keeps only what serialisation needs
-keepclassmembers class com.example.payments.model.** {
<fields>;
}
Renaming, hardening and runtime checks compared
| Control | Question it answers | Provided by a shrinker | What app shielding adds |
|---|---|---|---|
| Name obfuscation | Can an analyst read class and method names? | Yes, for Java and Kotlin code it processes | Renaming coordinated with your keep rules, plus protection of native and framework layers |
| String protection | Can an analyst read endpoints and keys? | No | Strings stored in protected form and decoded only when needed |
| Control-flow obfuscation | Can an analyst follow the logic? | No; optimisation may reshape code incidentally | Restructured branching in code you choose |
| Native and framework protection | Are C, C++, Dart and JavaScript layers readable? | No | Transformation of each layer on its own terms |
| Tamper detection | Has the app been modified or re-signed? | No | Integrity checks that fire at runtime and report to the backend |
| Runtime checks | Is the app under attack now? | No | Detection of hooking, debugging, emulators, root and jailbreak |
Counts rows of the comparison table on this page. A shrinker provides one of the six controls, and only for the Java and Kotlin code it processes.
What a shrinker gives you
- Smaller, faster release builds
- Unused code removed
- Short names for Java and Kotlin code it processes
- A mapping file for crash reports
What app shielding adds
- Protected strings, resources and assets
- Restructured control flow where you choose
- Native and framework code protection
- Tamper detection and runtime checks that report to your backend
When existing tooling is enough, and when it is not
R8 on its own can be a reasonable baseline when the app holds no secrets, shows only public content and relies on the server to enforce every rule. It stops being enough when any of these apply:
- the app moves money, changes account details or verifies identity;
- the logic or algorithms are intellectual property;
- repackaged, modded or cheat versions would hurt revenue or users;
- the app carries keys, tokens or endpoints you would rather not publish;
- a regulator or customer expects evidence of protection beyond the default build.
On iOS the question is different again. The toolchain compiles to native code and strips symbols on request but offers no protective renaming, and Objective-C selectors remain visible. iOS protection has to be scoped on its own terms, not inferred from the Android toolchain.
How Otukan extends the baseline
Otukan protects the parts a shrinker does not reach. It renames identifiers, protects strings, restructures control flow in the code you choose, protects configuration files, bundled scripts and assets, transforms native libraries and protects framework code, including Dart in Flutter apps and JavaScript or Hermes bytecode in React Native apps. Anti-tampering detects altered code and re-signed copies, and runtime checks detect hooking frameworks, debuggers, emulators, rooted Android devices and jailbroken iOS devices.
The control is covered on mobile code obfuscation. For the full product, see mobile app security.