NDK编程入门笔记

来源:互联网 发布:线切割编程软件win7 编辑:程序博客网 时间:2024/06/06 07:04

今天研究了一下NDK 的使用,以为写个hello world还是很简单的,竟然被一堆bug弄了几个小时。所以就详细的研究了一下,先摘一段NDK简介:

一、NDK简介

1.NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。

2.NDK提供了一份稳定、功能有限的API头文件声明
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。

二、为什么使用NDK

1.代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
2.可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
3.提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
4.便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。

至于开发环境搭建,这里就不说了。下面开始


1.创建一个android工程,名字随意,比如 hellondk

2. 在MainActivity中定义被调用方法,用native修饰

package com.example.hellondk;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.widget.TextView;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        TextView tv = new TextView(this);        int a = 1;        int b = 3;        tv.setText(getStringFromC()+"\na + b = "+add(a,b));        setContentView(tv);    }    //这里定义了两个方法,getStringFromC()和add(int,int)    public static native int add(int a, int b);    public static native String getStringFromC();    //这里被static修饰的静态代码块,在类加载时加载,会加载对应的so文    //件,上面声明的两个函数就是在这个so文件中    static {        System.loadLibrary("helloc");    }}

3.项目,右键,新建jni文件夹,然后在jni中新建一个helloc.c文件

#include <string.h>#include <jni.h>/*这些函数可以用javah命令生成,这里先不讲,这个函数名很长,但是却是有                *规律的,包名com.example.hellondk,类名MainActivity,*方法名getStringFromC,以下划线分割*com.example.hellondk.MainActivity.getStringFromC*/jstringJava_com_example_hellondk_MainActivity_getStringFromC( JNIEnv* env,                                                  jobject thiz ){    return (*env)->NewStringUTF(env, "Hello C from JNI !");}jintJava_com_example_hellondk_MainActivity_add( JNIEnv* env,                                                  jobject thiz,                                                  jint a, jint b){    return a+b;}

4.项目,右键,android tools -> add native support

这里写图片描述

在jni目录下会自动新建一个Android.mk文件,内容如下:格式不用记,copy就行。helloc.c是同目录的helloc.c,helloc同System.loadLibrary(“helloc”);,这两个要保持一致

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := hellocLOCAL_SRC_FILES := helloc.cinclude $(BUILD_SHARED_LIBRARY)

5. 构建安装运行

我用的IDE是adt bundle加NDK,如果环境搭建正确,这个时候,就应该能 项目右键 -> build project了,我测试的,没问题,但是c/c++文件有错误,无法通过eclipse安装。貌似是bug,以为编译生成都ok了,说明没有编译错误。项目根目录下面有个bin目录,里面有刚生成的apk文件,adb install hellondk.apk,安装到手机,运行OK
这里写图片描述

到这,已经手动完成了一个简单的NDK 函数调用。

如果是C++文件呢,则有点儿小不同,具体参见这篇博客,讲的很详细
http://blog.sina.com.cn/s/blog_72a98b310100x4r6.html
为防止外部链接失效,还是把代码贴出来吧
两处差异,

  1. 函数块被 extern “C”{}包裹
  2. jstring 类型 变成 JNIEXPORT jstring JNICALL
    同理jint变成JNIEXPORT jint JNICALL,等等
#include <string.h>#include <jni.h>extern "C"{    JNIEXPORT jstring JNICALL    Java_com_example_hellondk_MainActivity_getStringFromC( JNIEnv* env,                                                      jobject thiz ){        return env->NewStringUTF("Hello C++ from JNI !");    }    JNIEXPORT jint JNICALL    Java_com_example_hellondk_MainActivity_add( JNIEnv* env,                                                      jobject thiz,                                                      jint a, jint b){        return a+b;    }}

下面是另一种方式:

1. 第一步一样,在activity中声明native 方法,代码和上面第一步一样

2. 第二步,用javah生成头文件

命令格式
javah -classpath bin/classes;E:\androidsdk\platforms\android-15\android.jar -d jni com.example.hellondk.MainActivity

-classpath 指定了类路径,在这里,指定的是MainActivity的类的路径:
*.class文件就在hellondk/bin/classes/com/example/hellondk/…下面

-d 指定了自动生成的 .h文件的存放目录,最后面跟的是类的全名,即包名加类名

javah 能根据Mactivity的类文件,识别出里面声明的native方法,自动生成上面提到的超长方法名如
Java_com_example_hellondk_MainActivity_getStringFromC
Java_com_example_hellondk_MainActivity_add
下面看一下生成代码

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_hellondk_MainActivity */#ifndef _Included_com_example_hellondk_MainActivity#define _Included_com_example_hellondk_MainActivity#ifdef __cplusplusextern "C" {#endif#undef com_example_hellondk_MainActivity_MODE_PRIVATE#define com_example_hellondk_MainActivity_MODE_PRIVATE 0L#undef com_example_hellondk_MainActivity_MODE_WORLD_READABLE#define com_example_hellondk_MainActivity_MODE_WORLD_READABLE 1L#undef com_example_hellondk_MainActivity_MODE_WORLD_WRITEABLE#define com_example_hellondk_MainActivity_MODE_WORLD_WRITEABLE 2L#undef com_example_hellondk_MainActivity_MODE_APPEND#define com_example_hellondk_MainActivity_MODE_APPEND 32768L#undef com_example_hellondk_MainActivity_MODE_MULTI_PROCESS#define com_example_hellondk_MainActivity_MODE_MULTI_PROCESS 4L#undef com_example_hellondk_MainActivity_BIND_AUTO_CREATE#define com_example_hellondk_MainActivity_BIND_AUTO_CREATE 1L#undef com_example_hellondk_MainActivity_BIND_DEBUG_UNBIND#define com_example_hellondk_MainActivity_BIND_DEBUG_UNBIND 2L#undef com_example_hellondk_MainActivity_BIND_NOT_FOREGROUND#define com_example_hellondk_MainActivity_BIND_NOT_FOREGROUND 4L#undef com_example_hellondk_MainActivity_BIND_ABOVE_CLIENT#define com_example_hellondk_MainActivity_BIND_ABOVE_CLIENT 8L#undef com_example_hellondk_MainActivity_BIND_ALLOW_OOM_MANAGEMENT#define com_example_hellondk_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L#undef com_example_hellondk_MainActivity_BIND_WAIVE_PRIORITY#define com_example_hellondk_MainActivity_BIND_WAIVE_PRIORITY 32L#undef com_example_hellondk_MainActivity_BIND_IMPORTANT#define com_example_hellondk_MainActivity_BIND_IMPORTANT 64L#undef com_example_hellondk_MainActivity_BIND_ADJUST_WITH_ACTIVITY#define com_example_hellondk_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L#undef com_example_hellondk_MainActivity_CONTEXT_INCLUDE_CODE#define com_example_hellondk_MainActivity_CONTEXT_INCLUDE_CODE 1L#undef com_example_hellondk_MainActivity_CONTEXT_IGNORE_SECURITY#define com_example_hellondk_MainActivity_CONTEXT_IGNORE_SECURITY 2L#undef com_example_hellondk_MainActivity_CONTEXT_RESTRICTED#define com_example_hellondk_MainActivity_CONTEXT_RESTRICTED 4L#undef com_example_hellondk_MainActivity_RESULT_CANCELED#define com_example_hellondk_MainActivity_RESULT_CANCELED 0L#undef com_example_hellondk_MainActivity_RESULT_OK#define com_example_hellondk_MainActivity_RESULT_OK -1L#undef com_example_hellondk_MainActivity_RESULT_FIRST_USER#define com_example_hellondk_MainActivity_RESULT_FIRST_USER 1L#undef com_example_hellondk_MainActivity_DEFAULT_KEYS_DISABLE#define com_example_hellondk_MainActivity_DEFAULT_KEYS_DISABLE 0L#undef com_example_hellondk_MainActivity_DEFAULT_KEYS_DIALER#define com_example_hellondk_MainActivity_DEFAULT_KEYS_DIALER 1L#undef com_example_hellondk_MainActivity_DEFAULT_KEYS_SHORTCUT#define com_example_hellondk_MainActivity_DEFAULT_KEYS_SHORTCUT 2L#undef com_example_hellondk_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL#define com_example_hellondk_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L#undef com_example_hellondk_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL#define com_example_hellondk_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L/* * Class:     com_example_hellondk_MainActivity * Method:    add * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_example_hellondk_MainActivity_add  (JNIEnv *, jclass, jint, jint);#ifdef __cplusplus}#endif#endif

上面的大量的宏,忽视它们,重点是最后几行那个函数,很眼熟对不对。
是的,他就是自动生成的,上文中我们手动拼写的那个函数。
有了它,我们就不用在手动拼写,避免错误。
然后,在同目录(jni)下面新建文件helloc.c,记得include这个头文件
helloc.c的内容上文已经贴出,除了引用头文件,其他一样。

3.ndk-build编译c/c++文件

Android.mk文件和上面一样
cmd进入项目根目录,ndk-build开始编译C/C++文件。如果成功,项目会多出一个文件夹obj,里面就是so文件libhelloc.solib + helloc+.so

4.构建安装运行

项目右键,run as Android Applecation
如果还是build正常,但是运行app时显示C/C++有错误,还是手动命令行安装吧,这些bug太头大

因为捉bug弄了一下午,有些问题估计是确实ndk本身的问题,就先搁置了。以后在研究:

  1. 不知道为什么,有时用javah 生成的.h头文件会不一样。比如下面这个

为节省控件,我把宏和一些注释删除了

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>#ifdef __cplusplusextern "C" {#endifJNIEXPORT jstring JNICALL Java_com_example_hellondk3_MainActivity_getStringFromC  (JNIEnv *, jobject);#ifdef __cplusplus}#endif

2.项目编译生成OK,手动安装到手机运行演示OK,但是C/C++文件在IDE报错误,是bug,还是环境配置问题

代码下载

0 0