您的位置:首页 > 移动开发 > Android开发

Android NDK使用第一步,编译C文件,声明jni并调用

2017-11-02 20:33 435 查看
<project> - 你的Android应用程序工程的目录
<ndk> - 你的ndk安装的目录

捷径

这里先扯一句题外话 -- 如果你不需要使用NDK开发,只是需要使用第三方用NDK开发的库,那么你只需要这样做:

把第三方提供的libxxx.so放到你的<project>/libs/armeabi/下, 然后在程序随便什么地方中加入

static {

    System.loadLibray("xxx");

}

就可以使用该库了。

安装NDK

NDK的安装很简单:

1. 首先要将SDK升级至最新,然后下载ndk(可能要FQ,恩恩)将它解压到某个目录<ndk>下。

2. 运行: <ndk>/build/host-setup.sh (如果你使用的是r4版本,可以跳过此步。)

3. 如果成功的话就OK了,如果失败的话检查一下你是不是下载了正确的ndk版本(例如你的操作系统是linux而下载了windows版的ndk).

Java部分

现在我们用一个很简单的例子来说明NDK的使用。我们在eclipse中新建一个android工程,其中:

Project Name:jnitest

Build Target: Android 1.6

Application Name: JNI Test

Package Name: org.eshock.jnitest

Create Activity: JNITest

 

JNITest.java:

package org.eshock.jnitest;

 

import android.app.Activity;

import android.os.Bundle;

public class JNITest extends Activity {

    public native int plus (int x, int y);

    

    /** Called when the activity is first created. */

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        int x = plus(1, 2);

        android.util.Log.d("jni", String.valueOf(x));

    }

    

    static {

        System.loadLibrary("mylib");

    }

}

我们只是演示NDK,所以就不要界面了。在这个程序中,我们调用一个c语言编写的plus(int, int)函数来计算1+2的值,然后在log中打印它。这个库的名称叫做mylib。

要使用一个c语言的函数,需要在java中声明:

public native int plus(int x, int y);

这样java编译器就知道这个函数是外部库中实现的。

C部分

接下来我们使用C语言实现这个plus函数。

创建<project>/jni/mylib.c:

#include <string.h>

#include <jni.h>

 

JNIEXPORT jint JNICALL

Java_org_eshock_jnitest_JNITest_plus( JNIEnv* env,

                                                  jobject thiz,

                                                  jint x,

                                                  jint y )

{

    return x + y;

}

这里我们看到,mylib.c里的plus函数比java里面的plus函数的签名要复杂了很多,主要是plus前面增加了包名,函数中多了两个参数,以及所有的int变成了jint。关于c中类型与jni中类型的对应关系,可以参见jni的相关文档,例如core JavaII中的最后一章。如果你不想去查阅文档,有一个方法:

首先随便在某个目录下创建一个文件JNITest.java:

public class JNITest {

    public native int plus (int x, int y);

}

然后在命令行下执行:

javac JNITest.java && javah JNITest

将生成一个文件JNITest.h:

/* DO NOT EDIT THIS FILE - it is machine generated */

#include <jni.h>

/* Header for class JNITest */

 

#ifndef _Included_JNITest

#define _Included_JNITest

#ifdef __cplusplus

extern "C" {

#endif

/*

 * Class:     JNITest

 * Method:    plus

 * Signature: (II)I

 */

JNIEXPORT jint JNICALL Java_JNITest_plus

  (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus

}

#endif

#endif

这里自动生成了java中native函数对应的签名,你可以在c代码中使用它。(记得修改包名)。

Android。mk和application。mk

Android。mk是一个makefie,用来告诉NDK需要编译哪些文件,生成哪些模块。我们创建<jni>/Android.mk文件:

LOCAL_PATH := $(call my-dir) c源代码文件的位置

 

include $(CLEAR_VARS)

LOCAL_MODULE    := mylib 生成的共享库

LOCAL_SRC_FILES := mylib.c c代码的文件

include $(BUILD_SHARED_LIBRARY)

其中LOCAL_PATH表示c源代码文件的位置;LOCAL_MODULE表示生成的共享库的名称;LOCAL_SRC_FILES代表c代码的文件。不需要把头文件列在里面;头文件的依赖关系是ndk自动计算的。

 

Application.mk的作用是告诉Android SDK需要哪些库文件。有了它,NDK就可以把库放在正确的位置。我们创建<project>/Application.mk:

APP_PROJECT_PATH := $(call my-dir) android工程所在的目录

APP_MODULES      := mylib工程需要的库

APP_PROJECT_PATH代表android工程所在目录,在本例中它被放在<project>中; APP_MODULES表示工程需要的库,如果有多个以空格分开。

编译共享库

由于ndk规定Application.mk必须放在<ndk>/apps/project_name,我们在<ndk>下创建一个符号连接:

ln -s <project> <ndk>/apps/jnitest

这样,我们就可以开始编译共享库了:

在<ndk>目录下运行:

make APP=jnitest

Android NDK: Building for application 'jnitest'

Compile thumb  : mylib <= apps/jnitest/jni/mylib.c

SharedLibrary  : libmylib.so

Install        : libmylib.so => apps/jnitest/libs/armeabi

可以看到编译成功,并在<project>/libs/armeabi/下生成了libmylib.so。

测试

在运行之前,首先确认一下你的工程里面各个文件都齐全,应该类似于这样:

然后测试,正常的话应该能看到log中有输出3.

------------------------------------------------------------------------------------------------------

enjoy it。

========================================================》》》》》》》》》》》》》》》》》》

=============================================================================》》》》》》》》》》》》

==========================================================================================》》》》》》》》》》》》》

javah -jni命令,是根据java文件生成.h头文件的,会自动根据java文件中的类名(包含包名)与方法名生成对应的C/C++里面的方法名。

Android.mk和Application.mk。

Android.mk,注意LOCAL_MODULE的值与之前的名字相对应,LOCAL_SRC_FILES的值写c++文件的名字,这两个值成对设置,可设置多组。(:=是赋值的意思,$是引用某变量的值。)

里面的符号正确的应该是:=,代码中已更正,图片里面的更换麻烦就没改了。很奇怪,我当初写的时候编译运行好像是没出错是正常的…(Tips.20170519)

LOCAL_PATH := $(call my-dir)     // 设置当前的编译目录(Android.mk所在的目录)

include $(CLEAR_VARS)            // 清除LOCAL_XX变量(LOCAL_PATH除外)
LOCAL_MODULE := JNI_ANDROID_TEST  // 指定当前编译模块的名称
LOCAL_SRC_FILES := jnitest.cpp    // 编译模块需要的源文件
include $(BUILD_SHARED_LIBRARY) // 指定编译出的库类型,BUILD_SHARED_LIBRARY:动态库;BUILD_STATIC_LIBRARY:静态库, BUILD_EXECUTEABLE指:可执行文件


====================================》

===================================================》

==================================================================》

在一个Android.mk文件中配置多个Module的方式如下(include$(CLEAR_VARS)、include
$(BUILD_SHARED_LIBRARY)两个语句也需要加上):

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := JNI_STATIC_ANDROID_TEST
LOCAL_SRC_FILES := jnistaticutils.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := JNI_DYNAMIC_ANDROID_TEST
LOCAL_SRC_FILES := jnidynamicutils.cpp
include $(BUILD_SHARED_LIBRARY)
1
2
3
4
5
6
7
8
9
10
11

Application.mk,APP_ABI有四种类型(默认armeabi),armeabi、armeabi-v7a、x86、mips,设置时以空格隔开,all表示所有。该文件中有个可选配置的APP_MODULES,类似于上面Android.mk文件中的LOCAL_MODULE,以空格隔开,且会覆盖掉Android.mk文件中的LOCAL_MODULE设置(比如Android.mk文件中的写了两个jni库的配置,LOCAL_MODULE := JNI1、LOCAL_MODULE := JNI2,而Application.mk中设置的APP_MODULES
:= JNI1,则只能生成JNI1的so文件,要生成JNI2的so文件的时候会报错,除非写成APP_MODULES := JNI1 JNI2,这里我们直接省略默认使用Android.mk中的)。

APP_ABI := all
1

接下来我们需要对C++文件执行ndk-build操作,生成相应的so文件。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐