A comprehensive Android library for converting Java source code to Smali assembly language. Java2Smali provides a complete pipeline for transforming Java files into human-readable Smali code through compilation, dexing, and disassembly.
I made this for my other project ByteHaven and decided to distribute it for others to use because I couldnt find any other libararies like this that works on Android, and I made it easy to use.
Java2Smali orchestrates the complete transformation pipeline:
- Compilation: Java source files → .class files (using javac)
- Dexing: .class files → .dex files (using D8 or R8)
- Disassembly: .dex files → .smali files (using baksmali)
- Dual Dexing Options: Support for both D8 (fast) and R8 (with optimization and obfuscation)
- Classpath Management: Separate boot classpath (android.jar) and user classpath support
- ProGuard Integration: Configurable ProGuard rules for R8 optimization
- Status Callbacks: Real-time status updates for UI integration
- Partial Pipeline Execution: Run individual stages (compile-only, compile+dex, etc.)
- Multidex Support: Automatic handling of multiple DEX files
- Thread-Safe: Asynchronous execution with proper concurrency controls
- Android API Level 26 or higher
- Android Gradle Plugin 7.0+
- Java 8 or higher
Add the dependency to your module's build.gradle:
dependencies {
implementation("io.github.tooldroidyt:java2smali:1.1.1")
}1. Instantiate Java2Smali
// Instantiate Java2Smali
Java2Smali java2Smali = new Java2Smali(this);2. Adding Java Sources
// Add individual Java source files
java2Smali.setSourceFiles(new File("path/to/Main.java"), new File("path/to/Example.java"));
// Or provide a source directory (recursively discovers .java files)
java2Smali.setSourceFiles(new File("path/to/src"));
// Set output directory for generated Smali files
java2Smali.setOutputFolder(new File("/output"));
// Set temporary working directory for intermediate build artifacts
java2Smali.setTempFolder(new File("/.build"));3. Adding Dependencies
Files you might need (boot classes):
- android.jar: needed when you use Android java api (eg. android.app.Activity)
- core-lambda-stubs.jar: needed when the source you're gonna compile contains lambda syntax
// Boot classpath (e.g., android.jar): used for compilation only; not packaged
java2Smali.setBootClassPath(new File("android.jar"), new File("core-lambda-stubs.jar"));
// Alternatively, provide a directory containing framework jars
java2Smali.setBootClassPath(new File("path/to/folder/bootclasses"));
// Application classpath (e.g., mylibrary.jar): included in dex/smali output
java2Smali.setClassPath(new File("mylibrary.jar"), new File("morelibraries.jar"));
// Alternatively, provide a directory containing dependency jars
java2Smali.setClassPath(new File("path/to/folder/libraries"));4. R8 Configuration
// Optional: custom ProGuard/R8 rules file
File proguardFile = new File("proguard-rules.pro");
java2Smali.useR8(
true, // enable R8 (use D8 when false)
true, // release mode (enables optimizations)
true, // allow code minification/shrinking
proguardFile // ProGuard/R8 rules file (null to use defaults)
);5. Listeners
// Listen to pipeline status updates (e.g., STARTING, COMPILING, DEXING)
java2Smali.setOnStatusListener(new OnStatusCallback() {
@Override
public void onStatus(Status status, String message) {
Log.i("TAG", message);
}
});
// Handle errors during compilation/dexing/disassembly
java2Smali.setOnErrorListener(new OnErrorCallback() {
@Override
public void onError(Exception exception) {
// TODO: Handle error (e.g., show a message or retry)
}
});
// Handle successful completion and use the output folder
java2Smali.setOnFinishListener(new OnFinishCallback() {
@Override
public void onFinish(File outputFolder) {
// TODO: Use outputFolder (e.g., display path or open folder)
}
});
// Run the full pipeline
java2Smali.start();// Instantiate Java2Smali
Java2Smali java2Smali = new Java2Smali(this);
// Add individual Java source files
java2Smali.setSourceFiles(new File("path/to/Main.java"), new File("path/to/Example.java"));
// Or provide a source directory (recursively discovers .java files)
java2Smali.setSourceFiles(new File("path/to/src"));
// Set output directory for generated Smali files
java2Smali.setOutputFolder(new File("/output"));
// Set temporary working directory for intermediate build artifacts
java2Smali.setTempFolder(new File("/.build"));
// Boot classpath (e.g., android.jar): used for compilation only; not packaged
java2Smali.setBootClassPath(new File("android.jar"), new File("core-lambda-stubs.jar"));
// Alternatively, provide a directory containing framework jars
java2Smali.setBootClassPath(new File("path/to/folder/bootclasses"));
// Application classpath (e.g., mylibrary.jar): included in dex/smali output
java2Smali.setClassPath(new File("mylibrary.jar"), new File("morelibraries.jar"));
// Alternatively, provide a directory containing dependency jars
java2Smali.setClassPath(new File("path/to/folder/libraries"));
// Optional: custom ProGuard/R8 rules file
File proguardFile = new File("proguard-rules.pro");
java2Smali.useR8(
true, // enable R8 (use D8 when false)
true, // release mode (enables optimizations)
true, // allow code minification/shrinking
proguardFile // ProGuard/R8 rules file (null to use defaults)
);
// Listen to pipeline status updates (e.g., STARTING, COMPILING, DEXING)
java2Smali.setOnStatusListener(new OnStatusCallback() {
@Override
public void onStatus(Status status, String message) {
Log.i("TAG", message);
}
});
// Handle errors during compilation/dexing/disassembly
java2Smali.setOnErrorListener(new OnErrorCallback() {
@Override
public void onError(Exception exception) {
// TODO: Handle error (e.g., show a message or retry)
}
});
// Handle successful completion and use the output folder
java2Smali.setOnFinishListener(new OnFinishCallback() {
@Override
public void onFinish(File outputFolder) {
// TODO: Use outputFolder (e.g., display path or open folder)
}
});
// Run the full pipeline
java2Smali.start();Boot Classpath - Framework libraries used as references (not included in output):
android.jarcore-lambda-stubs.jar- Other Android framework JARs
Classpath - Application dependencies (included in output):
- Your library JARs
- Third-party dependencies (Gson, OkHttp, etc.)
- Custom libraries
When using libraries like Gson that reference optional annotation dependencies (Error Prone, JSR-305, etc.), Java2Smali automatically suppresses warnings. To disable:
java2smali.setIgnoreAnnotationWarnings(false);This adds the following ProGuard rules automatically:
-dontwarn com.google.errorprone.annotations.**
-dontwarn javax.annotation.**
-dontwarn org.checkerframework.**
-dontwarn org.codehaus.mojo.animal_sniffer.**
-dontwarn edu.umd.cs.findbugs.annotations.**
Creates a compiler instance with minimal configuration. Use setter methods to configure.
| Method | Description |
|---|---|
setSourceFiles(File... source) |
Sets Java source files or directories to compile |
setOutputFolder(File outputFolder) |
Sets the output directory for smali files |
setBootClassPath(File... jars) |
Sets boot classpath (framework JARs) |
setClassPath(File... jars) |
Sets user classpath (dependency JARs) |
setTempFolder(File tempFolder) |
Sets temporary directory for build artifacts |
useR8(boolean useR8, boolean release, boolean allowMinification, File proguardRules) |
Configures R8 dexing options |
setIgnoreAnnotationWarnings(boolean ignore) |
Controls annotation warning suppression |
| Method | Description |
|---|---|
setOnStatusListener(OnStatusCallback callback) |
Sets callback for status updates |
setOnFinishListener(OnFinishCallback callback) |
Sets callback for successful completion |
setOnErrorListener(OnErrorCallback callback) |
Sets callback for error handling |
| Method | Description |
|---|---|
start() |
Runs complete pipeline: Compile → Dex → Disassemble → Cleanup |
startUntilCompiling() |
Runs: Compile only |
startUntilDexing() |
Runs: Compile → Dex |
startUntilBackSmali() |
Runs: Compile → Dex → Disassemble (no cleanup) |
public enum Status {
STARTING, // Pipeline initialization
COMPILING, // Java → .class
DEXING, // .class → .dex
BAKSMALI, // .dex → .smali
CLEANING, // Removing temporary files
FINISHED, // Complete success
ERROR // Fatal error occurred
}Ensure you have javac available. On Android, the library includes an embedded javac.
Enable annotation warning suppression:
compiler.setIgnoreAnnotationWarnings(true);Check your ProGuard rules. Try disabling minification:
compiler.useR8(true, true, false, null);Verify your source files are valid Java and the paths are correct:
File[] sources = compiler.getSourceFiles();
Log.d(TAG, "Found " + sources.length + " source files");┌─────────────────┐
│ Java Sources │
│ (.java) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ javac │
│ (Compilation) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Class Files │
│ (.class) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ D8 or R8 │
│ (Dexing) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ DEX Files │
│ (.dex) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ baksmali │
│ (Disassembly) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Smali Files │
│ (.smali) │
└─────────────────┘
- Android Tools R8: DEX compilation and optimization
- Smali/Baksmali: DEX disassembly to Smali format
- DexLib2: DEX file parsing and manipulation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
For issues, questions, or suggestions:
- Open an issue on GitHub
- Contact: notlifetechlolv2@gmail.com
- Telegram: ByteHaven Pro
- Inspired by Java2Smali by Belluxx
- Android Open Source Project for R8/D8
- JesusFreke for Smali/Baksmali
- All contributors to this project