android jni 編程

1.引言
我們知道,Android系統的底層庫由c/c++編寫,上層Android應用程序通過Java虛擬機調用底層接口,銜接底層c/c++庫與Java應用程序間的接口正是JNI(JavaNative Interface)。本文描述瞭如何在ubuntu下配置AndroidJNI的開發環境,以及如何編寫一個簡單的c函數庫和JNI接口,並通過編寫Java程序調用這些接口,最終運行在模擬器上的過程。
2.環境配置
2.1.安裝jdk1.6
2.2.安裝android應用程序開發環境 eclipse adt sdk
2.3.安裝NDK
NDK是由android提供的編譯android本地代碼的一個工具。1)從androidndk官網http://wear.techbrood.com/tools/sdk/ndk/
(2)解壓ndk到工作目錄:
(3)設置ndk環境變量

exportPATH=/usr/local/ndk:$PATH

3.JNI實現
我們需要定義一個符合JNI接口規範的c/c++接口,這個接口不用太複雜,例如輸出一個字符串。接下來,則需要把c/c++接口的代碼文件編譯成共享庫(動態庫).so文件,並放到模擬器的相關目錄下。最後,啓動Java應用程序,就可以看到最終效果了。
3.1.編寫Java應用程序代碼

(1)啓動Eclipse,新建android工程

Project:JNITest

Package:org.tonny.jni(注意包名不能帶“_”符號)

Activity:JNITest

(2)編輯資源文件

編輯res/values/strings.xml文件如下:

    <?xmlversionxmlversion="1.0"encoding="utf-8"?>  
    <resources>  
    <stringnamestringname="hello">HelloWorld, JNITestActivity!</string>  
    <stringnamestringname="app_name">JNITest</string>  
    <stringnamestringname="btn_show">Show</string>  
    </resources>  

編輯res/layout/main.xml文件如下:

<?xmlversionxmlversion="1.0"encoding="utf-8"?>  
<LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
android:layout_width="fill_parent"  
android:layout_height="fill_parent"  
android:orientation="vertical">  
<TextView  
android:layout_width="fill_parent"  
android:layout_height="wrap_content"  
android:text="@string/hello"/>  
<EditText  
android:id="@+id/ed_name"  
android:layout_width="match_parent"  
android:layout_height="wrap_content"  
android:layout_gravity="center_horizontal"  
android:layout_marginLeft="5dp"  
android:layout_marginRight="5dp"/>  
<Button  
android:id="@+id/btn_show"  
android:layout_width="109dp"  
android:layout_height="wrap_content"  
android:layout_gravity="center_horizontal"  
android:text="@string/btn_show"/>  
</LinearLayout> 

我們在主界面上添加了一個EditText控件和一個Button控件。
(3)編輯JNITest.java文件

package com.example.jnitest;

import com.example.jnitest_1.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class JNITest extends Activity {
    static{  
        System.loadLibrary("JNITest");  
        } 

    public native String GetReply();  
    private EditText edtName;  
    private Button btnShow;  
    String reply;

    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        reply= GetReply();  
        edtName = (EditText)this.findViewById(R.id.ed_name);  
        btnShow = (Button)this.findViewById(R.id.btn_show);  
        btnShow.setOnClickListener(new View.OnClickListener() {  

        public void onClick(View v) {  
            edtName.setText(reply);  
            }  
        }); 
    }
}

我們看這一段代碼:

    static{  
    System.loadLibrary("JNITest");  
    }  

static表示在系統第一次加載類的時候,先執行這一段代碼,在這裏表示加載動態庫libJNITest.so文件。
再看這一段:

private native String GetReply();

native表示這個方法由本地代碼定義,需要通過jni接口調用本地c/c++代碼。
(4)編譯工程,生成.class文件。
3.2.用javah工具生成符合JNI規範的c語言頭文件
在終端中,進入android工程所在的bin目錄

$cd ~/project/Android/JNITest/bin 

我們用ls命令查看,可以看到bin目錄下有個classes目錄,其目錄結構爲classes/org/tonny/jni,即classes的子目錄結構是android工程的包名org.tonny.jni。請注意,下面我們準備執行javah命令的時候,必須進入到org/tonny/jni的上級目錄,即classes目錄,否則javah會提示找不到相關的java類。

下面繼續:

$cd classes  
$javah org.tonny.jni.JNITest  
$ls  
org org_tonny_jni_JNITest.h

執行javahorg.tonny.jni.JNITest命令,在classes目錄下會生成org_tonny_jni_JNITest.h頭文件。如果不進入到classes目錄下的話,也可以這樣:

$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest  

-classpath 參數表示裝載類的目錄。

javah 編譯時,會報錯,Error: cannot access android.app.Activity
class file for android.app.Activity not found
原因:不認識android.app.Activity
解決方法:在CLASSPATH中添加sdk中的android.jar路徑,就可以了!

export SDK_HOME=~/tool/adt-bundle-linux-x86_64-20140321/sdk/platforms/android-19
export 

CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:${SDK_HOME}/android.jar

3.3.編寫c/c++代碼

生成org_tonny_jni_JNITest.h頭文件後,我們就可以編寫相應的函數代碼了。下面在android工程目錄下新建jni目錄,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h頭文件拷貝到jni目錄下,並在jni目錄下新建org_tonny_jni_JNITest.c文件,編輯代碼如下:

    #include<jni.h>  
    #include<string.h>  
    #include"org_tonny_jni_JNITest.h"  


    JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply  
    (JNIEnv *env, jobject obj){  
    return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");  
    }  

我們可以看到,該函數的實現相當簡單,返回一個字符串爲:”Hello,JNITest”
3.4.編寫Android.mk文件

在~/project/Android/JNITest/jni目錄下新建Android.mk文件,android可以根據這個文件的編譯參數編譯模塊。編輯Android.mk文件如下:

LOCAL_PATH:= $(call my-dir)  
include $(CLEAR_VARS)  
LOCAL_MODULE := libJNITest  
LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c  
include $(BUILD_SHARED_LIBRARY)  

在使用NDK之初遇到這個一個問題,Android.mk文件我自己編寫一遍,沒想在編譯過程中一直編譯不過,報錯如下:
Android.mk:3: * missing separator. Stop.

報錯提示:提示缺省分隔符
於是找了好一會兒才找到問題原因所在:
$符號前面必須加一個空格。

“No rule to make target”
Android.mk 寫的一定有問題。空格之類的問題

LOCAL_MODULE表示編譯的動態庫名稱

LOCAL_SRC_FILES 表示源代碼文件
3.5.用ndk工具編譯並生成.so文件

進入到JNITest的工程目錄,執行ndk-build命令即可生成libJNITest.so文件。
$cd ~/project/Android/JNITest/  
$ndk-build  
Invalidattribute name:  
package  
Install : libJNITest.so => libs/armeabi/libJNITest.so  
可以看到,在工程目錄的libs/armeabi目錄下生成了libJNITest.so文件。
3.6.在模擬器上運行

(1)首先,我們把android模擬器啓動起來。進入到emulator所在目錄,執行emulator命令:
$cd ~/software/android/android-sdk-linux/tools  
$./emulator @AVD-2.3.3-V10 -partition-size 512  
2)接下來,我們需要把libJNITest.so文件拷貝到模擬器的/system/lib目錄下,執行以下命令:
$cd ~/project/Android/JNITest/libs/armeabi/  
$adb remount  
$adb push libJNITest.so /system/lib  
80 KB/s (10084 bytes in 0.121s) 

“`
當在終端上看到有80 KB/s (10084 bytes in 0.121s)傳輸速度等信息的時候,說明拷貝成功。
(3)在終端上執行JNITest程序

在這裏我還要補充下:
在我們開發過程中,改一個c/c++的文件,我們都要手動去編譯一下有點兒麻煩。這裏我們可以使用讓eclipse幫助我們自己編譯。

右擊app工程的properties–>Builders–>NEW –>;Program 可以看到以下內容:

這裏寫圖片描述

ubutun環境下第一個填的是/bin/bash
第二個空白填的是/bin
第三個空白填
–login -c “cd /home/eclipse_workspace/JNItest && $NDK ndk-build”

接着切換到Refresh 標籤頁
這裏寫圖片描述

接着切換到Build Options標籤頁
這裏寫圖片描述

這樣就完成了配置,點擊確定可看到控制檯自動編譯程序了
這裏寫圖片描述

4.參考文章
http://blog.csdn.net/xnwyd/article/details/7086384

http://bbs.51cto.com/thread-948244-1.html###zoom

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