1. 程式人生 > >Windows下,Eclipse的Android NDK(r8e) 配置

Windows下,Eclipse的Android NDK(r8e) 配置

 一、關於NDK
Android NDK全稱:Native Development Kit,即本地開發包。
1、NDK是一系列工具的集合。
NDK提供了一系列的工具,這些工具對開發者的幫助是巨大的。 
它們能幫助開發者快速開發C(或C++)的動態庫,並能自動將so庫和java應用一起打包成apk。
NDK集成了交叉編譯器,並提供了相應的mk檔案隔離CPU、平臺、ABI等差異,
開發人員只需要簡單修改mk檔案(指出“哪些檔案需要編譯”、“編譯特性要求”等),就可以創建出so庫。
NDK可以自動地將so和Java應用一起打包,極大地減輕了開發人員的打包工作。
2、NDK提供了一份穩定、功能有限的API標頭檔案宣告。
Google明確宣告該API是穩定的,在後續所有版本中都穩定支援當前釋出的API。
從該版本的NDK中看出,這些API支援的功能非常有限,包含有:
C標準庫(libc)、標準數學庫(libm)、壓縮庫(libz)、Log庫(liblog)。

二、NDK例項的實現
對於Windows環境下NDK的開發,如果使用的NDK是r7之前的版本,必須要安裝Cygwin才能使用NDK;
從r7開始,Google的提供了一個ndk-build.cmd的指令碼,可以直接用它編譯,而不需要使用Cygwin了
只需要為Eclipse Android工程新增一個Builder,就能讓Eclipse自動編譯NDK。

本文是講述NDK-r8e下的實現例項。
下面是在windows下為NDK配置自動編譯的builder的過程(對於Linux,只需將ndk-build.cmd修改為ndk-build就可以了)。

(1)先下載安裝NDK

(2)新建工程和jni資料夾
開啟Eclipse,新建或匯入一個Android工程(我的取名為NdkTest

),在工程目錄下新建jni資料夾;
該jni資料夾就用來儲存NDK需要編譯的檔案程式碼等。
(3)新建並配置一個Builder:
 (a)Project->Properties->Builders->New,新建一個Builder。
 (b)在彈出的【Choose configuration type】對話方塊,選擇【Program】,點選【OK】:
 (c)在彈出的【Edit Configuration】對話方塊中,配置選項卡【Main】。
      在“Name“中輸入新builders的名稱(我取名為Ndk_Builder)。
      在“Location”中輸入nkd-build.cmd的路徑。
      (我的是E:\adt-bundle-windows-x86-20130522\ndk-r8e\ndk-build.cmd,
        根據各自的ndk路徑設定,也可以點選“Browser File System…”來選取這個路徑)。
      在“Working Diretcoty”中輸入專案工程的路徑${workspace_loc:/project}
      (我的是${workspace_loc:/NdkTest},也可以點選“Browse Workspace”來選取本工程目錄)。
  (d)【Edit Configuration】對話方塊中,配置選項卡【Refresh】。
      勾選“Refresh resources upon completion”,
      勾選“The entire workspace”,
      勾選“Recuresively include sub-folders”。
   (e)【Edit Configuration】對話方塊中,配置選項卡【Build options】。
     
  勾選“After a “Clean””,
      勾選“During manual builds”,
      勾選“During auto builds”,
      勾選“Specify working set of relevant resources”。 
      點選“Specify Resources…”
      勾選工程的“jni“目錄,點選”finish“。
點選“OK“,完成配置。到這裡Eclipse就能自動呼叫NDK編譯jni目錄下的C/C++程式碼了。

(4)新建JniClient.java
在工程中新建一個JniClient.java(為了呼叫C/C++程式碼),儲存在MainActivity.java目錄下;其內容如下:

[java] view plaincopyprint?
  1. package com.example.ndktest;  
  2. publicclass JniClient {  
  3.     staticpublicnative String HelloWorld();  
  4. }  
package com.example.ndktest;
public class JniClient {
    static public native String HelloWorld();
}

(5)生成JniClinet.class檔案
用cmd命令定位到JniClient.java所在目錄,輸入“javac JniClient.java“後回車,生成JniClinet.class檔案。
(如果是用的Eclipse建的工程,在bin\classes\com\example\ndktest目錄下就已經有JniClinet.class檔案了)。

(6)生成C++標頭檔案
將JniClinet.class拷貝到bin\classes\com\example\ndktest目錄,將cmd命令定位到bin\classes目錄,
輸入”javah com.example.ndktest.JniClient“後回車,在bin\classes目錄下就生成了C++標頭檔案了。

com_example_ndktest_JniClient.h的檔案內容如下:

[cpp] view plaincopyprint?
  1. /* DO NOT EDIT THIS FILE - it is machine generated */
  2. #include <jni.h>
  3. /* Header for class com_example_ndktest_JniClient */
  4. #ifndef _Included_com_example_ndktest_JniClient
  5. #define _Included_com_example_ndktest_JniClient
  6. #ifdef __cplusplus
  7. extern"C" {  
  8. #endif
  9. /* 
  10.  * Class:     com_example_ndktest_JniClient 
  11.  * Method:    HelloWorld 
  12.  * Signature: ()Ljava/lang/String; 
  13.  */
  14. JNIEXPORT jstring JNICALL Java_com_example_ndktest_JniClient_HelloWorld  
  15.   (JNIEnv *, jclass);  
  16. #ifdef __cplusplus
  17. }  
  18. #endif
  19. #endif
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ndktest_JniClient */

#ifndef _Included_com_example_ndktest_JniClient
#define _Included_com_example_ndktest_JniClient
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_ndktest_JniClient
 * Method:    HelloWorld
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_ndktest_JniClient_HelloWorld
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

(7)新建一個Android.mk檔案
在jni目錄下新建一個Android.mk檔案,其內容如下(詳細的語法以後再另外解釋):

[plain] view plaincopyprint?
  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3. LOCAL_MODULE := NdkTest  
  4. LOCAL_SRC_FILES := com_example_ndktest_JniClient.c  
  5. include $(BUILD_SHARED_LIBRARY)  
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := NdkTest
LOCAL_SRC_FILES := com_example_ndktest_JniClient.c
include $(BUILD_SHARED_LIBRARY)

(8)建立C++原始檔
com_example_ndktest_JniClient.h拷貝到本工程的jni目錄下;
然後新建一個com_example_ndktest_JniClient.c檔案,用來實現標頭檔案中函式,其內容如下:

[cpp] view plaincopyprint?
  1. #include "com_example_ndktest_JniClient.h"
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #ifdef __cplusplus
  5. extern"C"
  6. {  
  7. #endif
  8. JNIEXPORT jstring JNICALL Java_com_example_ndktest_JniClient_HelloWorld  
  9. (JNIEnv *env, jclass arg)  
  10. {  
  11.     jstring str = (*env)->NewStringUTF(env, "HelloWorld from JNI !");  
  12.     return str;  
  13. }  
  14. #ifdef __cplusplus
  15. }  
  16. #endif
#include "com_example_ndktest_JniClient.h"

#include <stdlib.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif

JNIEXPORT jstring JNICALL Java_com_example_ndktest_JniClient_HelloWorld
(JNIEnv *env, jclass arg)
{
    jstring str = (*env)->NewStringUTF(env, "HelloWorld from JNI !");
    return str;
}

#ifdef __cplusplus
}
#endif
編輯並儲存後,可以看到obj/local/armeabi目錄下將自動生成libNdkTest.so庫。
(9)用java呼叫c函式
修改MainActivity.java,完成對JniClient.java中函式的呼叫:
[java] view plaincopyprint?
  1. package com.example.ndktest;  
  2. import android.os.Bundle;  
  3. import android.app.Activity;  
  4. //remove for NdkTest
  5. //import android.view.Menu;
  6. //add for NdkTest
  7. import android.widget.TextView;  
  8. publicclass MainActivity extends Activity {  
  9.     //add for NdkTest
  10.     static {  
  11.         System.loadLibrary("NdkTest");  
  12.     }  
  13.     @Override
  14.     protectedvoid onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         //remove for NdkTest
  17.         //setContentView(R.layout.activity_main);
  18.         //add  for NdkTest
  19.         String str = JniClient.HelloWorld();  
  20.         TextView tv = new TextView(this);  
  21.         tv.setText(str);  
  22.         setContentView(tv);  
  23.     }  
  24.     //remove for NdkTest
  25.     /*@Override 
  26.     public boolean onCreateOptionsMenu(Menu menu) { 
  27.         // Inflate the menu; this adds items to the action bar if it is present. 
  28.         getMenuInflater().inflate(R.menu.main, menu); 
  29.         return true; 
  30.     }*/
  31. }  
package com.example.ndktest;

import android.os.Bundle;
import android.app.Activity;
//remove for NdkTest
//import android.view.Menu;
//add for NdkTest
import android.widget.TextView;

public class MainActivity extends Activity {
	//add for NdkTest
    static {
        System.loadLibrary("NdkTest");
    }
    
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		//remove for NdkTest
		//setContentView(R.layout.activity_main);
		//add  for NdkTest
		String str = JniClient.HelloWorld();
        TextView tv = new TextView(this);
        tv.setText(str);
        setContentView(tv);
	}

	//remove for NdkTest
	/*@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}*/
}