Android Stuido下NDK的简单实现

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 __cplusplus
extern "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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值