仅创建 JNI/NDK apk 命令行,无需 gradle、ant 或 cmake

Per*_*mes 6 android android-ndk apk

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

我现在还有什么?

我可以使用 ndk-build 成功构建我的项目,它会创建一个包含 .o 文件的“obj/”目录,但是现在呢?

方法是什么?

使用AAPT的zipalign

目标文件是我使用aapt所需的全部内容吗?

Per*_*mes 9

我什至给了 Gradle 一个机会,但我很不安,不知道他在做什么,决定把他放在一边,自己做来学习,就在这里。

教程构建java和JNI/NDK APK

1. 安装JDK8和android SDK with NDK, build-tools, platform-tools 和android platform 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"
Run Code Online (Sandbox Code Playgroud)

3. 创建项目目录和文件。

创建目录树:

$ mkdir -p  src/"${ORG_DIRS}" res/layout build/gen build/obj build/apk jni
Run Code Online (Sandbox Code Playgroud)

创建 ./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>
Run Code Online (Sandbox Code Playgroud)

创建 ./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>
Run Code Online (Sandbox Code Playgroud)

创建 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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

4. 使用Android Asset Packaging Tool (aapt) 生成R.java 文件。

$ "${BUILD_TOOLS}/aapt" package -f -m -J build/gen/ -S res \
       -M AndroidManifest.xml -I "${PLATFORM}/android.jar"
Run Code Online (Sandbox Code Playgroud)

-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
Run Code Online (Sandbox Code Playgroud)

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

6. 用dx工具将build/obj/中的.class文件翻译成Dalvik字节码。

$ "${BUILD_TOOLS}/dx" --dex --output=build/apk/classes.dex build/obj/
Run Code Online (Sandbox Code Playgroud)

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/
Run Code Online (Sandbox Code Playgroud)

8.在APK中使用zipalign工具。

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

$ "${BUILD_TOOLS}/zipalign" -f -p 4 \
      build/"${APK_NAME}".unsigned.apk build/"${APK_NAME}".aligned.apk
Run Code Online (Sandbox Code Playgroud)

9. 创建用于使用 Java 密钥工具签名的密钥库和密钥。

$ keytool -genkeypair -keystore keystore.jks -alias androidkey \
      -validity 10000 -keyalg RSA -keysize 2048
Run Code Online (Sandbox Code Playgroud)

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

$ "${BUILD_TOOLS}/apksigner" sign --ks keystore.jks \
      --ks-key-alias androidkey --out build/"${APK_NAME}".apk \
      build/"${APK_NAME}".aligned.apk
Run Code Online (Sandbox Code Playgroud)

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

$ "${SDK}/platform-tools/adb" install -r build/"${APK_NAME}".apk
$ "${SDK}/platform-tools/adb" shell am start -n "${PACKAGE_NAME}"/.MainActivity
Run Code Online (Sandbox Code Playgroud)

您可以使用adb logcatbeforeadb shell...进行调试。

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

12、更改MainActivity.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());
    }
}
Run Code Online (Sandbox Code Playgroud)

重新编译:

$ 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
Run Code Online (Sandbox Code Playgroud)

并再次转换为 Dalvik 字节码:

$ "${BUILD_TOOLS}/dx" --dex --output=build/apk/classes.dex build/obj/
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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]);
}
Run Code Online (Sandbox Code Playgroud)

15. 使用 arm 工具链创建 libhello.so

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

$ mkdir -p build/apk/lib/armeabi-v7a
Run Code Online (Sandbox Code Playgroud)

构建 libhello.so:

$ ${ANDROID_TOOLCHAIN} -shared -o build/apk/lib/armeabi-v7a/libhello.so jni/hello.c
Run Code Online (Sandbox Code Playgroud)

16.再次打包APK。

$ "${BUILD_TOOLS}/aapt" package -f -M AndroidManifest.xml -S res/ \
      -I "${PLATFORM}/android.jar" \
      -F build/"${APK_NAME}".unsigned.apk build/apk/
Run Code Online (Sandbox Code Playgroud)

17. 再次压缩 APK。

$ "${BUILD_TOOLS}/zipalign" -f -p 4 \
      build/"${APK_NAME}".unsigned.apk build/"${APK_NAME}".aligned.apk
Run Code Online (Sandbox Code Playgroud)

18.再次签署APK。

$ "${BUILD_TOOLS}/apksigner" sign --ks keystore.jks \
      --ks-key-alias androidkey --out build/"${APK_NAME}".apk \
      build/"${APK_NAME}".aligned.apk
Run Code Online (Sandbox Code Playgroud)

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

与 aapt:

$ "${BUILD_TOOLS}/aapt" list build/"${APK_NAME}".apk
Run Code Online (Sandbox Code Playgroud)

或者更详细地说明:

$ "${BUILD_TOOLS}/aapt" list -v build/"${APK_NAME}".apk
Run Code Online (Sandbox Code Playgroud)

或用罐子:

$ jar tf build/"${APK_NAME}"
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

--activity-clear-top是干净的避免警告的任务 “警告:尚未开始活动,其当前的任务已经放到了前面”

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

  • 感谢分享本教程。我和您一样想要了解 Gradle 等一体化工具背后的原理。 (3认同)
  • 这是纯金。感谢您的指导。 (2认同)
  • 谢谢你!我会尝试看看 2023 年它的表现如何。 (2认同)