Android筆記之JNI學習

Android筆記之JNI學習

交叉編譯

  • 在一個平臺上去編譯另一個平臺上可以執行的本地代碼
  • cpu平臺 arm x86 mips
  • 操作系統平臺 windows linux mac os
  • 原理 模擬不同平臺的特性去編譯代碼

jni開發工具

  • ndk native develop kit
  • ndk目錄
    • docs 幫助文檔
    • platforms 好多平臺版本文件夾 選擇時選擇項目支持的最小版本號對應的文件夾
    • 每一個版本號的文件夾中放了 不同cpu架構的資源文件
    • include文件夾 jni開發中常用的 .h頭文件
    • lib 文件夾 google打包好的 提供給開發者使用的 .so文件
    • samples google官方提供的樣例工程 可以參考進行開發
    • android-ndk-r9d\build\tools linux系統下的批處理文件 在交叉編譯時會自動調用
    • ndk-build 交叉編譯的命令
  • cdt eclipse的插件 高亮C代碼 C的代碼提示

jnihelloworld

  • jni開發的步驟

  • ①寫java代碼 聲明本地方法 用到native關鍵字 本地方法不用去實現

  • ②項目根目錄下創建jni文件夾

  • ③在jni文件夾下創建.c文件

    • 本地函數命名規則: Java_包名_類名_本地方法名
    • JNIENV* env JNIEnv 是JniNativeInterface這個結構體的一級指針
    • JniNativeInterface這個結構體定義了大量的函數指針
    • env 就是結構體JniNativeInterface這個結構體的二級指針
    • (*env)->調用結構體中的函數指針
    • 第二個參數jobject 調用本地函數的java對象就是這個jobject
  • ④ 導入<jni.h>

  • 創建Android.mk makefile 告訴編譯器.c的源文件在什麼地方,要生成的編譯對象的名字是什麼

    LOCAL_PATH := $(call my-dir)

    include $(CLEAR_VARS)

    LOCAL_MODULE := hello #指定了生成的動態鏈接庫的名字
    LOCAL_SRC_FILES := hello.c #指定了C的源文件叫什麼名字

    include $(BUILD_SHARED_LIBRARY)

  • ⑥ 調用ndk-build編譯c代碼生成動態鏈接庫.so文件 文件的位置 lib->armeabi->.so

  • ⑦ 在java代碼中加載動態鏈接庫 System.loadlibrary(“動態鏈接庫的名字”); Android.mkLOCAL_MODULE所指定的名字

jni開發中的常見錯誤

  • java.lang.UnsatisfiedLinkError: Native method not found: 本地方法沒有找到
    • 本地函數名寫錯
    • 忘記加載.so文件 沒有調用System.loadlibrary
  • findLibrary returned null
    • System.loadLibrary(“libhello”); 加載動態鏈接庫時 動態鏈接庫名字寫錯
    • 平臺類型錯誤 把只支持arm平臺的.so文件部署到了 x86cpu的設備上
      • 在jni目錄下創建 Application.mk 在裏面指定
      • APP_ABI := armeabi
        APP_PLATFORM := android-14
  • javah
    • jdk 1.7 項目 src目錄下運行javah
    • jdk 1.6 項目 bin目錄下 classes文件夾
    • javah native方法聲明的java類的全類名

jni簡便開發流程

  • ① 寫java代碼 native 聲明本地方法
  • ② 添加本地支持 右鍵單擊項目->andorid tools->add native surport
    • 如果發現 finish不能點擊需要給工作空間配置ndk目錄的位置
    • window->preferences->左側選擇android->ndk 把ndk解壓的目錄指定進來
  • ③ 如果寫的是.c的文件 先修改一下生成的.cpp文件的擴展名 不要忘了 相應修改Android.mk文件中LOCAL_SRC_FILES的值
  • ④ javah生成頭文件 在生成的頭文件中拷貝c的函數名到.c的文件
  • ⑤ 解決CDT插件報錯的問題
  • 右鍵單擊項目選擇 properties 選測 c/c++ general->paths and symbols->include選項卡下->點擊add…->file system 選擇ndk目錄下 platforms文件夾 對應平臺下(項目支持的最小版本)
    usr 目錄下 arch-arm -> include 確定後 會解決代碼提示和報錯的問題
  • ⑥編寫C函數 如果需要單獨編譯一下c代碼就在c/c++視圖中找到小錘子
  • 如果想直接運行到模擬器上 就不用錘子了
  • ⑦ java代碼中不要忘了 system.loadlibrary();

C代碼中向logcat輸出內容

Android.mk文件增加以下內容
LOCAL_LDLIBS += -llog
C代碼中增加以下內容
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
  • define C的宏定義 起別名 #define LOG_TAG “System.out” 給"System.out"起別名LOG_TAG
  • #define LOGI(…) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, VA_ARGS)
  • 給 __android_log_print函數起別名 寫死了前兩個參數 第一個參數 優先級 第二個參數TAG
  • VA_ARGS 可變參數的固定寫法
  • LOGI(…)在調用的時候 用法跟printf()一樣

C代碼回調java方法

  • ① 找到字節碼對象
    • //jclass (FindClass)(JNIEnv, const char*);
    • //第二個參數 要回調的java方法所在的類的路徑 “com/itheima/callbackjava/JNI”
  • ② 通過字節碼對象找到方法對象
    • //jmethodID (GetMethodID)(JNIEnv, jclass, const char*, const char*);
    • 第二個參數 字節碼對象 第三個參數 要反射調用的java方法名 第四個參數 要反射調用的java方法簽名
    • javap -s 要獲取方法簽名的類的全類名 項目/bin/classes 運行javap
  • ③ 通過字節碼創建 java對象(可選) 如果本地方法和要回調的java方法在同一個類裏可以直接用 jni傳過來的java對象 調用創建的Method
    • jobject obj =(*env)->AllocObject(env,claz);
    • 當回調的方法跟本地方法不在一個類裏 需要通過剛創建的字節碼對象手動創建一個java對象
    • 再通過這個對象來回調java的方法
    • 需要注意的是 如果創建的是一個activity對象 回調的方法還包含上下文 這個方法行不通!!!回報空指針異常
  • ④ 反射調用java方法
    • //void (CallVoidMethod)(JNIEnv, jobject, jmethodID, …);
    • 第二個參數 調用java方法的對象 第三個參數 要調用的jmethodID對象 可選的參數 調用方法時接收的參數
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章