Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android NDK學習筆記3-入門案例篇

Android NDK學習筆記3-入門案例篇

編輯:關於Android編程

上篇文章我們安裝了NDK系統,在NDK系統文件中包含samples文件夾,打開該文件夾,我們發現裡面有大量的案例項目,這裡我們通過Eclipse導入一個名為hello-jni的項目

這裡寫圖片描述

導入成功後,我們可以看到項目目錄如下:

這裡寫圖片描述

然後,我們逐個學習案例源碼

1.聲明Native方法

打開com.example.hellojni目錄下的HelloJni.java
package com.example.hellojni;

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


public class HelloJni extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        /* Create a TextView and set its content.
         * the text is retrieved by calling a native
         * function.
         */
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }

    /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */
    public native String  stringFromJNI();

    /* This is another native method declaration that is *not*
     * implemented by 'hello-jni'. This is simply to show that
     * you can declare as many native methods in your Java code
     * as you want, their implementation is searched in the
     * currently loaded native libraries only the first time
     * you call them.
     *
     * Trying to call this function will result in a
     * java.lang.UnsatisfiedLinkError exception !
     */
    public native String  unimplementedStringFromJNI();

    /* this is used to load the 'hello-jni' library on application
     * startup. The library has already been unpacked into
     * /data/data/com.example.hellojni/lib/libhello-jni.so at
     * installation time by the package manager.
     */
    static {
        System.loadLibrary("hello-jni");
    }
}

這裡我給大家做個注釋

這裡寫圖片描述

2.實現原生方法

打開jni目錄下的hello-jni.c
#include 
#include 
/**
 * 原生方法stringFromJNI也用一個名為Java_com_example_hellojni_HelloJni_stringFromJNI的完全
 * 限定的函數來聲明,這種顯示的函數命名讓虛擬機在加載的共享庫中自動查找原生函數。函數命名規則為:  *Java_類全路徑_方法名。如Java_com_example_hellojni_HelloJni_stringFromJNI,其中Java_是函數的前  *綴,com_example_hellojni_HelloJni是類名,stringFromJNI是方法名,它們之間用 _(下劃線) 連接
 */
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )// 第一個參數JNIEnv是指向可用JNI函數表的接口指針
                                                                      // 第二個參數jobject是HelloJni類實例的Java對象引用
{

    // env指向java當前線程的實例,訪問一個NewStringUTF函數
    return (*env)->NewStringUTF(env, "Hello Castiel" ABI ".");
}

下面我對代碼中的各個函數做一下講解

(1)JNIEnv接口指針

原生代碼通過JNIEnv接口指針提供的各種函數來使用虛擬機的功能。JNIEnv是一個執行線程-局部數據的指針,而線程-局部數據中包含執行函數表的指針。

C代碼中,JNIEnv指向JNINativeInterface結構的指針,為了訪問任何一個JNI函數,該指針需要首先被解引用。調用格式如下:

        return (*env)->NewStringUTF(env,"hello china");

C++代碼中,JNIEnv實際上是C++類實例,JNI函數以成員函數的形式存在。

        return env->NewStringUTF("hello china");

在標准的Java平台下,每個Process裡可以產生很多JavaVM對象,每個JavaVM對象都有一個與之對應的JavaVM對象,但是在Android平台上,每個Process只能產生一個DalvikVM對象,也就是說在一個Android的進程中是通過有且只有一個虛擬器對象來服務所有Java和C++代碼的。
1、JNIEnv 內部包含一個Pointer,Pointer指向Dalvik的JavaVM對象的Fanction Table,JNIEnv 關於程序執行環境的眾多函數正是來源於Dalvik虛擬機
2、Android中每當一個Java線程第一次要調用本地C/C++代碼時,Dalvik虛擬機實例會為該Java線程產生一個JNIEnv *指針
3、Java每條線程在和C/C++互相調用時,JNIEnv*是相互獨立的,互不干擾
4、每本地的C/C++代碼想獲得當前線程所要使用的JNIEnv時,可以使用Dalvik VM對象的Java VM* jvm->getEnv()方法,該方法即會返回當前線程所在的JNIEnv*

(2)實例方法與靜態方法

Java程序中有兩類方法:實例方法和靜態方法。靜態方法和實例方法均可以聲明為原生的,可以通過JNI技術以原生代碼的形式提供它們的實現。原生實例方法通過第二個參數獲取實例引用,該參數是jobject類型的。如:
a.原生實例方法定義:

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )

因為靜態方法沒有與實例綁定,因此通過第二個參數獲取類引用而不是實例引用,第二個參數是jclass值類型的。
b.原生靜態方法定義:

JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jclass clazz )
JNI提供了自己的數據類型從而讓原生代碼了解java數據類型

3.使用NDK-Build腳本編譯

開發過程中我們可以使用NDK-Build腳本啟動Android NDK構建系統。
● 默認情況下,NDK-Build腳本應該在主項目目錄中執行,-C參數可以用於指定命令行中NDK項目的位置,這樣一來NDK-Build腳本可以從任意位置開始:

ndk-build -C /path/to/the/project

● 如果源文件沒被修改,Android NDK構建系統不會重構建目標。可以用-B執行NDK-Build腳本來強制重構建所有源代碼。

ndk-build -B

● 為了清理生成的二進制文件和目標文件,可以在命令行執行ndk-build clean命令。Android NDK構建系統會刪除生成的二進制文件。

ndk-build clean

● Android NDK構建系統依賴與GUN Make工具對模塊進行構建。默認情況下GUN Make工具一次執行依據構建命令,等這一句執行完了以後再執行下一句。如果在命令行使用-j參數,GUN Make就可以並行執行構建命令。另外也可以通過執行該參數之後的數字來指定並行執行的命令總數。

ndk-build -j 4

OK,我們開始編譯項目,在cmd命令行下,我們首先進入HelloJni項目的目錄中,然後執行ndk-build命令。

這裡寫圖片描述

編譯成功後,我們發現整個項目目錄生成libs和obj兩個文件夾

 

這裡寫圖片描述

 

這裡給大家解釋一下這些文件的用途

jni:該目錄包含原生組件的源代碼及描述原生組件構建方法的Android.mk構建文件。Android NDK構建系統將該目錄作為NDK項目目錄並希望在項目根目錄中找到它。mk文件不用改動,是留給ndk-build編譯使用的。
Libs:在Android NDK構建系統的構建過程中創建該目錄,它包含指定的目標機體系結構的獨立子目錄,例如ARM的armeabi,在打包過程中,該目錄被包含在APK文件中。
Obj:這是一個中間目錄,編譯源代碼後產生的目標文件都被保存在該目錄下,開發人員不需要訪問該目錄。

最後,我們運行項目,得到我們想要的結果

這裡寫圖片描述
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved