Android中JNI的使用方法

來源:互聯網
上載者:User

標籤:des   android   style   blog   http   io   ar   color   os   

from://http://www.cnblogs.com/bastard/archive/2012/05/19/2508913.html

 

Android中JNI的使用方法

首先看一下Android平台的架構圖:(網上盜用)

    

 

  可以看到Android上層的Application和ApplicationFramework都是使用Java編寫,

底層包括系統和使用眾多的LIiraries都是C/C++編寫的。

  所以上層Java要調用底層的C/C++函數庫必須通過Java的JNI來實現。

下面將學習Android是如何通過Jni來實現Java對C/C++函數的調用。以HelloWorld程式為例:

第一步:

使用Java編寫HelloWorld 的Android應用程式:

package com.lucyfyr;import android.app.Activity;import android.os.Bundle;import android.util.Log;public class HelloWorld extends Activity {/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.main);  Log.v("dufresne", printJNI("I am HelloWorld Activity"));}  static  {    //載入庫檔案    System.loadLibrary("HelloWorldJni");  }   //聲明原生函數 參數為String類型 傳回型別為String  private native String printJNI(String inputStr);}

 

  這一步我們可以使用eclipse來產生一個App;

因為eclipse會自動為我們編譯此Java檔案,後面要是用到。

 

第二步:

  產生共用庫的標頭檔:

  進入到eclipse產生的Android Project中 :/HelloWorld/bin/classes/com/lucyfyr/ 下:

  可以看到裡面後很多尾碼為.class的檔案,就是eclipse為我們自動編譯好了的java檔案,其中就有:

    HelloWorld.class檔案。

  退回到classes一級目錄:/HelloWorld/bin/classes/

執行如下命令:

  javah com.lucyfyr.HelloWorld

  組建檔案:com_lucyfyr_HelloWorld.h

 

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_lucyfyr_HelloWorld */#ifndef _Included_com_lucyfyr_HelloWorld#define _Included_com_lucyfyr_HelloWorld#ifdef __cplusplusextern "C" {#endif/** Class: com_lucyfyr_HelloWorld* Method: printJNI* Signature: (Ljava/lang/String;)Ljava/lang/String;*/JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI(JNIEnv *, jobject, jstring);#ifdef __cplusplus}#endif#endif

 

  可以看到自動產生對應的函數:Java_com_lucyfyr_HelloWorld_printJNI

    Java_ + 包名(com.lucyfyr) + 類名(HelloWorld) + 介面名(printJNI):必須要按此JNI規範來操作;

  java虛擬機器就可以在com.simon.HelloWorld類調用printJNI介面的時候自動找到這個C實現的Native函數調用。

  當然函數名太長,可以在.c檔案中通過函數名映射表來實現簡化。

 

第三步:

實現JNI原生函數源檔案:

建立com_lucyfyr_HelloWorld.c檔案:

 

#include <jni.h>#define LOG_TAG "HelloWorld"#include <utils/Log.h>/* Native interface, it will be call in java code */JNIEXPORT jstring JNICALL Java_com_lucyfyr_HelloWorld_printJNI(JNIEnv *env, jobject obj,jstring inputStr){  LOGI("dufresne Hello World From libhelloworld.so!");  // 從 instring 字串取得指向字串 UTF 編碼的指標  const char *str =  (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE );  LOGI("dufresne--->%s",(const char *)str);  // 通知虛擬機器本地代碼不再需要通過 str 訪問 Java 字串。  (*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str );  return (*env)->NewStringUTF(env, "Hello World! I am Native interface");}
/* This function will be call when the library first be load.* You can do some init in the libray. return which version jni it support.*/jint JNI_OnLoad(JavaVM* vm, void* reserved){  void *venv;  LOGI("dufresne----->JNI_OnLoad!");  if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {    LOGE("dufresne--->ERROR: GetEnv failed");    return -1;  }
  return JNI_VERSION_1_4;}

 

  OnLoadJava_com_lucyfyr_HelloWorld_printJNI

函數裡面做一些log輸出 注意JNI中的log輸出的不同。

  JNI_OnLoad函數JNI規範定義的,當共用庫第一次被載入的時候會被回調,

這個函數裡面可以進行一些初始化工作,比如註冊函數映射表,緩衝一些變數等,

最後返回當前環境所支援的JNI環境。本例只是簡單的返回當前JNI環境。

 

第四步:

編譯產生so庫

  編譯com_lucyfyr_HelloWorld.c成so庫可以和app一起編譯,也可以都單獨編譯。

在目前的目錄下建立jni檔案夾:HelloWorld/jni/

  下建立Android.mk ,並將com_lucyfyr_HelloWorld.c和 com_lucyfyr_HelloWorld.h 拷貝到進去

編寫編譯產生so庫的Android.mk檔案:

 

LOCAL_PATH:= $(call my-dir)# 一個完整模組編譯include $(CLEAR_VARS)LOCAL_SRC_FILES:=com_lucyfyr_HelloWorld.cLOCAL_C_INCLUDES := $(JNI_H_INCLUDE)LOCAL_MODULE := libHelloWorldJniLOCAL_SHARED_LIBRARIES := libutilsLOCAL_PRELINK_MODULE := falseLOCAL_MODULE_TAGS :=optionalinclude $(BUILD_SHARED_LIBRARY)

 

系統變數解析:

  LOCAL_PATH - 編譯時間的目錄
  $(call 目錄,目錄….) 目錄引入操作符
    如該目錄下有個檔案夾名稱 src,則可以這樣寫 $(call src),那麼就會得到 src 目錄的完整路徑

  include $(CLEAR_VARS) -清除之前的一些系統變數
  LOCAL_MODULE - 編譯產生的目標對象
  LOCAL_SRC_FILES - 編譯的源檔案
  LOCAL_C_INCLUDES - 需要包含的標頭檔目錄
  LOCAL_SHARED_LIBRARIES - 連結時需要的外部庫
  LOCAL_PRELINK_MODULE - 是否需要prelink處理 
  include$(BUILD_SHARED_LIBRARY) - 指明要編譯成動態庫

  android.mk編譯模組添加具體方法參考:http://blog.csdn.net/yili_xie/article/details/4906865

 

編譯此模組:輸入編譯命令

  ./makeMtk mm packages/apps/HelloWorld/jni/

  上面是我的工程根目錄編譯命令。具體編譯方式根據自己系統要求執行。

編譯輸出: libHelloWorldJni.so(system/lib中視具體而定)

  此時庫檔案編譯好了可以使用,如果在eclipse中模擬器上使用,需要將 libHelloWorldJni.so匯入到system/lib 下,

  或者對應app的data/data/com.lucyfyr/lib/下;

但是在匯入system/lib 下的時候,卻始終提示out of memory。於是網上說使用emulator去搞,

  但是此命令使用起來卻有問題,始終沒有解決。但是可以匯入到data/data/com.lucyfyr/lib/下使用。但是我這裡直接在真機上弄的。

看一下HelloWorld中Android.mk檔案的配置

  其中存在:

    include $(LOCAL_PATH)/jni/Android.mk 表示編譯庫檔案

    LOCAL_JNI_SHARED_LIBRARIES := libHelloWorldJni 表示app依賴庫,打包的時候會一起打包。

 

第五步:

驗證執行

  將編譯好的apk安裝到手機上

  使用adb push到手機上去需要自己去匯入庫檔案libHelloWorldJni.so到data/data/com.lucyfyr/lib/

  使用adb install方式安裝則會自動匯入。

啟動HelloWorld :輸入命令 adb logcat |grep dufresne

輸出log如下:

  I/HelloWorld(28500): dufresne Hello World From libhelloworld.so!

  I/HelloWorld(28500): dufresne--->I am HelloWorld Activity

  V/dufresne(28500): Hello World! I am Native interface

符合調用列印順序正確。

 

以上通過一個簡單的例子學習了Android如何編寫編譯C庫檔案,以及如何使用庫檔案。

對於JNI的使用其中還涉及到其他方面的一些知識:C++介面如何調用,函數名註冊表怎麼回事,

參數類型如何匹配,JNI如何調用Java中的方法java<--->JNI等。

 

Android中JNI的使用方法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.