Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android JNI/NDK開發(一)

Android JNI/NDK開發(一)

編輯:關於Android編程

少壯不努力,老大徒傷悲。大學時光的潇灑散漫導致今天連C/C++編程都不會。作為一個程序員,不會C/C++說出去簡直丟人啊。最近浏覽公司招聘信息(Android職位),發現對NDK開發的要求越來越普遍了。筆者學習的是java,從事Android開發,對於Android底層的東西多少有點畏懼,因為沒有涉及過,但是我們知道,不能因為怕就放棄。如我曾經簽名所言:現在開始行動,就比還在猶豫的人快了一步。

回到正題,NDK開發真的很難嗎?其實不是的,覺得難是因為你沒懂,不懂是因為沒學。那就來吧

NDK:什麼是NDK

Android NDK(Native Development Kit )是一套工具集合,允許你用像C/C++語言那樣實現應用程序的一部分。

NDK:什麼時候使用NDK

提高性能。在某些情況下使用C/C++處理邏輯算法的效率高於java(圖片相關處理算法等) 使用第三方庫。許多第三方庫由C/C++語言編寫,而Android應用程序需要使用現有的第三方庫,如Ffmpeg、OpenCV這樣的庫。 底層程序設計。例如,應用程序不依賴Dalvik Java虛擬機 。

別的不說,光看 提高性能 這一塊就很有吸引力了。入門篇,不說那些虛的,先跑起來。

1.搭建NDK開發環境

不會的話百度,這個不難,秒配置。1.下載ndk文件,解壓;2.配置到環境變量;3.cmd測試ndk是否已經安裝配置成功

2.實現第一個NDK項目

目標實現: 從Android層面傳遞兩個int值到底層,使用C/C++處理比較兩個值的大小,返回結果信息(string)給Android使用。
項目問題: Android如何將java的數據類型傳遞給C/C++使用?如何將C/C++數據類型傳遞給java使用?中間的橋梁是什麼?
項目分析: Android將java數據類型傳遞給native層,通過JNI“編碼”轉化為C/C++可用的數據類型,反過來一樣也是使用JNI作為橋梁溝通java和C++

2.1 Native (Android層面開發)

創建一個Android項目。
HelloNdk 中定義 native 接口,在C++中計算兩個int數的大小,返回結果信息string

public native String getMaxInt(int first, int second);

HelloNdk 中使用static 靜態塊調用so庫。

static {
        System.loadLibrary("hi-ndk");
    }

HelloNdk(Activity)完整代碼

public class HelloNdk extends Activity {

    static {
        System.loadLibrary("hi-ndk");//"hi-ndk" so 名稱
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        TextView tv = new TextView(this);
        tv.setText(getMaxInt(15, 10));
        setContentView(tv);
    }

    public native String getMaxInt(int first, int second);

}

2.2 JNI(Android和C/C++連接層處理)

首先配置JNI,在項目中新建 jni 文件夾。jni包含 Android.mkApplication.mk頭文件[cn_r_ndk_android_HelloNdk.h]C++文件

這裡寫圖片描述

新建Android.mkApplication.mk,這兩個文件內容不必記,配置文件嘛,理解一下什麼意思就好,也可以從ndk的sample項目中拷貝,解壓ndk<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPtauuvPL5rD8uL20+NPQc2FtcGxlo6zV0rW9IDxzdHJvbmc+aGVsbG8tam5pPC9zdHJvbmc+ILi01sbP7sS/1tC1xCA8c3Ryb25nPkFuZHJvaWQubWs8L3N0cm9uZz6jrDxzdHJvbmc+QXBwbGljYXRpb24ubWs8L3N0cm9uZz48L3A+DQo8cD48aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160709/20160709092100800.png" title="\" />

Android.mk 文件代碼。(看文件中注釋)

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hi-ndk   
#so項目名稱

LOCAL_SRC_FILES := hi-ndk.cpp
#資源文件名稱,對應的c/c++代碼文件

include $(BUILD_SHARED_LIBRARY)

Application.mk 文件代碼。(看文件中注釋)

APP_ABI := all

#APP_ABI:= armeabi armeabi-v7a x86 mips mips-r2 mips-r2-sf
#如果是普通arm處理器的Android手機,使用APP_ABI := armeabi;
#如果是x86處理器的,使用APP_ABI := x86,等等。
#如果APP_ABI := all,會編譯所有指令的so

APP_STL := stlport_static

#NDK中C++標准庫、STL的配置
#如未配置可能出現錯誤: fatal error: iostream: No such file or directory

頭文件[cn_r_ndk_android_HelloNdk.h]。C++頭文件,通過 javah 生成頭文件。javah 命令使用詳情,可以在 cmd 中查看敲入 javah 查看

這裡寫圖片描述

生成頭文件方式:打開cmd面板,直接進入項目src目錄,然後執行

javah -d ../jni cn.r.ndk.android.HelloNdk
備注:cn.r.ndk.android.HelloNdk 是(定義了native接口)類的完整路徑

這裡寫圖片描述

執行成功之後可以在 jni 文件夾中查看 [cn_r_ndk_android_HelloNdk.h]

2.3 C/C++(邏輯處理層)

前面兩步完成了基本的配置,現在接下來進入重點的邏輯代碼編寫。
寫代碼之前就有幾個問題啦:

1.java數據類型和C++數據類型怎麼打通?
答:使用JNI。前面有提及到,JNI是java和C++這兩種語言的橋梁,JNI提供了一種規范,打通兩種語言之間的障礙

2.java數據類型如何轉化為JNI?
基本數據類型:
這裡寫圖片描述

引用類型:
這裡寫圖片描述

通過上表,不難看出,通過JNI規范,java中的基礎類型轉化成JNI數據類型基本都是加上了 j
例:int:int -> jint

3.JNI數據類型與C++數據類型 如何相互轉換?
這個就比較麻煩啦,需要自行查閱資料了解JNI編程中如何處理字符串以及對象類型的數據。
先看一下主要需要解釋的代碼。(這裡使用C++語言實現,引入頭文件什麼的就不說了,筆者也是一邊學習NDK,一邊學習C++,所以C++代碼寫的很水的話請不要嘲笑,麻煩指正出來)

jstring Java_cn_r_ndk_android_HelloNdk_getMaxInt(JNIEnv * env, jobject thiz,
        jint first, jint second) {

    int first_j = first;
    int second_j = second;

    int max = maxInt(first_j, second_j);

    /**
     * int > string
     */
    string msg_result = xToString(max);

    string msg_tip = "the max number:";

    string msg = msg_tip + msg_result;

    const char* msg_j_a = msg.c_str();

    jstring msg_j = env->NewStringUTF(msg_j_a);

    return msg_j;
}

C++語言跟java很相似,筆者基本是按照java的思想寫代碼

這裡的基本數據類型 jint直接轉換成C++的int。接下來調用方法計算兩個數的最大值。這裡想吐槽一下的是,原來C++將 int 數據類型和 string類型拼接在一起這麼麻煩,在 javaStringint 使用 + 可以直接拼接。C++中需要使用轉換好多次。
我們想要把將 “the max number: XX” 這樣的結果返回給 java。這裡使用

    env->NewStringUTF(msg_j_a);

由於NewStringUTF()方法中接收的參數是 char* ch類型的,所以才有代碼中各種轉換。
筆者先將int轉化為string,然後拼接兩個string,接著使用string.c_str()將字符串轉化為char*,最終才調用NewStringUTF()方法返回jstring
備注:如果使用C語言調用JNI的方法有所不同

(*env)->NewStringUTF(env, "Hello from JNI ! ");

關於C/C++部分的代碼這裡就不多說了,因為我也不太會,萬一說錯了,那就尴尬了。讀者只能自行捉摸了。
關於C/C++和JNI之間的數據類型“轉換”有兩點我還是要分享一下:
1.基本數據類型可以直接轉換
2.string類型處理的幾個方法

http://blog.csdn.net/xyang81/article/details/42066665

看看完整版的代碼 hi-ndk.cpp

#include 
#include 
#include 
#include "cn_r_ndk_android_HelloNdk.h"

using namespace std;

// 函數聲明
int maxInt(int first, int second);
string xToString(int x);

jstring Java_cn_r_ndk_android_HelloNdk_getMaxInt(JNIEnv * env, jobject thiz,
        jint first, jint second) {

    int first_j = first;
    int second_j = second;

    int max = maxInt(first_j, second_j);

    /**
     * int > string
     */
    string msg_result = xToString(max);

    string msg_tip = "the max number:";

    string msg = msg_tip + msg_result;

    const char* msg_j_a = msg.c_str();

    jstring msg_j = env->NewStringUTF(msg_j_a);

    return msg_j;
}

/**
 * 比較兩個數大小
 */
int maxInt(int first, int second) {
    if (first > second) {
        return first;
    } else {
        return second;
    }
}

/**
 * X轉化為string
 */
string xToString(int x) {
    char ch[10];
    sprintf(ch, "%d", x);
    string result(ch);
    return result;
}

OK,到這裡基本的工作就做完了,會不會有點蒙圈?不會最好,如果蒙圈那就休息一下,再理一理思路,停下來想想實現步驟:
1. Native (Android層面開發)
簡述:【一個定義了native方法的類】
2. JNI(Android和C/C++連接層處理)
簡述:【一個JNI文件夾,裡面有四個重要文件, Android.mk,Application.mk,頭文件,C++文件】
3. C/C++(邏輯處理層)
簡述:【java->jni;jni<->c++;業務邏輯處理】
這樣一看是不是就簡單明了了呢?三部分組成,一看簡述能知道每一步大概做什麼了,如果還是一片空白就回去那一步再看看。

如果沒問題了,那就進行最後一步了,ndk-build 編譯 so 文件。

這裡寫圖片描述

編譯成功的畫面總是看的那麼自然,整齊劃一。
人們說黎明前最黑暗,成功前最渺茫。是的沒錯,在這一步你也可能卡很久,不過別怕,這一部分編譯出錯基本上是C/C++文件代碼的問題了,如果是數據類型轉換問題可參考上面提到的兩點,1基本數據類型直接使用,2.string問題參考友情鏈接。如果是代碼邏輯和規范問題的話,那就只能問度娘了,耐心點,你總能編譯成功。說出來不怕別人笑話,那個數字轉換和字符串拼接的需求我弄了很久,C++語言不熟悉,硬是問著百度寫完的。

編譯成功之後可以在lib文件夾中看到so文件,名稱為:hi-ndk

這裡寫圖片描述

你會不會想問那我Android怎麼使用這個so文件啊?怎麼拿到C++計算得到結果?

其實一開始就給出了,HelloNdk中代碼
類靜態塊中加載so文件,注意這裡不要寫 libhi-ndk.so ,除去前面的 【lib】 和後面的 【.so】 ,只需要hi-ndk

static {
        System.loadLibrary("hi-ndk");//"hi-ndk" so 名稱
    }

方法調用

TextView tv = new TextView(this);
tv.setText(getMaxInt(15, 10));

OK,到這裡第一個NDK項目就寫完了,很難嗎?也不是很難啊。用點心,一切沒多難,只是看你有多想做成一件事情。成功的那種喜悅是用錢買不到的,哈哈。同時還學習了新的語言C++。何樂而不為呢?

有什麼不懂的地方可以留言討論,有不足的地方還望指正批評。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved