Android的NDK开发~Hellow world!

1、到Google官网下载adt-bundle----开发Android App的工具打包下载,下载后解压即可,免去开发环境的配置。

     然后下载NDK,建议下载最新版本的,(ps:之前下载过r8a的,有bug,导致编译很慢,r8b就没有),配置ADT中的NDK Path。

     ~~这样子就完成了NDK开发的全部准备了。

2、新建Android项目,step by step,这里就不罗嗦了...

3、ADT v20之后的版本,提供了较为友善的NDK开发支持(ps:所以一定要升级ADT到最新版本哦),Android Tools-->Add Native Support :


Android的NDK开发~Hellow world!
 

 然后是所如so的名称,也就是编译后,库的名称,默认即可,之后可以在mk文件上改的:


Android的NDK开发~Hellow world!

 完成后项目的结构会变成:

 
Android的NDK开发~Hellow world!
 

 我们重点关注jni文件夹的内容,文件里放置的是我们的源码(.h, .c, .cpp)和mk文件(即make文件,告诉编译器应该如何进行编译,详细请自行Google),默认自动生成的cpp基本是空白的,mk文件如下:


Android的NDK开发~Hellow world!
 上面mk文件定义了编译的路径(LOCAL_PATH),include的路径(BUILD_SHARED_LIBRARY),编译后库的名称(LOCAL_MODULE),编译源文件的路径(LOCAL_SRC_FILES)

 4、下面来一个例子:

 Scgps_Client.h

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

#include <android/log.h>

#ifndef LOG_TAG
#define LOG_TAG "android-ndk"
#endif

#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

    头文件除了引用到C的头文件之外,还引用了android方面的头文件log.h,用以实现打印log。

 Scgps_Client.c

#include <Scgps_Client.h>

JNIEXPORT jstring JNICALL Java_com_scgps_client_MainActivity_sayHellow(JNIEnv* env, jobject thiz)
{
	const char * ret = "Hellow Form Ndk";

	LOGI("stationAndLines --> %s", ret);

        return (*env)->NewStringUTF(env, ret);
}

   .c文件里面就是实现Java的回调方法了,主要就是返回字符串“Hellow From Ndk”。

 MainActivity.java

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		Toast.makeText(getApplicationContext(), sayHellow(), Toast.LENGTH_LONG).show();
	}
	
	public native String sayHellow();
	
	static {
              System.loadLibrary("Scgps_Client");
        }

}

    接着,我们一步步地解析:

    1、Java方面只管调用,倒是很简单,使用关键字native,声明本地方法,然后在静态代码块中加载本地库。

    注意,loadLibrary的参数是库的名称,也就是mk文件中LOCAL_MODULE定义的值。

    ps:之前也做过windows下Jni方面的开发,编译出来是dll,然后Java加载的参数就是dll的名称;

           但是ndk编辑的so文件名称却是libScgps_Client,这里有待研究...

Android的NDK开发~Hellow world!
 

    2、本地代码方面,回调方法的定义 JNIEXPORT jstring JNICALL Java_com_scgps_client_MainActivity_sayHellow(JNIEnv* env, jobject thiz); ,稍稍有点复杂,我尽量详述:

         首先是方法名,Java_com_scgps_client_MainActivity_sayHellow,这里与Java层是关系密切的,根据JNI的编程规范,调用的方法必须以Java开头,然后详细描述Java层native方法所在的详细信息,并以“_”分隔,区分大小写!

         因此,sayHellow是Java声明的本地方法,MainActivity是类名,com_scgps_client是包名。

         假如C定义的方法名称与Java声明的名称对不上,运行则会报错,抛出下面的信息:


Android的NDK开发~Hellow world!

  

        友情提示:

        使用JDK的javah生成JNI的头文件!

        先编译Java工程,之后项目目录下会有bin文件夹,bin存放编译好的class文件; 

        使用console,cd到bin目录下:

javah -classpath . -jni com.scgps.client.MainActivity

       然后是参数:JNIEnv* env 和 jobject thiz

       开讲之前,先简单说明一下,Jni中与Java基本数据类型的映射关系:

Android的NDK开发~Hellow world!

      定义都在jni.h的头文件中,各看官可自行查阅,基本上Java中的各种数据类型,jni中都有定义,也提供了相应的转换方法,供C与Java之间传递。

      OK,现在让我们继续分析 Java_com_scgps_client_MainActivity_sayHellow(JNIEnv* env, jobject thiz);

      JNIEnv* env  : 指向 JNI 函数表的指针变量。

      jobject thiz    :  该参数的意义取决于该方法是静态还是实例方法(static or an instance method)。 

  •                           当本地方法作为一个实例方法时,第二个参数相当于对象本身,即 this;
  •                           当本地方法作为一个静态方法时,指向所在类;

      在上面的例子中,Java_com_scgps_client_MainActivity_sayHellow是一个实例方法,因为thiz指向Java对象本身!

      最后是返回值,jstring   

      很明显,这是返回Java字符串类型的值,通过 (*env)->NewStringUTF(env, ret); ,调用JNI函数表中的NewStringUTF方法,转换ret指向的字符串,使其能被Java所识别。

  

      最后的最后,当然就是编译、运行了:

      先看看现在的mk文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Scgps_Client

LOCAL_SRC_FILES := \ Scgps_Client.c \

LOCAL_LDLIBS	:= -llog -landroid

LOCAL_STATIC_LIBRARIES	:= android_native_app_glue
	
include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

      因为引用到Android的库,所以也要在这里声明一下,见LOCAL_LDLIBS和LOCAL_STATIC_LIBRARIES的值。

   

      总结几个开发中的技巧:

           由于CDT(eclipse开发c/c++的插件)开发时,依赖编译的结果,所以提示总是会滞后。如果提示源码中有错,或找不到include依赖时,不妨先查看一下mk文件是否已经描述清楚了,然后再rebuild一下。

           当整个编译的结构有大的改动时,也不要忘了clear project,在build~~。

   

  

相关推荐