Android Stuido下NDK的简单实现

来源:互联网 发布:智能化数据分析 编辑:程序博客网 时间:2024/06/04 19:07

JNI是Sun公司定义的一套编程框架标准接口,允许Java代码和本地代码的相互调用.
我们什么情况下会使用JNI技术呢?

  1. 需要注重处理速度
  2. 直接进行硬件控制
  3. 对已有的本地代码进行复用

-加载动态链接库

我们通常接入别人sdk的时候都是使用的这种方法,比如接入新浪的SDK我们会得到一堆的动态链接库,我们需要将他们复制)到我们项目中调用,并将提供的jar文件添加到依赖库中
这里写图片描述
这里写图片描述

那如果我们自己来实现简单的本地方法和调用呢?

1.下载SDK Tools->NDK

在Setting->Android SDK->SDK Tools下下载NDK
这里写图片描述

2.声明本地函数

如果要在Java类中使用本地函数,那么我们需要现在Java类中声明它,声明本地函数时,需要在函数名前面添加关键字”native”关键字进行修饰,如下

package com.jju.yuxin.jnidev;/** * ============================================================================= * Copyright (c) 2016 yuxin All rights reserved. * Packname com.jju.yuxin.jnidev * Created by yuxin. * Created time 2016/10/5 0005 上午 11:10. * Version   1.0; * Describe : 本地函数的声明 * History: * ============================================================================== */public class JniAddUtils {    public static native int add(int a,int b);}

我们在项目中创建了一个叫JniAddUtils 的类,并在里面声明了一个本地方法add,我们准备用它来实现两个数相加的操作.(如果在这一步出现方法找不到之类的错误,我们可以到Setting->Plugins下将Android NDK Support后面的勾去掉,重启android studio就好了)

3.编译文件并生成头文件

我们知道在C这种调用别的文件的方法都是通过引入头文件的方式.在生成头文件之前我们需要先编译java文件让其生成.class文件,编译java文件我们只需要在点击编译项目就可以了.(点击运行项目按钮旁边的Make Project)
我们会在项目下的app\build\intermediates\classes\debug的包路径下找到生成的class文件(build是项目生成的临时文件夹,编译的东都在这个目录下),我们用android studio 下的Terminal cd到这个目录并使用javah -jni 路径的方式生成头文件
这里写图片描述
之后我们会在debug的目录下看到生成的头文件,如:
com_jju_yuxin_jnidev_JniAddUtils.h

/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_jju_yuxin_jnidev_JniAddUtils */#ifndef _Included_com_jju_yuxin_jnidev_JniAddUtils#define _Included_com_jju_yuxin_jnidev_JniAddUtils#ifdef __cplusplusextern "C" {#endif/* * Class:     com_jju_yuxin_jnidev_JniAddUtils * Method:    add * Signature: (II)I */JNIEXPORT jint JNICALL Java_com_jju_yuxin_jnidev_JniAddUtils_add  (JNIEnv *, jclass, jint, jint);#ifdef __cplusplus}#endif#endif

我们在mian下创建一个jni的文件夹,将之前的头文件剪切在这个文件夹中

4.实现本地函数

我们在 刚才创建的jni文件夹下创建一个.c文件用来实现加法运算,函数编写如下,正在编写函数时我们需要忘了将头文件引入,函数名为头文件中生成的函数名
jniadd.c

#include "com_jju_yuxin_jnidev_JniAddUtils.h"//JNIEnv *, jclass是固定参数 jint, jint为传入的两个参数JNIEXPORT jint JNICALL Java_com_jju_yuxin_jnidev_JniAddUtils_add  (JNIEnv * env, jclass thiz, jint a, jint b){       jint result=a+b;       return result;  }

这里写图片描述
其中JNIEnv * env, jclass thiz,这两个参数是固定的参数,因为我们的方法在java中声明为static所以传入的是jclass,如果是非静态方法,那么传入的将是jobject,有人会问jint是什么东西?我们接下来来说一下,如果你知道了可以直接跳到下一步,

在本地函数的参数和返回值类型根据等价约定映射到Java语言中的数据类型
1.基本类型的映射
这里写图片描述
2.引用类型的映射
这里写图片描述
3.引用类型的继承关系
这里写图片描述

4.ndk路径的添加与配置

一般在新版的android stuido你下载完成ndk后他都会帮你在项目的local.properties添加NDK的路径,就像SDK一样,如果没有的话记得添加上去
这里写图片描述
,就像一开始一样我们要用jni我们先需要动态链接库(.so)所以我们需要在mudule的build.gradle中配置要生成的动态链接库的名称和类型(注意添加的位置,是在defaultConfig的里面)

 ndk{            moduleName "AddJniLibName"//名字自拟            abiFilters "armeabi","armeabi-v7a","x86"        }

这里写图片描述
(如果后面运行成功,你会在app\build\intermediates\ndk\debug\lib\下看到三个目录下生成的对应的.so库)

5.代码中加载动态链接库

我们在最开始的JniAddUtils类中用到了本地方法,所以我们需要在那加载动态链接库

public class JniAddUtils {    static {        System.loadLibrary("AddJniLibName"); //加载动态链接库    }    public static native int add(int a,int b);}

6.验证

我们在MainActivity中来验证下能否成功(布局就不写了,就一个TextView)

public class MainActivity extends Activity {    private TextView tv_result;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv_result = (TextView) findViewById(R.id.tv_result);        int addresult = JniAddUtils.add(1, 3);        tv_result.setText("1+3="+addresult);    }}

截图:
这里写图片描述

7.日志的输出

在android代码中能够输出log日志,那怎么在C或C++中代码中输出log日志呢?

1.配置log库
如果你用的是正式版gradle,在ndk标签中加入
ldLibs “log”

如果你用的是实验版gradle,在ndk标签中加入:
ldLibs.add(“log”)

如果你使用CMakeLists,在target_link_libraries标签中加入log

如果你使用的是MK文件,加入如下语句:
LOCAL_LDLIBS := -llog

因为我使用的是Android Studio2.2正式版,那我需要修改ndk标签为

  ndk{            moduleName "AddJniLibName"            abiFilters "armeabi","armeabi-v7a","x86"            ldLibs "log"        }

2.引入头文件
在你需要打印日志的C或C++文件中引入#include<android/log.h>头文件.下面是我C文件修改版

#include <jni.h>#include<android/log.h>#include "com_jju_yuxin_jnidev_JniAddUtils.h"//JNIEnv *, jclass是固定参数 jint, jint为传入的两个参数JNIEXPORT jint JNICALL Java_com_jju_yuxin_jnidev_JniAddUtils_add  (JNIEnv * env, jclass thiz, jint a, jint b){       jint result=a+b;       //Log.i("JNIADD","result="+result);   __android_log_print(ANDROID_LOG_INFO,"JNIADD","result=%d",result);       return result;  }

其他几种日志输出为
ANDROID_LOG_INFO –> Log.i
ANDROID_LOG_WARN –> Log.w
ANDROID_LOG_DEBUG –> Log.d
ANDROID_LOG_ERROR –> Log.e
ANDROID_LOG_FATAL –> Log.a

8.遇到的问题

1.Error:Execution failed for task ‘:app:compileDebugNdk’.

Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set “$USE_DEPRECATED_NDK=true” in gradle.properties to continue using the current NDK integration.

这个错误需要在项目的gradle.properties下添加android.useDeprecatedNdk=true
2. connot delete “app\build\intermediates\classes\debug”
我的解决办法是重启android studio 因为每次重启,他都会重新的编译,也就会重新生成build文件,但是我局的这方法不怎么靠谱,不知道你们的能用不

以上就是我的测试后成功之谈,不当之处还望指出.

本文参考我同学的博文实现,有不清楚的地方,可以去他那看看.
在android studio 2.1 实现简单的ndk

我的博客网站:http://huyuxin.top/欢迎大家访问,评论.

0 0