Android JNI編程指南及模擬器配置問題

目前正在學習JNI,從一開始的一無所知,到如今的略知一二,走了很多彎路,爲了讓有興趣的同行少走彎路,下面把個人經驗記錄下來,給你們一個參考:
一、先從SimpleJNI提及:
在Android SDK的源碼目錄下./development/samples/SimpleJNI能夠找到一個最簡單的JNI例子,其文件樹以下
?
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
12
.
|-- AndroidManifest.xml
|-- Android.mk
|-- jni
| |-- Android.mk
| `--native.cpp
`-- src
`-- com
`-- example
`-- android
`-- simplejni
`-- SimpleJNI.java


該例子的主要思想是用JNI生成一個本地庫libsimplejni.so,實現一個add(a,b)功能,而後經過SimpleJNI.java調用該庫輸出顯示信息

此例子的Android.mk文件以下:

?
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
# This makefile shows how to build a shared library and an activity that
# bundles the shared library and calls it using JNI.
 
TOP_LOCAL_PATH:= $(call my-dir)
 
# Build activity
LOCAL_PATH:= $(TOP_LOCAL_PATH)
include $(CLEAR_VARS)
 
LOCAL_MODULE_TAGS := samples
LOCAL_SRC_FILES := $(call all-subdir-java-files) #查找當前目錄下全部的java文件
LOCAL_PACKAGE_NAME := SimpleJNI                #編譯一個java包:SimpleJNI.apk
LOCAL_JNI_SHARED_LIBRARIES := libsimplejni #編譯一個動態庫:libsimplejni.so
LOCAL_PROGUARD_ENABLED := disabled
 
include $(BUILD_PACKAGE)
 
# ============================================================
 
# Also build all of the sub-targets underthisone: the shared library.
include $(call all-makefiles-under,$(LOCAL_PATH))


在Android SDK的根目錄下面運行終端,輸入以下編譯命令:
?
代碼片斷,雙擊複製
01
make SimpleJNI libsimplejni


將獲得以下兩個文件:
?
代碼片斷,雙擊複製
01
02
out/target/product/sdkDemo/system/app/SimpleJNI.apk
out/target/product/sdkDemo/system/lib/libsimplejni.so


JNI代碼的目錄爲jni/vative.cpp,其內容以下:
?
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
View Code
#define LOG_TAG"simplejni native.cpp"
#include <utils/Log.h>
 
#include <stdio.h>
 
#include"jni.h"//JNI相關的頭文件
   
  staticjint add(JNIEnv *env, jobject thiz, jint a, jint b) {   /*定義Java方法add(),具備兩個整數類型的參數和一個整數類型的返回值,由本地代碼add函數實現*/
int result = a + b;
     LOGI("%d + %d = %d", a, b, result);
return result;
}
 
staticconstchar*classPathName ="com/example/android/simplejni/Native";   //類的路徑名
 
static JNINativeMethod methods[ ] = {     //本地方法列表
   {"add", "(II)I", (void*)add },
};
 
/*使用JNI的核心是JNINativeMethod結構體,這個結構體在jni.h中定義
 
typedef struct {
 
        const char* name;           /*JNI函數的名稱*/
 
constchar* signature;     /*描述JNI函數的參數和返回值*/
 
void*           fnPtr;            /*JNI函數對應的C(C++)語言函數指針*/
 
}JNINativeMethod;
 
關於參數和返回值的類型以下表:
 
Java 類型 JNI類型 對應字母
Java 布爾類型(boolean) jboolean(8位無符號) Z
Java字節(byte) jbyte(8位有符號) B
Java字符(char) jchar(16位無符號) C
Java短整型(short) jshort(16位有符號) S
Java整型(int) jint(32位有符號) I
Java長整型(long) jlong(64位有符號) J
Java單精度浮點(folat) jfloat(IEEE754,32位) F
Java雙精度浮點(double) jdouble(IEEE754,64位) D
Java對象 jobject L
Java無返回值 void V
 
 
該例子裏"(II)I表明的是,有兩個整型參數和一個整"
 
*/
/*
  * Register several native methods for one class.
 
*/
staticint registerNativeMethods(JNIEnv* env, constchar* className,
     JNINativeMethod* gMethods, int numMethods)
{
     jclass clazz;
 
     clazz = env->FindClass(className);
if (clazz == NULL) {
         LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
     }
if (env->RegisterNatives(clazz, gMethods, numMethods) <0) {
         LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
     }
 
return JNI_TRUE;
}
 
/*
  * Register native methods for all classes we know about.
  *如下是註冊JNI方法,它又調用registerNativeMethods()函數
  * returns JNI_TRUE on success.
*/
staticint registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName,
                  methods, sizeof(methods) /sizeof(methods[0]))) {
return JNI_FALSE;
   }
 
return JNI_TRUE;
}
 
// ----------------------------------------------------------------------------
 
/*
  * This is called by the VM when the shared library is first loaded.
 
*在加載庫的過程當中調用registerNatives()函數實現方法註冊
*/
   
typedef union {
     JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
 
jint JNI_OnLoad(JavaVM* vm,void* reserved)
{
     UnionJNIEnvToVoid uenv;
     uenv.venv = NULL;
     jint result =-1;
     JNIEnv* env = NULL;
    
     LOGI("JNI_OnLoad");
 
if(vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
         LOGE("ERROR: GetEnv failed");
gotobail;
     }
     env = uenv.env;
 
if(registerNatives(env) != JNI_TRUE) {
         LOGE("ERROR: registerNatives failed");
gotobail;
     }
    
     result = JNI_VERSION_1_4;
    
bail:
returnresult;
}


編譯此JNI代碼所須要的Android.mk以下:
?
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# This makefile supplies the rulesforbuilding a library of JNI codefor
#use by our example of how to bundleashared library with an APK.
 
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
 
LOCAL_MODULE_TAGS := samples
 
# This is the target being built.
LOCAL_MODULE:= libsimplejni
 
# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
   native.cpp
 
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := \
  libutils
 
# Nostaticlibraries.
LOCAL_STATIC_LIBRARIES :=
 
# Also need the JNI headers.
LOCAL_C_INCLUDES += \
  $(JNI_H_INCLUDE)
 
# No special compiler flags.
LOCAL_CFLAGS +=
 
# Don't prelinkthislibrary.  For more efficient code, you may want
# to addthislibrary to the prelink map and setthis totrue. However,
# it's difficult todothisforapplications that are not supplied as
# part of a system image.
 
LOCAL_PRELINK_MODULE :=false     #不須要從新連接此庫
 
include $(BUILD_SHARED_LIBRARY)


應用部分的代碼目錄爲/src/com/example/android/simplejni/SimpleJNI.java,在這個類中Native類是對本地方法的封裝,內容以下:

?
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
classNative {         
  //定義Java的封裝類
static{
// The runtime will add "lib" on the front and ".o" on the end of
// the name supplied to loadLibrary.
         System.loadLibrary("simplejni");           //加載本地庫
     }
 
staticnativeint add(inta,intb);              //調用本地方法
}


在這個類中調用的過程以下: 
?
代碼片斷,雙擊複製
01
02
03
04
05
06
07
08
09
10
11
publicclass SimpleJNIextendsActivity {
/** Called when the activity is first created. */
     @Override
publicvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
         TextView tv =newTextView(this);                                    //創建一個UI中的類TextView
intsum = Native.add(2,3);                                                //經過封裝類調用本地方法
         tv.setText("2 + 3 = "+ Integer.toString(sum));                  //設置顯示內容
         setContentView(tv);
     }
}


一般JNI的使用自下而上有4個層次:本地庫、JNI庫、聲明本地接口的Java類,Java調用者。在本例中,本地庫和JNI庫合二爲一,聲明本地接口的Java類和Java調用者合二爲一。

二、將以上所獲得的libsimplejni.so與SimpleJNI.apk兩個文件從Ubuntu中拷貝出來,放置在windows C盤的根目錄下,

運行Android模擬器

在windows的「運行」中輸入cmd打開windows的命令窗口

輸入cd c:\命令切換到C盤根目錄下

而後輸入adb version確實系統是否已經安裝了adb工具,若是已經安裝將獲得以下內容

Android Debug Bridge version 1.0.26

若是沒有安裝,能夠到\android-sdk-windows\tools目錄下將adb.exe和AdbWinApi.dll兩個文件拷貝到windows C盤的system32目錄下便可

而後輸入以下命令將libsamplejni.so拷貝到模擬器的system/lib目錄下



?
代碼片斷,雙擊複製
01
adb push libsamplejni.so /system/lib


再輸入以下命令把SampleJNI.apk拷貝到模擬器的system/app目錄下
?
代碼片斷,雙擊複製
01
adb push SampleJNI.apk


上面可能遇到的問題解決辦法:

(1)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Read-only file system
這是由於當前狀態下,此目錄是一個只讀目錄,輸入以下命令就能夠得到寫的權限

?
代碼片斷,雙擊複製
01
adb remount


(2)、提示failed to copy 'libsimplejni.so'to'/system/lib/libsimplejni.so':Out of memory

這是由於建議模擬器的時候默認的系統memory過小了,關閉當前模擬器,輸入以下命令就能夠解決此問題
?
代碼片斷,雙擊複製
01
emulator -avd Android2.2-partition-size128


說明:其中Android2.2是我當前所建AVD的名稱,128表明的是設置的系統memory的大小,輸入此命令以後將會自動打開模擬器

一切正常後,輸入相應命令後將獲得:
?
代碼片斷,雙擊複製
01
02
03
04
05
C:\>adb push libsimplejni.so /system/lib
40KB/s (5188bytes in0.125s)
 
C:\>adb push SimpleJNI.apk /system/app
52KB/s (5064bytes in0.093s)


在模擬器中,咱們將看到已經安裝好了的Simple JNI運行它以後 將獲得咱們所指望的結果 2+3=5
相關文章
相關標籤/搜索