基於Eclipse的Android JNI層測試應用開發過程記錄

來源:互聯網
上載者:User


前言


  本文記錄一個Java層與JNI層參數與資料互動的應用程式開發過程,為實現一個功能完整的帶Java與JNI的應用程式打下基礎。本文假設讀者已搭建好Android的Eclipse與NDK開發環境,包括通過ADB串連手機的配置。


1. 構建基本的Android應用程式 1.1  引導介面配置
  開啟Eclipse,"File"->"New"->"Android Application Project",在彈出介面,配置如下(紅色框表示不是預設,是作者修改過的地方,下同):



  之後,一路點擊"Next >",直到"Finish"。點擊"Finish"之後,自動回開啟如下介面:


   1.2 XML檔案配置
  我們可以看到一個虛擬應用介面,其中有一行文字"Hello world!"。點擊右下角的activity_main.xml,其中有如下定義:
    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/hello_world" />
  其中的字串就定義在res檔案夾下values檔案夾中的strings.xml中。我們對hello_world的值做修改,如下:
<?xml version="1.0" encoding="utf-8"?><resources>    <string name="app_name">app</string>    <string name="action_settings">Settings</string>    <string name="hello_world">Napolean: Hello Android Application!</string></resources>
  根據需要,可以將中類比應用介面的左側邊欄中的控制項拖入類比應用介面上放置,相應地,XML檔案中就會有該控制項的布局描述。

1.3 運行應用程式
  可以再src目錄下有個MainActivity.java,這是上面引導介面配置完成後自動產生的程式碼。
package com.napolean.app;import android.os.Bundle;import android.app.Activity;import android.view.Menu;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}}
  插入手機串連上ADB,或者配置一個Android模擬器,點擊運行程式,即可看到應用程式運行並顯示我們所修改的字串。關於手機如何通過ADB串連以及Android模擬器的配置,在此不作展開。

2. 構建Java與C/C++互動的JNI層介面
  Java與C/C++互動分為兩邊:一邊是Java層:在Java層的類中聲明本地介面函數,並在公用介面函數調用,使得需要訪問JNI層函數的類調用這些公用函數即可;另一邊是JNI層:JNI層有對應於Java層中本地函數的對應函數,也有其它普通函數,在Java層本地函數被調用時,實際上最終會執行對應的C/C++層函數。

2.1 Java層包含JNI本地函數的類    右鍵點擊src目錄下的com.napolean.app,選擇"New"->"Class",在彈出的介面中輸入類名"NativeInterface","Finish"即可完成NativeInterface類的建立。在產生的NativeInterface.java中的空類NativeInterface中添加Native函數及其封裝的公用介面,具體代碼如下:
package com.napolean.app;public class NativeInterface {private native void native_init();private native void native_exit();private native byte[] native_process(byte[] in_buffer, int width, int height);public void NativeInit() {native_init();}public void NativeExit() {native_exit();}public void NativeProcess(byte[] in_buffer, int width, int height) {native_process(in_buffer, width, height);}static {        System.loadLibrary("NativeInterface");    }}
  所建立的Native函數有初始化函數、退出函數和處理函數。其中處理函數包含輸入位元組流、Buffer寬度、Buffer高度,返回處理後的位元組流。接下來三個函數分別是三個Native函數的公用介面。
  最後在static中調用System.loadLibrary(),其中引號內為後面JNI層動態庫庫名。實際產生的動態庫名會在Android.mk中指定的庫名前添加lib之後添加.so,即聲明為xxx的庫,最終產生libxxx.so。值得注意的是:在上述載入函數中必須使用xxx而不能使用全名,並且在Android中指定的庫名不能帶lib首碼,否則無法載入。
2.2 本地函數在JNI層的實現
  首先建立jni檔案夾:右鍵點擊工程名,"New"->"Folder",輸入"jni",建立jni檔案夾。



  接著,在終端輸入javah命令,建立根據NativeInterface中聲明native函數對應的JNI層函數介面,在工程根目錄下具體命令為
javah -classpath bin/classes -d jni com.napolean.app.NativeInterface
  運行命令後,在jni檔案夾下就會產生一個名為com_napolean_app_NativeInterface.h的標頭檔,裡麵包含有的介面函數如下:
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_napolean_app_NativeInterface */#ifndef _Included_com_napolean_app_NativeInterface#define _Included_com_napolean_app_NativeInterface#ifdef __cplusplusextern "C" {#endif/* * Class:     com_napolean_app_NativeInterface * Method:    native_init * Signature: ()V */JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1init  (JNIEnv *, jobject);/* * Class:     com_napolean_app_NativeInterface * Method:    native_exit * Signature: ()V */JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1exit  (JNIEnv *, jobject);/* * Class:     com_napolean_app_NativeInterface * Method:    native_process * Signature: ([BII)[B */JNIEXPORT jbyteArray JNICALL Java_com_napolean_app_NativeInterface_native_1process  (JNIEnv *, jobject, jbyteArray, jint, jint);#ifdef __cplusplus}#endif#endif
  產生這個標頭檔的目的是免去手動編寫JNI層的介面函數,並無其它用途,使用後可刪除。在jni檔案夾下建立NativeInterface.cpp,將上述標頭檔中所有內容拷入,經過改造,如下:
#include <jni.h>#include <android/log.h>#define LOG_TAG "APP"#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)#ifndef _Included_com_napolean_app_NativeInterface#define _Included_com_napolean_app_NativeInterface#ifdef __cplusplusextern "C" {#endif/* * Class:     com_napolean_app_NativeInterface * Method:    native_init * Signature: ()V */JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1init  (JNIEnv *, jobject){LOGI("APP native init.");}/* * Class:     com_napolean_app_NativeInterface * Method:    native_exit * Signature: ()V */JNIEXPORT void JNICALL Java_com_napolean_app_NativeInterface_native_1exit  (JNIEnv *, jobject){LOGI("APP native exit.");}/* * Class:     com_napolean_app_NativeInterface * Method:    native_process * Signature: ([BII)[B */JNIEXPORT jbyteArray JNICALL Java_com_napolean_app_NativeInterface_native_1process  (JNIEnv *, jobject, jbyteArray, jint, jint){LOGI("APP native process.");}#ifdef __cplusplus}#endif#endif
  上述改造的代碼主要是將函式宣告改為函數定義,並且添加了列印資訊的支援。

2.3 JNI層動態庫編譯配置
  如果你親手建立工程,就會發現標頭檔和函數定義的字元下面有黃色底線,這是因為Eclipse找不到jni.h和android/log.h的標頭檔。所以需要在Eclipse配置JNI層特有的標頭檔路徑。

  第一步,添加C/C++特性。右鍵點擊jni檔案夾,"New"->"Other",在彈出介面如下選擇:



在下一個介面中的指定工程類型中選擇"Shared Library"(這一步並不重要,因為後面需要修改編譯工具),點擊"Finish",Eclipse就開始轉換工作。
  第二步,修改編譯工具。右鍵點擊工程名,"Properties",彈出如下介面,其中左側邊欄中的"C/C++ Build"與"C/C++ General"是上一步轉換工作完成後新的。選擇"C/C++ Build",在"Builder Settings"標籤中,去掉"Use default build command"前的勾,輸入"ndk-build",點擊"Apply"應用,如下所示:



  第三步,配置標頭檔路徑。在"C/C++ General"->"Paths and Symbols"->"Includes"->"GNU C++"中添加標頭檔路徑。點擊右邊的"Add...",添加四個路徑,然後點擊"Apply"應用。



其中,四個標頭檔路徑為:
/home/work/android/android-ndk-r9/platforms/android-18/arch-arm/usr/include//home/work/android/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/include//home/work/android/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include//home/work/android/android-ndk-r9/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/lib/gcc/arm-linux-androideabi/4.6/include/

  第四步,建立Android.mk。右鍵點擊jni檔案夾,"New"->"File",輸入Android.mk。添加如下代碼:
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := NativeInterfaceLOCAL_SRC_FILES := NativeInterface.cppLOCAL_LDLIBS := -lloginclude $(BUILD_SHARED_LIBRARY)

  第五步,編譯產生動態庫。點擊Eclipse功能表列中的"Project"->"Build Project",即可完成編譯。在libs/armeabi/檔案夾下產生了"libNativeInterface.so"動態庫。

2.4 Java層中調用本地函數
  Java層調用JNI層本地函數的方法,如下所示。
package com.napolean.app;import android.os.Bundle;import android.app.Activity;import android.view.Menu;public class MainActivity extends Activity {private NativeInterface myJNI;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);byte[] in_buffer = new byte[1920*1080*3/2];int width = 1920;int height = 1080;myJNI = new NativeInterface();myJNI.NativeInit();myJNI.NativeProcess(in_buffer, width, height);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}protected void onDestroy() {myJNI.NativeExit();super.onDestroy();}}


附錄:更多內容
  參見我的ChinaUnix部落格文章:http://blog.chinaunix.net/uid-20746260-id-3910616.html







聯繫我們

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