Android JNI開發入門之一

來源:互聯網
上載者:User

 
JNI在Android系統中有著廣泛的應用。Android系統底層都是C/C++實現的,上層提供的API都是Java的,Java通過JNI調用底
層的實現。比如:Android
API多媒體介面MediaPlayer類,其實底層通過JNI調用libmedia庫。由於JNI的存在可以讓我們重用很多已經存在C/C++的庫,省
去了重複開發的麻煩,並且可以利用很多開源的庫(Android庫中就有很多開源庫,比如libjpeg,libpng等等),並且讓我們開發的程式更有
效率(C/C++代碼發揮硬體最佳效能)。如果你對標準JNI不熟悉的話,可以先參考我的博文《Linux下JNI實現》,文中介紹了怎樣用標準JNI在Linux系統中實現一個Helloworld程式,可以讓你對JNI有一個初步的認識。本文簡單介紹一下怎樣在Android下面怎樣用JNI開發程式,並開發一個經典Helloworld應用程式。

交叉編譯環境

    首先是要搭建交叉編譯環境,因為Java層的應用程式是和硬體無關的,JDK編譯即可;但是Native
C/C++代碼是和硬體相關的,必須要用交叉編譯器編譯成特定硬體可執行代碼。請根據你的硬體平台搭建你的交叉編譯環境,我的MIPS平台,當然選擇的是
MIPS的交叉編譯器,如果你是Arm的請配置自己的交叉編譯器。

    我們首先編譯一個Native C的helloworld程式,一個學習怎樣在Android中增加一個程式,另外也可以驗證我們的交叉編譯環境是否正確,可以參考《Android編譯環境(1) – 編譯Native C的helloworld模組》。如果這個步沒有問題說明你的交叉編譯環境是沒有問題的,可以繼續往下面進行。

Java編寫的Android應用程式

    我們首先用java編寫helloworld應用程式(APK),這個代碼很簡單建立一個HelloWorld activity。代碼如下:

package com.simon;import android.app.Activity;import android.os.Bundle;import android.util.Log;public class HelloWorld extends Activity {    private static final String TAG = "HelloWorld";static {System.loadLibrary("helloworld");}private native String printJNI();    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        Log.d(TAG, "Activity call JNI: " + printJNI());    }}

     這個HelloWorld
Activity非常簡單,只是調用JNI介面printJNI()列印一些資訊到Android
logger上面。我們需要關注一下的是printJNI()的聲明,有一個native的關鍵字,說明他是一個用native代碼實現的函數,需要用
JNI調用Native代碼。另外注意static程式碼片段,這段代碼意思是當類HelloWorld第一次被載入的時候,載入
libhelloworld.so(請注意這裡寫的是庫名稱,在Linux中共用庫名為xxx共用庫,檔案存在形式為libxxx.so。所以
loadLibrary的參數不是libhelloworld.so,而是helloworld。如果寫錯誤了將會載入庫失敗,將會收到異常)。

C語言實現helloworld共用庫

     接下來我們需要來完成Native 代碼部分了,這裡需要強調一下,Android
JNI實現中為C/C++提供了兩套不同的API,調用的時候需要注意,否則非常有可能你會受到一些libc庫的崩潰資訊,沒準兒會把你整“崩潰”,呵
呵!下面先實現Native C來實現helloworld庫。

     如果你對Java標準JNI熟悉的話,肯定知道javah工具,可以根據java來源程式,產生Native代碼的標頭檔(可以參考我的博文《Linux下JNI實現》)。如果你是在Eclipse中開發apk的話,可以在開啟終端進入bin目錄,然後執行:

javah com.simon.HelloWorld

你將會得到,一個標頭檔com_simon_Helloworld.h,這裡包含有printJNI介面的C/C++聲明。這個聲明肯定正確,如果你把printJNI介面聲明寫錯了,HelloWorld將找不到printJNI介面,然後產生崩潰。

     我們建立com_simon_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_simon_HelloWorld_printJNI(JNIEnv *env, jobject obj){    LOGI("Hello World From libhelloworld.so!");    return (*env)->NewStringUTF(env, "Hello World!");}/* 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("JNI_OnLoad!");    if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {        LOGE("ERROR: GetEnv failed");        return -1;    }     return JNI_VERSION_1_4;}

請注意Java_com_simon_HelloWorld_printJNI函數的名字,這個名字是符合JNI規
範的,Java_開頭,後面緊跟著調用他類名(包含包名和類名,com_simon_HelloWorld),然後才是介面的名字printJNI。這樣
java虛擬機器就可以在com.simon.HelloWorld類調用printJNI介面的時候自動找到這個C實現的Native函
數調用。你可能注意到這個名字非常的長,作為一個函數名它非常有可能不是一個好的選擇。JNI
API允許你提供一個函數映射表,註冊給Jave虛擬機器,這樣Java虛擬機器就可以用函數映射表來調用相應的函數,就可以不必通過函數名來尋找需要調用的
函數了。這樣你的函數名也可以隨便定義了(可以定義最能表現函數功能的函數名),這個將會在helloworld共用庫的C++實現中示範。但是
Android系統中,還是推薦用JNI標準的函數名定義的。

    JNI_OnLoad函數JNI規範定義的,當共用庫第一次被載入的時候會被回調,這個函數裡面可以進行一些初始化工作,比如註冊函數映射表,緩衝一些變數等,最後返回當前環境所支援的JNI環境。本例只是簡單的返回當前JNI環境。

注意網上很多例子都說可以不實現JNI_OnLoad,我發現如果不是實現的
話,printJNI只可以返回整數型別的值,如果返回其他類型的值都會崩潰。並且GetEnv的調用也是必須的,否則也是崩潰,但是GetEnv的返回
值我並沒有用到,這些地方使我非常迷惑,期待達人解惑。

    接下來編寫Android.mk檔案,建立之,並輸入:

LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:=com_simon_Helloworld.cLOCAL_C_INCLUDES := $(JNI_H_INCLUDE)LOCAL_MODULE := libhelloworldLOCAL_SHARED_LIBRARIES := libutilsLOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)

     這裡面有幾個標籤需要說明:

1、LOCAL_C_INCLUDES說明包含的標頭檔,這裡需要包含JNI的標頭檔。

2、LOCAL_MODULE當前模組的名稱

3、LOCAL_SHARED_LIBRARIES當前模組需要依賴的共用庫,因為在hellowold中我們調用Android列印系統輸出到logger,所以我們必須要依賴libutils庫。

4、LOCAL_PRELINK_MODULE指明該模組是否被啟動就載入,可以參考《動態庫最佳化——Prelink(預串連)技術》。我們的helloworld庫不需要prelink,所以置為false。

    編譯這個模組,根據你的環境不同,選擇不同的編譯途徑。然後安裝apk,然後運行他們,通過logcat工具將會看到相應的輸出。

原文

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.