创建 JNI/NDK apk only 命令行,不带 gradle、ant 或 cmake



我创建了一个只使用 JNI 和NDK 的 android 应用程序,除了连接我的代码与 JNI 所需的 Java 代码之外,没有使用 Java 代码,因为是一个使用 opengl es 的应用程序,它是使用 Linux 和 android 的一个代码。但是我不明白如何在没有许多我不希望它咬我的东西的情况下创建一个apk文件。所以,我尝试创建一个没有gradle,ant,android-studio的应用程序......也只有命令行。

我现在还剩下什么?

我可以成功地使用 ndk-build 构建我的项目,它创建了一个带有文件 .o 的"obj/"目录,但是现在呢?

有什么办法?

使用aapt并在拉链对齐后?

目标文件都是我需要使用的吗?

我什至给了格拉德尔一个机会,但我不知道他在做什么,我感到不安,决定把他放在一边,自己动手学习,就在这里。

教程构建 java 和 JNI/NDK APK

1. 使用 NDK、构建工具安装 JDK8 和安卓 SDK, 平台工具和安卓平台 23.

2. 设置环境变量。

$ 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. 创建项目目录和文件。

创建目录树:

$ mkdir -p  src/"${ORG_DIRS}" res/layout build/gen build/obj build/apk jni

创建 ./AndroidManifest.xml 文件:

<?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>

创建 ./res/layout/main.xml 文件:

<?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>

创建 src/some/some/some/MainActivity.java 文件:

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. 使用安卓资产打包工具(aapt(生成R.java文件。

$ "${BUILD_TOOLS}/aapt" package -f -m -J build/gen/ -S res 
-M AndroidManifest.xml -I "${PLATFORM}/android.jar"

-f标志用于覆盖任何现有的输出文件。

-m导致它在输出目录下创建包目录。

-J使其生成 R.java 文件并设置输出目录。

-S指出资源目录。-M 指定清单。

-I将平台.jar添加为"包含文件"。

这一切都创造了:build/gen/"${ORG_DIRS}"/R.java。

5. 使用 javac 编译 java 文件。

$ 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

如果看到有关 JDK 版本的编译错误,请尝试使用-source 1.7 -target 1.7javac命令中的标记。

6. 使用 dx 工具将构建/obj/中的.class文件转换为 Dalvik 字节码。

$ "${BUILD_TOOLS}/dx" --dex --output=build/apk/classes.dex build/obj/

7. 打包以再次使用 aapt 工具创建 APK。

$ "${BUILD_TOOLS}/aapt" package -f -M AndroidManifest.xml -S res/ 
-I "${PLATFORM}/android.jar" 
-F build/"${APK_NAME}".unsigned.apk build/apk/

8.在APK中使用拉链工具。

它用于在 4 字节边界上对齐 APK 中的未压缩文件 以便更轻松地进行内存映射。

$ "${BUILD_TOOLS}/zipalign" -f -p 4 
build/"${APK_NAME}".unsigned.apk build/"${APK_NAME}".aligned.apk

9. 创建用于使用 Java 密钥工具进行签名的密钥存储和密钥。

$ keytool -genkeypair -keystore keystore.jks -alias androidkey 
-validity 10000 -keyalg RSA -keysize 2048

10. 使用 apksigner 工具对 APK 进行签名。

$ "${BUILD_TOOLS}/apksigner" sign --ks keystore.jks 
--ks-key-alias androidkey --out build/"${APK_NAME}".apk 
build/"${APK_NAME}".aligned.apk

11. 使用 adb 工具测试您的应用。

$ "${SDK}/platform-tools/adb" install -r build/"${APK_NAME}".apk
$ "${SDK}/platform-tools/adb" shell am start -n "${PACKAGE_NAME}"/.MainActivity

可以在adb shell...之前使用adb logcat进行调试。

在第 11 步之前,您从 Java 代码构建了 APK。现在让我们看看 如何制作 JNI/NDK 代码的 APK。

12. 更改主活动.java文件,然后重新编译并再次翻译。

更改 src/some/some/some/MainActivity.java 文件:

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());
}
}

重新 编译:

$ 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

并再次翻译成达尔维克字节码:

$ "${BUILD_TOOLS}/dx" --dex --output=build/apk/classes.dex build/obj/

13. 使用 javah 工具搜索与 Java 方法对应的 C 函数签名。

$ 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. 创建 hello.c 文件。

#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. 使用 arm 工具链创建 libhello.so

首先,创建 build/apk/lib/armeabi-v7a 目录:

$ mkdir -p build/apk/lib/armeabi-v7a

构建 libhello.so:

$ ${ANDROID_TOOLCHAIN} -shared -o build/apk/lib/armeabi-v7a/libhello.so jni/hello.c

16. 再次打包 APK。

$ "${BUILD_TOOLS}/aapt" package -f -M AndroidManifest.xml -S res/ 
-I "${PLATFORM}/android.jar" 
-F build/"${APK_NAME}".unsigned.apk build/apk/

17. 再次压缩 APK。

$ "${BUILD_TOOLS}/zipalign" -f -p 4 
build/"${APK_NAME}".unsigned.apk build/"${APK_NAME}".aligned.apk

18. 再次对 APK 进行签名。

$ "${BUILD_TOOLS}/apksigner" sign --ks keystore.jks 
--ks-key-alias androidkey --out build/"${APK_NAME}".apk 
build/"${APK_NAME}".aligned.apk

19.使用aapt工具或jar工具检查APK内容。

使用 AAPT:

$ "${BUILD_TOOLS}/aapt" list build/"${APK_NAME}".apk

或提供更多详细信息:

$ "${BUILD_TOOLS}/aapt" list -v build/"${APK_NAME}".apk

或带罐子:

$ jar tf build/"${APK_NAME}"

20. 再次使用 adb 工具测试您的应用。

$ "${SDK}/platform-tools/adb" install -r build/"${APK_NAME}".apk
$ "${SDK}/platform-tools/adb" shell am start --activity-clear-top -n "${PACKAGE_NAME}"/.MainActivity

--activity-clear-top用于清理任务,避免警告"警告:活动未启动,其当前任务已变为 前线"。

本教程基于: https://www.hanshq.net/command-line-android.html

最新更新