- How to run Java programs directly on Android (without creating an APK)
- A (not so) simple Hello World program
- Setting up the working directory
- Compiling and dexing the Java class
- Creating the startup shellscript
- Installing and running the (non-) app
- It works, but how do I get a Context?!
- Follow up articles
- How to Run Java Code in Android Studio
How to run Java programs directly on Android (without creating an APK)
A step by step instruction for compiling a Java program into an Android executable and using ADB to run it.
When you want to create a system / commandline tool for Android, you have to write it in C(++)… or do you? TLDR; here’s the final proof of concept. Sticking with Java would have the benefit of avoiding all of the native ABI hassle and also being able to call into the Android runtime. So how do we do that?
A (not so) simple Hello World program
Let’s start with the Java program we want to run. In order to make it a bit more interesting (and because any useful program has dependencies), it won’t just print the obligatory “Hello World” message, but also use the Apache Commons CLI library to parse its commandline arguments:
package com.example; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; public class HelloWorld public static void main(String[] args) throws ParseException Option version = new Option("v", "Print version"); Option help = new Option("h", "Print help"); Options options = new Options(); options.addOption(help); options.addOption(version); for (Option opt : new DefaultParser().parse(options, args).getOptions()) if (opt.equals(version)) String os = System.getProperty("os.arch"); System.out.println("Hello World (" + os + ") v0.1"); > if (opt.equals(help)) new HelpFormatter().printHelp("Hello World", options); > > > >
Setting up the working directory
We will have to manually run several commandline tools in the next step, assuming the following final directory structure:
. ├── android-sdk-linux │ └── build-tools │ └── 23.0.2 │ └── dx ├── bin │ └── com │ └── example │ └── HelloWorld.class ├── lib │ └── commons-cli-1.3.1.jar ├── src │ └── com │ └── example │ └── HelloWorld.java ├── helloworld.jar └── helloworld.sh
- Android SDK (either via Android Studio or the SDK Manager). NOTE: If you are an Android developer, you’ll have the Android SDK already installed. In that case, you don’t actually need to copy it to the working directory as long as you know the path to the dx tool.
- Apache Commons CLI library v1.3.1
Afterwards copy&paste the HelloWorld code from above into the source folder. You might also find my semantic version parser class useful later on (not required here, though).
Compiling and dexing the Java class
Next step is to compile the java class (keep in mind that Android is stuck with Java 7 — bytecode for later versions won’t work). In case you are not used to doing this outside of an IDE, here’s the command:
javac -source 1.7 -target 1.7 -d bin -cp lib/commons-cli-1.3.1.jar src/com/example/HelloWorld.java
Make sure the program compiled properly:
java -cp lib/commons-cli-1.3.1.jar:bin com.example.HelloWorld -h usage: Hello world -h Print help -v Print version
Android cannot run Java class files directly. They have to be converted to Dalvik’s DEX format first (yes, even if you are using ART):
./android-sdk-linux/build-tools/23.0.2/dx --output=helloworld.jar --dex ./bin lib/commons-cli-1.3.1.jar
NOTE: Android Build Tools v28.0.2 and later contain a dx upgrade, called d8 . The d8 tool can process Java 8 class files. I’ll stick with dx for backwards compatibility reasons here.
Creating the startup shellscript
Android does not have a (normal) JRE, so JAR files cannot be started the same way as on a PC. You need a shellscript wrapper to do this. Copy&paste the one below to the workspace.
base=/data/local/tmp/helloworld export CLASSPATH=$base/helloworld.jar export ANDROID_DATA=$base mkdir -p $base/dalvik-cache exec app_process $base com.example.HelloWorld "$@"
NOTE: DEX files can also be started directly using the dalvikvm command, but going through app_process gives us a pre-warmed VM from the Zygote process (it is also the method employed by framework commands like pm and am ).
Installing and running the (non-) app
Time to push everything to the device:
adb shell mkdir -p /data/local/tmp/helloworld adb push helloworld.jar /data/local/tmp/helloworld adb push helloworld.sh /data/local/tmp/helloworld adb shell chmod 777 /data/local/tmp/helloworld/helloworld.sh
Moment of truth (fingers crossed):
adb shell /data/local/tmp/helloworld/helloworld.sh -v Hello World (armv7l) v0.1
NOTE: Since nothing was installed into the system, getting rid of the program is simply done by deleting the directory again.
It works, but how do I get a Context?!
Contexts represent an environment that is associated with an app (which we explicitly did not build) and are also device dependant. They can only be created by the ActivityThread class (a hidden system class that you cannot instantiate). If you want to interact with the Android runtime, you have to talk to the system services directly through their Binder interfaces. But that’s a topic for another article.
Follow up articles
How to Run Java Code in Android Studio
Along with Kotlin Android SDK runs with Java programming language. Whereas if you specifically need to run the java code in Android Studio, it is difficult to run without accessing any of Android building blocks like Activity or Services or Broadcast Receivers. However, you can run Java code within Android Studio by creating a separate module. So let’s learn how to run Java code in Android Studio.
Initially, we assume that you have already created an Android project with Android Studio IDE.
1. Create a new module by going to File –> New and hit on the New Module.
2. After hitting New Module, select Java Library and name it on the next screen. Further, finish it.
After creating a Java Library module it will be displayed in the Android Studio.
3. In the third step, you need to configure Android Studio to Run the Java module(created in the previous step). Hit on Edit Configurations… from the drop-down.
In the Run/Debug Configurations window, hit on the ‘+’ button from the top left corner. Later select Application from the drop-down and name it as ‘Java’.
4. While in the same Run/Debug Configurations window, provide the main class field with the class name along with the package name of the Java module.
Finally, you have created a Java module to run Java code and configured it. Afterward, we create a sample code in the MyClass and run it.
MyClass.java
package com.shadow.technos.javacodelib; public class MyClass < private static final String TAG = MyClass.class.getSimpleName(); public static void main(String[] args)< System.out.println("running "+TAG+" java code"); >>
Select java configuration and run it. Later Check Run tab in Android Studio to check the output of the code.