前言:

前几篇我主要介绍了jni先关的基础知识和常用API,相信看过的童靴对JNI已经有了一定的了解,如果不了解也没关系,下面我给出了链接,可以点进去学习。接下来我将实战一个完整案例,案例很简单,就是一个简单的计算器。

  1. Android JNI(一)——NDK与JNI基础
  2. Android JNI(二)——实战JNI入门之Hello World
  3. Android JNI(三)——JNI数据结构之JNINativeMethod
  4. Android JNI学习(四)——JNI的常用方法的API
  5. Android JNI学习(五)——Java与Native之间如何实现相互调用

实战效果:

讲解之前我们先看一下实战效果,因为接下来也是围绕这个实现效果一一讲解。

简单计算器

好了,接下来我们就围绕这个效果图开启实战演练之旅吧。

开启实战:

整个开发过程很简单,大致可以分成三步,分别是如下三步:

1. 编写Android代码    

包括了xml布局,以及activity的逻辑处理方法.

2. 编写Java代码用于生成头文件

       主要用于生成对应得.h头文件。

3. 实现jni代码(native代码) 

    native具体的实现方法。

1.编写Android代码

Android的代码包括俩部分,分别是布局和activity,这里不过多叙述,直接上对应得代码。

1.1 xml 对应得ui布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".jni.CalculationActivity"><TextViewandroid:layout_marginTop="5dp"android:textSize="32sp"android:textStyle="bold"android:textColor="@color/colorAccent"android:layout_margin="10dp"android:gravity="center"android:text="c/c++和Java,Kotlin相互传值调用"android:layout_width="match_parent"android:layout_height="wrap_content" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/inputOne"android:hint="请输入第一个数字"android:inputType="number"android:layout_weight="1.0"android:layout_width="match_parent"android:layout_height="wrap_content" /><EditTextandroid:inputType="number"android:id="@+id/inputTwo"android:hint="请输入第二个数字"android:layout_weight="1.0"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout><TextViewandroid:layout_marginTop="5dp"android:textSize="22sp"android:textStyle="bold"android:text="请选择计算类型:"android:layout_width="match_parent"android:layout_height="wrap_content" /><LinearLayoutandroid:layout_marginTop="5dp"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><Buttonandroid:gravity="center"android:layout_weight="1.0"android:text="加法"android:id="@+id/add"android:layout_width="match_parent"android:layout_height="wrap_content" /><Buttonandroid:gravity="center"android:layout_weight="1.0"android:text="减法"android:id="@+id/sub"android:layout_width="match_parent"android:layout_height="wrap_content" /><Buttonandroid:gravity="center"android:layout_weight="1.0"android:text="乘法"android:id="@+id/mul"android:layout_width="match_parent"android:layout_height="wrap_content" /><Buttonandroid:gravity="center"android:layout_weight="1.0"android:text="除法"android:id="@+id/div"android:layout_width="match_parent"android:layout_height="wrap_content" /></LinearLayout><TextViewandroid:layout_marginTop="5dp"android:textSize="22sp"android:textStyle="bold"android:id="@+id/cal_result"android:text="计算结果:"android:layout_width="wrap_content"android:layout_height="wrap_content" /></LinearLayout>

1.2 activity的代码

package com.bnd.multimedialearning.jniimport android.os.Bundle
import android.text.TextUtils
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.bnd.multimedialearning.R
import kotlinx.android.synthetic.main.activity_calculation.*
import org.jetbrains.anko.toastclass CalculationActivity : AppCompatActivity(),View.OnClickListener {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_calculation)add.setOnClickListener(this)sub.setOnClickListener(this)mul.setOnClickListener(this)div.setOnClickListener(this)}override fun onClick(v: View?) {val viewId=v?.idval strOne: String = inputOne.text.trim().toString()val strTwo: String = inputTwo.text.trim().toString()if (TextUtils.isEmpty(strOne)||TextUtils.isEmpty(strTwo)){toast("请输入要计算的俩个数字!")return}val one = strOne.toInt()val two = strTwo.toInt()when(viewId){R.id.add  -> calculationAdd(one,two)R.id.sub  -> calculationSub(one,two)R.id.mul  -> calculationMul(one,two)R.id.div  -> calculationDiv(one,two)else ->   calculationAdd(one,two)}}private fun calculationAdd(one: Int, two: Int) {val result = JNICalculationTools.addition(one,two)cal_result.text="计算结果:${result}"}private fun calculationSub(one: Int, two: Int) {val result = JNICalculationTools.subtraction(one,two)cal_result.text="计算结果:${result}"}private fun calculationMul(one: Int, two: Int) {val result = JNICalculationTools.multiplication(one,two)cal_result.text="计算结果:${result}"}private fun calculationDiv(one: Int, two: Int) {val result = JNICalculationTools.division(one,two)cal_result.text="计算结果:${result}"}}

2. 编写Java代码用于生成头文件

2. 1编写逻辑java类

我们把运算的逻辑抽象出来,用一个来表示。代码如下:

package com.bnd.multimedialearning.jniobject JNICalculationTools {//加法external fun addition(a: Int, b: Int): Int//减法external fun subtraction(a: Int, b: Int): Int//乘法external fun multiplication(a: Int, b: Int): Int//除法external fun division(a: Int, b: Int): Intinit {System.loadLibrary("JNICalculationTools")}
}

好了,接下来就是要生成头文件了。

2. 2 生成头文件

在上一篇我们讲过如何生成.h头文件,这里不做过多描述,直接通过如下命令一步生成:

javac -encoding utf8 -h . 类名.java

执行命令,生成后缀是.h的文件如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_bnd_multimedialearning_jni_JNICalculationTools */#ifndef _Included_com_bnd_multimedialearning_jni_JNICalculationTools
#define _Included_com_bnd_multimedialearning_jni_JNICalculationTools
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     com_bnd_multimedialearning_jni_JNICalculationTools* Method:    addition* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_com_bnd_multimedialearning_jni_JNICalculationTools_addition(JNIEnv *, jclass, jint, jint);/** Class:     com_bnd_multimedialearning_jni_JNICalculationTools* Method:    subtraction* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_com_bnd_multimedialearning_jni_JNICalculationTools_subtraction(JNIEnv *, jclass, jint, jint);/** Class:     com_bnd_multimedialearning_jni_JNICalculationTools* Method:    multiplication* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_com_bnd_multimedialearning_jni_JNICalculationTools_multiplication(JNIEnv *, jclass, jint, jint);/** Class:     com_bnd_multimedialearning_jni_JNICalculationTools* Method:    division* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_com_bnd_multimedialearning_jni_JNICalculationTools_division(JNIEnv *, jclass, jint, jint);#ifdef __cplusplus
}
#endif
#endif

3. 实现jni代码(native代码)

到了这里,准备工作准备的差不多了,回想一下如何编写native代码了。

由于我们不是通过javah来生成.c文件,所以我们要创建一个jni的文件夹,然后创建一个JNICalculationTools.c文件。这时候JNICalculationTools.c文件里面应该什么都没有,我们看到JNICalculationTools这类里有4个native方法,所以我们要也要在JNICalculationTools.c里面声明这4个方法。

3.1 声明与之对应得native方法

jint addition(JNIEnv *env,jclass clazz,jint a,jint b);jint subtraction(JNIEnv *env,jclass clazz,jint a,jint b);jint multiplication(JNIEnv *env,jclass clazz,jint a,jint b);jint division(JNIEnv *env,jclass clazz,jint a,jint b);

然后就是实现Java类与之对应得native方法。

3.2 实现对应Java层的native方法。

jint addition(JNIEnv *env,jclass clazz,jint a,jint b){return a+b;
}jint subtraction(JNIEnv *env,jclass clazz,jint a,jint b){return a-b;
}jint multiplication(JNIEnv *env,jclass clazz,jint a,jint b){return a*b;
}jint division(JNIEnv *env,jclass clazz,jint a,jint b){return a/b;
}

到了这里改实现的方法均已经实现,这样是否就已经完成了功能,答案是肯定不能的,通过之前的讲解,我们发现,到目前为止,虽然功能实现了,但是我们尚未实现jni的注册,接下来就是改实现注册了,这也在最重要的一个环节。

通过以前的讲解,我们知道jni的注册有俩种方式,一种是静态注册,一种是动态注册,今天,我就以动态注册方式实现。

3.3 动态注册jni

首先我们要引入生成的头文件。

3.3.1 引入头文件

//引入头文件
#include "JNICalculationTools.h"

3.3.2  重写JNI_OnLoad(JavaVM* vm, void* reserved)函数

依照前面动态注册方法的步骤,我们要重写JNI_OnLoad(JavaVM* vm, void* reserved)函数。所以我们在JNICalculationTools.c中重写函数这个函数,如下:

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){return JNI_VERSION_1_6;
}

为了方便打印日志,来帮助我们识别是否进入这个方法,所以我们要配置一个log,这时候,我们创建一个Android.mk文件,然后进行如下的编辑:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)#解决 error: undefined reference to '__android_log_print'
LOCAL_LDLIBS    := -lm -llogLOCAL_MODULE    := JNICalculationToolsLOCAL_SRC_FILES := JNICalculationTools.cinclude $(BUILD_SHARED_LIBRARY)

然后在引入然后在JNICalculationTools.c添加#include <android/log.h>代码,引入日志:

#include <jni.h>
#include <android/log.h>
#include <stdio.h>
//引入日志功能
#define  LOG_TAG  "NATIVE_LOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

注意:

我们在编译的时候可能经常会遇到error: undefined reference to '__android_log_print'这个异常,你可能很纳闷,自己明明引入了<android/log.h>文件,但是还报错,难道是用法错了,其实不是,这需要我们在Android.mk文件里添加这样一句话:LOCAL_LDLIBS    := -lm -llog;详细配置可以看上面的Android.mk配置文件,有特别注释。

3.3.3 开始编写注册代码

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
LOGD("enter jni_onload");JNIEnv* env = NULL;
jint result = -1;if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_6)!= JNI_OK){return result;}const JNINativeMethod method[]={{"addition","(II)I",(void*)addition},{"subtraction","(II)I",(void*)subtraction},{"multiplication","(II)I",(void*)multiplication},{"division","(II)I",(void*)division}
};jclass jClassName=(*env)->FindClass(env,"com/bnd/multimedialearning/jni/JNICalculationTools");jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);if (ret != JNI_OK) {LOGD("jni_register is Error!");
return -1;
}return JNI_VERSION_1_6;}

补充一点,有的人到了这里可能会失败,失败的原因可能有多种,最后失败,大多数都是签名的问题。下面我们通过Javap命令查看一下签名。

3.3.4 查看签名是否正确

生成的JNICalculationTools.class文件如下:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//package com.bnd.multimedialearning.jni;public class JNICalculationTools {public JNICalculationTools() {}public static native int addition(int var0, int var1);public static native int subtraction(int var0, int var1);public static native int multiplication(int var0, int var1);public static native int division(int var0, int var1);static {System.loadLibrary("JNICalculationTools");}
}

得到了class文件下面我们就可以通过javap命令查看签名了:

javap -s -p JNICalculationTools.class

签名信息如下:

Compiled from "JNICalculationTools.java"
public class com.bnd.multimedialearning.jni.JNICalculationTools {public com.bnd.multimedialearning.jni.JNICalculationTools();descriptor: ()Vpublic static native int addition(int, int);descriptor: (II)Ipublic static native int subtraction(int, int);descriptor: (II)Ipublic static native int multiplication(int, int);descriptor: (II)Ipublic static native int division(int, int);descriptor: (II)Istatic {};descriptor: ()V
}

然后我们一一比对方法名称,参数个数,参数类型,发现没有任何问题,到了这里我们基本就完成了jni整个流程的注册。就下来就是在.gradle配置文件里核对配置了。

3.3.5 .gradle配置文件核对配置

        ndk{         moduleName "JNICalculationTools"abiFilters "armeabi-v7a"ldLibs "log"}

注意配置的gradle文件千万不要错了,是在app目录下,看一下此时完整的配置目录结构:

最后附上完整的JNICalculationTools.c代码:

//引入头文件
#include "JNICalculationTools.h"
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
//引入日志功能
#define  LOG_TAG  "NATIVE_LOG"
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)jint addition(JNIEnv *env,jclass clazz,jint a,jint b);jint subtraction(JNIEnv *env,jclass clazz,jint a,jint b);jint multiplication(JNIEnv *env,jclass clazz,jint a,jint b);jint division(JNIEnv *env,jclass clazz,jint a,jint b);jint addition(JNIEnv *env,jclass clazz,jint a,jint b){return a+b;
}jint subtraction(JNIEnv *env,jclass clazz,jint a,jint b){return a-b;
}jint multiplication(JNIEnv *env,jclass clazz,jint a,jint b){return a*b;
}jint division(JNIEnv *env,jclass clazz,jint a,jint b){return a/b;
}JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
LOGD("enter jni_onload");JNIEnv* env = NULL;
jint result = -1;if((*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_6)!= JNI_OK){return result;}const JNINativeMethod method[]={{"addition","(II)I",(void*)addition},{"subtraction","(II)I",(void*)subtraction},{"multiplication","(II)I",(void*)multiplication},{"division","(II)I",(void*)division}
};jclass jClassName=(*env)->FindClass(env,"com/bnd/multimedialearning/jni/JNICalculationTools");jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);if (ret != JNI_OK) {LOGD("jni_register is Error!");
return -1;
}return JNI_VERSION_1_6;}

配置无误后就可以实现实战演练的效果图了,运行一下看一下最终的效果图如下:

好了,至此,关于jni的基础好实战知识已经讲解完毕了,希望对刚入门,或者即将入门的你有所帮助,同时,如果有写的不好的地方,希望路过的大佬指出,希望大家可以共同进步。

Android JNI学习(六)——Java与Native实战演习相关推荐

  1. Android JNI学习(五)——Java与Native之间如何实现相互调用

    本章将讲述Java与Native之间如何实现相互调用.我将围绕围绕如下三点来讲解. #mermaid-svg-qeVnGlVrLWrB5ryX .label{font-family:'trebuche ...

  2. Android JNI学习(四)——JNI的常用方法的API

    前三篇主要讲解了jni基础相关的理论知识,今天主要讲解一下JNI的常用方法的API,掌握了基本的理论知识和常用的API接下来才能更好的实战. jni的常用API大纲 再看API前,我建议大家主要结合官 ...

  3. android jni中的java调c的两种方法

    Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数. 也就是java虚拟机通过一种机制可以找到对应的C函数  这里就涉及到静态注册和动态注册jni函数的方法 一.这里 ...

  4. android jni c调用java,Android学习JNI,使用C调用JAVA语言

    本节学习使用C语言调用JAVA语言.在生活中比如我们某些底层的硬件必须使用C语言去编写,当C语言返回的某些数值显示在界面上时,就比如使用JNI.这样可以将C返回的值显示在界面上. 本节模拟传感器返回的 ...

  5. Android JNI 学习(十):String Operations Api Other Apis

    一.String Operations(字符串操作) 1. NewString jstring NewString(JNIEnv *env, const jchar *unicodeChars, js ...

  6. Android JNI 学习(十一):Invocation Api

    1. 简介 Invocation API允许软件提供商在原生程序中内嵌Java虚拟机.因此可以不需要链接任何Java虚拟机代码来提供Java-enabled的应用程序. 以下代码演示如何使用: #in ...

  7. Android多媒体学习六:访问网络上的Audio对应的M3U文件,实现网络音频流的播放

    Android中提供了对网络上流媒体的支持,我们可以使用MediaPlayer类来播放一个网络上的音频文件. 但是网络上的站点并不建议我们直接访问流,我们需要获取他提供的M3U文件,根据M3U文件来实 ...

  8. Android Framework学习(六)之RefBase,SP,WP

    Android中通过引用计数来实现智能指针,并且实现有强指针与弱指针.由对象本身来提供引用计数器,但是对象不会去维护引用计数器的值,而是由智能指针来管理. 要达到所有对象都可用引用计数器实现智能指针管 ...

  9. android java服务,Android进阶学习必会:Java Binder中的系统服务

    前言 这个知识点是Android进阶学习必须掌握的知识点之一,也是高阶Android架构师经常问到的点.在这里分想给大家,希望对大家的工作和学习有所帮助.喜欢本文的记得点赞关注哦~ 在前面的Andro ...

最新文章

  1. 博客园今天早上是不是出现什么问题了?
  2. 一文总结词向量的计算、评估与优化
  3. 网络工程学习资料2---IEEE 802 标准集合
  4. kong组件_Kong插件开发工具包
  5. 11月碎碎念-谈职场礼貌
  6. openlayers加载svg,如何在OpenLayers-3中将SVG图像用作地图标记?
  7. Paint X for Mac的用法
  8. python成长之路--python的安装与配置 pycharm的安装与激活
  9. 电子工程师私藏的一个网站
  10. 12306列车时刻表查询api功能实现
  11. UVA1389 Hard Life
  12. hdu5755 Gambler Bo(高斯消元)
  13. 西门子 Prodave通讯
  14. 基于内容可变长度分块Content Defined Chunking
  15. 磁盘管理以及文件系统管理
  16. git 远端更新合并到本地
  17. oracle文件快速入库,文件入库ORACLE自动化脚本
  18. 机器人彩铅画_高达机器人铅笔画图片
  19. GS1条形码为什么那么贵?有什么便宜的办法吗?
  20. ATH9K Driver Learning Part II: Important Transmission Functions

热门文章

  1. SQL 2016 性能调优培训来了!!! 还免费!!!
  2. 灌醉茅台董事长拿到便宜酒?潘长江和茅台双双回应...
  3. 哔哩哔哩公布2021年度弹幕:“破防了”
  4. 雷军接连退出多家小米关联公司董事职务
  5. 华为鸿蒙OS 2.0系列Beta 2发布:逼近公测版
  6. 荣耀V40将采用300Hz 触控采样率,1月18日正式发布!
  7. 开着开着,Model S天窗飞了!特斯拉回应...
  8. 蚂蚁集团暂缓两地上市,重新上市或推迟半年,阿里股价相继大跌...
  9. 李开复“口误”惹事,人脸隐私数据合作?蚂蚁、旷视大喊冤枉!
  10. 马斯克:2024年送人上火星 2050年建城