I'm created one app to android that use only the JNI and NDK, without use Java code beyond what is necessary for the connection my code with the JNI, because is one application that use opengl es, it's to use one code for linux and android. But I don't understand how to create one apk file without the many things that I don't want it bite me for now. So, I try create one app without gradle, ant, android-studio... Only command line as well.
What Am I have still now?
I can build my project with ndk-build with success and it create one "obj/" directory with files .o, but and now?
What is the way?
Use aapt and after zipalign?
Are the object files all what I need to use aapt?
To have the ndk-build script build an application, first create a project folder. In the folder, create a folder named jni. In the jni folder, place the Android.mk file, Application.mk file, and source files. Then navigate to the project folder in a terminal and execute ndk-build.
JNI is just the way that Java handles calling into native/C++ code, and calling back into Java from there. It has nothing to say about Android - it is a Java language feature. The Android NDK is a way to write Android applications using code called by JNI.
You'll need to point to your NDK in your eclipse by adding the path of ndk-build to Window > preferences > android > NDK. Right click on your project folder. Choose android tools -> add native support (the bottom one) and click finish. Now it will ask for a name for your .
I even gave Gradle a chance, but I was disturbed not to know what he was doing and decided to put him aside and do it myself to learn, here it is.
1. Install JDK8 and the android SDK with NDK, build-tools, platform-tools and the android platform 23.
2. Set the environment variables.
$ export SDK="${HOME}/Programs/Android" \
export BUILD_TOOLS="${SDK}/build-tools/29.0.2" \
export PLATFORM="${SDK}/platforms/android-23" \
export ANDROID_API=23 \
export APK_NAME="APKName" \
export PACKAGE_NAME="some.some.some" \
export ORG_DIRS="${PACKAGE_NAME//./\/}" \
export NDK="${SDK}/ndk-bundle"
export ANDROID_TOOLCHAIN="${NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi23-clang"
3. Create project directories and files.
Create directories tree:
$ mkdir -p src/"${ORG_DIRS}" res/layout build/gen build/obj build/apk jni
Create ./AndroidManifest.xml file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="some.some.some"
versionCode="1"
versionName="0.1">
<uses-sdk android:minSdkVersion="23"/>
<application android:label="Hello">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
Create ./res/layout/main.xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/my_text"/>
</LinearLayout>
Create src/some/some/some/MainActivity.java file:
package some.some.some;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView text = (TextView)findViewById(R.id.my_text);
text.setText("Hello, world!");
}
}
4. Generate R.java file with Android Asset Packaging Tool (aapt).
$ "${BUILD_TOOLS}/aapt" package -f -m -J build/gen/ -S res \
-M AndroidManifest.xml -I "${PLATFORM}/android.jar"
The -f
flag serves to aapt to overwrite any existing output file.
The -m
causes it to create package directories under the output directory.
The -J
makes it generate the R.java file and sets the output directory.
The -S
points out the resource directory. The -M specifies the manifest.
The -I
adds the platform .jar as an "include file".
It all creates: build/gen/"${ORG_DIRS}"/R.java.
5. Compile the java files with javac.
$ javac -bootclasspath "${JAVA_HOME}/jre/lib/rt.jar" \
-classpath "${PLATFORM}/android.jar" -d build/obj \
build/gen/"${ORG_DIRS}"/R.java src/"${ORG_DIRS}"/MainActivity.java
If you see compile errors about JDK version, try use the
-source 1.7 -target 1.7
tags in javac
command.
6. Translate the .class files in build/obj/ to Dalvik byte code with dx tool.
$ "${BUILD_TOOLS}/dx" --dex --output=build/apk/classes.dex build/obj/
7. Package to create APK using the aapt tool again.
$ "${BUILD_TOOLS}/aapt" package -f -M AndroidManifest.xml -S res/ \
-I "${PLATFORM}/android.jar" \
-F build/"${APK_NAME}".unsigned.apk build/apk/
8. Using zipalign tool in the APK.
It serves to aligns uncompressed files in the APK on 4-byte boundaries for easier memory mapping.
$ "${BUILD_TOOLS}/zipalign" -f -p 4 \
build/"${APK_NAME}".unsigned.apk build/"${APK_NAME}".aligned.apk
9. Create a key store and key for signing with the Java keytool.
$ keytool -genkeypair -keystore keystore.jks -alias androidkey \
-validity 10000 -keyalg RSA -keysize 2048
10. Sign the APK with apksigner tool.
$ "${BUILD_TOOLS}/apksigner" sign --ks keystore.jks \
--ks-key-alias androidkey --out build/"${APK_NAME}".apk \
build/"${APK_NAME}".aligned.apk
11. Test your app with the adb tool.
$ "${SDK}/platform-tools/adb" install -r build/"${APK_NAME}".apk
$ "${SDK}/platform-tools/adb" shell am start -n "${PACKAGE_NAME}"/.MainActivity
You can use adb logcat
before adb shell...
to debug.
Until the step 11 you built the APK from your java code. Now let's see how to make a APK of the JNI/NDK code.
12. Change the MainActivity.java file and recompile and translate again.
Change the src/some/some/some/MainActivity.java file:
package some.some.some;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
static {
System.loadLibrary("hello");
}
public native String getMessage();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView text = (TextView)findViewById(R.id.my_text);
text.setText(getMessage());
}
}
Recompile:
$ javac -bootclasspath "${JAVA_HOME}/jre/lib/rt.jar" \
-classpath "${PLATFORM}/android.jar" -d build/obj \
build/gen/"${ORG_DIRS}"/R.java src/"${ORG_DIRS}"/MainActivity.java
And Translate to Dalvik byte code again:
$ "${BUILD_TOOLS}/dx" --dex --output=build/apk/classes.dex build/obj/
13. Search the C function signature that corresponds to the Java method with javah tool.
$ javah -classpath "${PLATFORM}/android.jar:build/obj" \
-o /tmp/jni.h "${PACKAGE_NAME}".MainActivity
$ grep -A1 _getMessage /tmp/jni.h
JNIEXPORT jstring JNICALL Java_some_some_some_MainActivity_getMessage
(JNIEnv *, jobject);
14. Create hello.c file.
#include <stdlib.h>
#include <jni.h>
#include <time.h>
static const char *const messages[] = {
"Hello, world!",
"Hej världen!",
"Bonjour, monde!",
"Hallo Welt!"
};
JNIEXPORT jstring JNICALL
Java_net_hanshq_hello_MainActivity_getMessage(JNIEnv *env, jobject obj) {
int i;
srand(time(NULL));
i = rand() % (sizeof(messages) / sizeof(messages[0]));
return (*env)->NewStringUTF(env, messages[i]);
}
15. Create the libhello.so with arm toolchain
First, create the build/apk/lib/armeabi-v7a directory:
$ mkdir -p build/apk/lib/armeabi-v7a
Build the libhello.so:
$ ${ANDROID_TOOLCHAIN} -shared -o build/apk/lib/armeabi-v7a/libhello.so jni/hello.c
16. Package the APK again.
$ "${BUILD_TOOLS}/aapt" package -f -M AndroidManifest.xml -S res/ \
-I "${PLATFORM}/android.jar" \
-F build/"${APK_NAME}".unsigned.apk build/apk/
17. Zipalign the APK again.
$ "${BUILD_TOOLS}/zipalign" -f -p 4 \
build/"${APK_NAME}".unsigned.apk build/"${APK_NAME}".aligned.apk
18. Sign the APK again.
$ "${BUILD_TOOLS}/apksigner" sign --ks keystore.jks \
--ks-key-alias androidkey --out build/"${APK_NAME}".apk \
build/"${APK_NAME}".aligned.apk
19. Check the APK content with aapt tool or jar tool.
with aapt:
$ "${BUILD_TOOLS}/aapt" list build/"${APK_NAME}".apk
or aapt with more details:
$ "${BUILD_TOOLS}/aapt" list -v build/"${APK_NAME}".apk
or with jar:
$ jar tf build/"${APK_NAME}"
20. Test your app with the adb tool again.
$ "${SDK}/platform-tools/adb" install -r build/"${APK_NAME}".apk
$ "${SDK}/platform-tools/adb" shell am start --activity-clear-top -n "${PACKAGE_NAME}"/.MainActivity
The --activity-clear-top
is for clean the task avoiding the warning
"Warning: Activity not started, its current task has been brought to
the front".
This tutorial was based on: https://www.hanshq.net/command-line-android.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With