android開發——關於編寫JNI

最近由於要開發android支付應用,實現刷卡讀取磁卡的數據功能,需要編寫JNI調用C語言底層庫,在學習過程中也遇到了一些困難和問題,在這裏記錄下來,希望能給遇到同樣問題的朋友提供幫助,避免走彎路。通過一個簡單的調用c語言輸出“hello”語句的例子來介紹如何編寫JNI。
工程如下:


TestActivity.java:調用JNI方法,輸出hello語句。
JniTest.java: 編寫native方法,調用C語言方法,讓TestActivity.java調用。
jni:在創建工程的時候自行創建,放編譯好的so動態鏈接庫。

1.在android工程中寫native方法。
文件JniTest.java
package com.android.jni;

public class JniTest {

     public static native String hello ();
}

2. 編譯h頭文件(windows環境下)
打開控制檯,進入工程目錄(F:\androidDemo\test)

輸入如下命令編譯h頭文件
javah -classpath bin /classes -d jni com.android.jni.JniTest


-classpath ——類路徑 bin/classes
-d ——保存目錄:jni
com.android.jni.JniTest:包名+類名

這時候jni文件夾下就多出了一個h頭文件——com_android_jni_JniTest.h。

3.編寫C文件。
新建一個C文件——JniTest.c,實現com_android_jni_JniTest.h裏的方法。
文件JniTest.c:
#include "com_android_jni_JniTest.h"
#include <stdio.h>

/*
 * Class:     com_android_jni_JniTest
 * Method:    hello
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_android_jni_JniTest_hello
  (JNIEnv * env, jclass cla){
	return (*env)->NewStringUTF(env, "hello");
  }

注:在h頭文件中沒有寫上參數名,如env和cla,在c文件需要補上。

4.編寫Android.mk文件。
在jni目錄下新建Android.mk文件,內容如下:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := JniTest
LOCAL_SRC_FILES := JniTest.c

include $(BUILD_SHARED_LIBRARY)

該文件中的一些變量對應的含義如下:
LOCAL_SRC_FILES -編譯的源文件
LOCAL_MODULE -編譯的目標對象

4.編譯so動態鏈接庫
由於編譯so動態鏈接庫需要Linux環境,如果你的操作系統是windows,可以安裝cygwin模擬Linux環境,然後安裝NDK即可,如果你是Linux環境,那麼恭喜你,可以省略一步,直接安裝NDK即可,若你是ubuntu環境,那麼可以直接參考我之前的文章,(Ubuntu環境下配置NDK)其他環境就需要你自己google下了,過程應該大同小異了。
進入test工程(由於NDK配置路徑問題,我將工程拷到ndk目錄下的samples裏)(F:\android\android-ndk-r7b\samples\test)

輸入編譯so命令
$NDK/ndk-build

若出現如上顯示,則代表編譯成功。

5.加載so文件
在JniTest.java中添加加載so文件代碼,具體代碼如下:
package com.android.jni;

public class JniTest {

     static { 
           System. loadLibrary("JniTest"); //加載so動態鏈接庫
           } 
     public static native String hello();
}

在JniTest.java調用hello方法,具體代碼如下:
package com.android.test;

import com.android.jni.JniTest;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class TestActivity extends Activity {
     private TextView tv;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout. main);
        tv=(TextView)findViewById(R.id. tv);
        tv.setText(JniTest.hello());
    }
}

main.xml:
<?xml version="1.0" encoding= "utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width= "fill_parent"
    android:layout_height= "fill_parent"
    android:orientation="vertical" >

    <TextView
        android:id= "@+id/tv"
        android:layout_width= "fill_parent"
        android:layout_height= "wrap_content"
        android:text= "@string/hello" />

</LinearLayout>

運行結果如下:


大功告成,若有其他不明白的地方,隨時和我聯繫,我一定盡力幫助,大家互相學習!若有不對的地方,也請各位前輩們指正,謝謝!

在編寫JNI的過程中,也遇到了一些問題,編譯不成功,問題和解決辦法如下:
問題1.
Android NDK:Your APP_BUILD_SCRIPT points to an unknow files: ./jni/Android.mk
若出現該問題,是由於沒有編寫Android.mk文件。

問題2.
arm-linux-androideabi-gcc.exe:CreateProcess: no such file or directory
可能是內存溢出問題,只要關閉eclipse或者佔內存很大的軟件即可。

問題3.
error:parameter name omitted

方法缺少參數名。由於h頭文件是沒有參數名的,所以很容易在C文件忘記加上,例如:
JNIEXPORT jstring JNICALL Java_com_android_jni_JniTest_hello
  (JNIEnv *, jclass){
     return (*env)->NewStringUTF(env, "hello");
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章