首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Android NDK 開發實戰(二):完整案例與常見錯誤處理指南

     編輯:關於C語言

核心概念補充

  • 編譯→連結→交叉編譯:源碼 .c → 目標檔 .o → 可執行檔。交叉編譯在宿主機(如 x86 Linux)產出目標機(如 ARM Android)二進制,需工具鏈模擬目標環境。
  • NDK 工具鏈:提供 arm-linux-androideabi-gcc 等編譯器、libc、libm、liblog 等庫。

標準 NDK 開發流程(6 步驟)

  1. 建立 Android 專案。
  2. Java/Kotlin 宣告 native 方法。
  3. 建立 jni/ 目錄,撰寫 C/C++ 實作(函數命名規則:Java_包名_類名_方法名)。
  4. 編寫 Android.mk(或 CMakeLists.txt)。
  5. 執行 ndk-build 生成 .so 動態庫。
  6. Java 端 System.loadLibrary 載入並呼叫。

完整 Hello World 範例

Java 端

public class DemoActivity extends Activity {
    static { System.loadLibrary("Hello"); }
    public native String helloFromJNI();
    public void onClick(View v) {
        Toast.makeText(this, helloFromJNI(), Toast.LENGTH_SHORT).show();
    }
}

C 端 (Hello.c)

#include <stdio.h>
#include <jni.h>

jstring Java_cn_itcast_ndk_DemoActivity_helloFromC(JNIEnv *env, jobject obj) {
    return (*env)->NewStringUTF(env, "hello from c");
}

Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := Hello
LOCAL_SRC_FILES := Hello.c
include $(BUILD_SHARED_LIBRARY)

進階:javah 產生頭檔

# 1. 編譯 Java
javac -d bin/classes src/com/.../DemoActivity.java
# 2. 產生 .h
cd bin/classes
javah com.example.DemoActivity
# 產生 com_example_DemoActivity.h,複製到 jni/ 內含引用

常見錯誤與解決方案(含 Logcat 除錯)

錯誤訊息原因解決
Android NDK: Your APP_BUILD_SCRIPT points to an unknown fileAndroid.mk 路徑錯誤或遺失確認 jni/Android.mk 存在,ndk-build 在專案根目錄執行
jni/Android.mk:4: *** missing separatorMakefile 語法錯誤(縮排必須用 Tab)檢查每行命令前為 Tab,非空格
make: *** [obj/.../Hello.o] Error 1C 語法錯誤查看第一個 error 訊息,修正對應行
UnsatisfiedLinkError: Library Hello not found.so 未生成或名稱不符確認 LOCAL_MODULE 名稱,loadLibrary 不含 "lib" 前綴與 ".so" 後綴
UnsatisfiedLinkError: hello_from_cnative 方法找不到對應 C 函數檢查函數簽名是否與 javah 產生的 .h 一致
Build fingerprint: ... signal 11 (SIGSEGV)C 代碼邏輯錯(空指標、越界、類型不符)用 __android_log_print 定位崩潰行,或用 ndk-gdb 除錯

Logcat 除錯技巧

Android.mk 加入 log 库

LOCAL_LDLIBS += -llog

C 代碼定義巨集

#include <android/log.h>
#define LOG_TAG "MyTag"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 使用
LOGD("x=%d", x); LOGI("init done");

中文亂碼處理

  • NDK r6+:源碼儲存為 UTF-8,編譯鏈預設 UTF-8,Java 端直接 NewStringUTF 即可。
  • NDK r4 舊版:編碼為 ISO-8859-1,Java 端需 new String(str.getBytes("ISO-8859-1"), "UTF-8") 轉碼。

Android.mk 關鍵變數詳解

變數說明
LOCAL_PATH$(call my-dir) 取得當前目錄
include $(CLEAR_VARS)重置所有 LOCAL_* 變數(除 LOCAL_PATH)
LOCAL_MODULE模組名,生成 lib<name>.so
LOCAL_SRC_FILES源檔列表,相對路徑
LOCAL_LDLIBS連結庫,如 -llog -lz
include $(BUILD_SHARED_LIBRARY)產出動態庫 .so
include $(BUILD_STATIC_LIBRARY)產出靜態庫 .a

靜態庫 vs 動態庫選擇

  • 動態庫:體積小、載入時解析符號、支援插件架構。
  • 靜態庫:編譯期鏈入、無執行期相依、適合發布獨立模組給第三方。

資料傳遞:Java ↔ C 完整範例

Java 宣告

public class DataProvider {
    public native int add(int x, int y);
    public native String sayHelloInC(String s);
    public native int[] intMethod(int[] arr);
}

C 實作重點

  • jint 直接對應 int,無需轉換。
  • jstring → char*:需呼叫 GetStringUTFChars 或自行 GetByteArrayElements 轉碼。
  • jintArray:GetIntArrayElements 取得指標,修改後自動反映回 Java 陣列(釋放時模式 0 為複製回)。

C 反向呼叫 Java

  1. 取得類引用:jclass cls = (*env)->FindClass(env, "pkg/Class");
  2. 取得方法 ID:jmethodID mid = (*env)->GetMethodID(env, cls, "methodName", "(Ljava/lang/String;)V");(簽名用 javap -s 取得)。
  3. 建立實例(非靜態):jobject obj = (*env)->AllocObject(env, cls);
  4. 呼叫:(*env)->CallVoidMethod(env, obj, mid, (*env)->NewStringUTF(env, "from c"));

靜態方法呼叫

用 GetStaticMethodID + CallStaticVoidMethod,無需 AllocObject,直接傳 cls。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved