Android JNI学习(五)——Demo演示
本系列文章如下:
我们这里做一个简单的计算器demo,其中运算的逻辑由Native实现,而且我们采用动态注册的方式来实现
样式大概如下:

里面有两个输入框,下面有4个按钮,代表加减乘除,最下面有个TextView,表示结果。
我们把运算的逻辑抽象出来,用一个JNITools来表示。代码如下:
public class JNITools {
static {
System.loadLibrary("jnidemo3");
}
//加法
public static native int add(int a,int b);
//减法
public static native int sub(int a,int b);
//乘法
public static native int mul(int a,int b);
//除法
public static native int div(int a,int b);
}
然后,我们先用来看下对应的布局文件如下:
<?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="com.gebilaolitou.jni.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="@+id/inputa"
android:hint="请输入a"
android:inputType="number"
android:layout_weight="1.0"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<EditText
android:inputType="number"
android:id="@+id/inputb"
android:hint="请输入b"
android:layout_weight="1.0"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:text="请选择符号"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:gravity="center"
android:layout_weight="1.0"
android:text="@string/add"
android:id="@+id/add"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:gravity="center"
android:layout_weight="1.0"
android:text="@string/sub"
android:id="@+id/sub"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:gravity="center"
android:layout_weight="1.0"
android:text="@string/mul"
android:id="@+id/mul"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:gravity="center"
android:layout_weight="1.0"
android:text="@string/div"
android:id="@+id/div"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:id="@+id/result"
android:text="计算结果"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
这时候,我们来看下对应的MainActivity
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button btnAdd,btnSub,btnMul,btnDiv;
private EditText inputA,inputB;
private TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupView();
addListener();
}
private void addListener() {
btnAdd.setOnClickListener(this);
btnDiv.setOnClickListener(this);
btnMul.setOnClickListener(this);
btnSub.setOnClickListener(this);
}
private void setupView() {
btnAdd=this.findViewById(R.id.add);
btnDiv=this.findViewById(R.id.div);
btnMul=this.findViewById(R.id.mul);
btnSub=this.findViewById(R.id.sub);
inputA=this.findViewById(R.id.inputa);
inputB=this.findViewById(R.id.inputb);
tvResult=this.findViewById(R.id.result);
}
@Override
public void onClick(View v) {
double result=0;
String strA=inputA.getText().toString();
String strB=inputB.getText().toString();
int a=Integer.parseInt(strA);
int b=Integer.parseInt(strB);
switch (v.getId()){
case R.id.add:
result=JNITools.add(a,b);
break;
case R.id.div:
result=JNITools.div(a,b);
break;
case R.id.mul:
result=JNITools.mul(a,b);
break;
case R.id.sub:
result=JNITools.sub(a,b);
break;
}
tvResult.setText(""+result);
}
}
由于我们不是通过javah来生成.c文件,所以我们要创建一个jni的文件夹,然后创建一个jnitools.c文件。这时候jnitools.c文件里面应该什么都没有,我们看到JNITools这个文件有4个native方法,所以我们要也要在jnitools.c里面声明这4个方法。如下:
jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b);
jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b);
jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b);
jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b);
依次对应Java层的native方法,然后我们写出这些方法的具体实现,如下;
jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a+b;
}
jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a-b;
}
jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a*b;
}
jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a/b;
}
最后,然后加入引用,如下:
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
依照前面动态注册方法的步骤,我们要重写JNI_OnLoad(JavaVM* vm, void* reserved)函数。所以我们在jnitools.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)
LOCAL_MODULE := jnidemo3
LOCAL_SRC_FILES := jnitools.c
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog
include $(BUILD_SHARED_LIBRARY)
然后在jnitools.c添加#include <android/log.h>代码
这时候,开始注册代码,我们开始编写注册代码
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
//打印日志,说明已经进来了
__android_log_print(ANDROID_LOG_DEBUG,"JNITag","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[]={
{"add","(II)I",(void*)addNumber},
{"sub","(II)I",(void*)subNumber},
{"mul","(II)I",(void*)mulNumber},
{"div","(II)I",(void*)divNumber}
};
//找到对应的JNITools类
jclass jClassName=(*env)->FindClass(env,"com/gebilaolitou/jni/JNITools");
//开始注册
jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);
//如果注册失败,打印日志
if (ret != JNI_OK) {
__android_log_print(ANDROID_LOG_DEBUG, "JNITag", "jni_register Error");
return -1;
}
return JNI_VERSION_1_6;
}
这里补充一下,很多人,最后失败,都是签名的问题,可以建议使用java -p 命令行来对比签名,我这是这样操作的。首先打开JNITools.class,如下图:

然后打开Android Studio里面的Terminal,然后拖拽JNITools.class到Terminal。这样Terminal就自动打开了,然后我们在Terminal里面输入pwd,就已经在这个目录里面了。然后我们在Terminal输入如下代码
javap -s JNITools.class
Terminal就会输出这个类的所有签名,如下:
Compiled from "JNITools.java"
public class com.gebilaolitou.jni.JNITools {
public com.gebilaolitou.jni.JNITools();
descriptor: ()V
public static native int add(int, int);
descriptor: (II)I
public static native int sub(int, int);
descriptor: (II)I
public static native int mul(int, int);
descriptor: (II)I
public static native int div(int, int);
descriptor: (II)I
static {};
descriptor: ()V
}
我们在这里核对下我们注册方法的签名。
最后,我们在对应的build.gradle里面defaultConfig里面添加如下代码:
ndk{
moduleName "jnidemo3"
abiFilters 'x86','armeabi-v7a','arm64-v8a'
ldLibs "log"
}
这时候我们目录结构如下:

jnitools.c的内容如下:
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b);
jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b);
jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b);
jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b);
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
__android_log_print(ANDROID_LOG_DEBUG,"JNITag","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[]={
{"add","(II)I",(void*)addNumber},
{"sub","(II)I",(void*)subNumber},
{"mul","(II)I",(void*)mulNumber},
{"div","(II)I",(void*)divNumber}
};
jclass jClassName=(*env)->FindClass(env,"com/gebilaolitou/jni/JNITools");
jint ret = (*env)->RegisterNatives(env,jClassName,method, 4);
if (ret != JNI_OK) {
__android_log_print(ANDROID_LOG_DEBUG, "JNITag", "jni_register Error");
return -1;
}
return JNI_VERSION_1_6;
}
jint addNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a+b;
}
jint subNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a-b;
}
jint mulNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a*b;
}
jint divNumber(JNIEnv *env,jclass clazz,jint a,jint b){
return a/b;
}
至此,整个代码已经全部完成。我们可以运行下试试。其实挺简单的。
作者:隔壁老李头
链接:https://www.jianshu.com/p/0f34c097028a
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Android JNI学习(五)——Demo演示的更多相关文章
- Android JNI 学习(五):References Api
1. NewGlobalRef(创建全局引用) jobjectNewGlobalRef(JNIEnv *env, jobject obj); 创建 obj 参数所引用对象的新全局引用.obj 参数既可 ...
- Android JNI学习(四)——JNI的常用方法的中文API
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI学习(三)——Java与Native相互调用
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI学习(二)——实战JNI之“hello world”
本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...
- Android JNI 学习(一):JNI 简介
JNI 即 Java Native Interface 是 native 编程接口,它允许在Java虚拟机(VM)内运行Java代码与其他编程语言(主要是C和C++)编写的应用程序和库进行交互操作. ...
- Android JNI 学习(十一):Invocation Api
1. 简介 Invocation API允许软件提供商在原生程序中内嵌Java虚拟机.因此可以不需要链接任何Java虚拟机代码来提供Java-enabled的应用程序. 以下代码演示如何使用: #in ...
- Android JNI 学习(六):Object Operations Api
一.AllocObject jobjectAllocObject(JNIEnv *env, jclass clazz); 分配新 Java 对象而不调用该对象的任何构造函数.返回该对象的引用. cla ...
- Android JNI 学习(二):JNI 设计机制
本章我们重点说明以下JNI设计的问题,本章中提到的大多数设计问题都与native方法有关.至于调用相关的API的设计,我们会在后面进行介绍. 一.JNI接口函数和指针 native 代码通过调用JNI ...
- android JNI学习之一
执行System.loadLibrary()函数时,VM会反向调用*.so里的JNI_OnLoad()函数.用途有二:1. VM询问此*.so使用的JNI版本编号.2. VM要求*.so做一些初期设定 ...
随机推荐
- http协议基础(三)几种数据传输方式
说说http协议的一些特点: 1)无状态 http协议是一种自身不对请求和响应之间的通信状态进行保存的协议,即无状态协议. 这种设置的好处是:更快的处理更多的请求事务,确保协议的可伸缩性 不过随着we ...
- JS中的对象数组
<html> <head> <title>对象数组的字符串表示</title> <script type="text/javascrip ...
- Trove系列(五)—Trove的数据存储管理程序类型和版本管理功能介绍
功能描述数据存储管理程序(Datastore)类型管理允许Trove的用户从操作者列出的名单中选择数据库存储管理程序和版本.操作者将可以控制数据库存储管理程序的类型,添加一个新的版本并去活一个老版本. ...
- zw版【转发·台湾nvp系列Delphi例程】HALCON SetComprise2
zw版[转发·台湾nvp系列Delphi例程]HALCON SetComprise2 procedure TForm1.Button1Click(Sender: TObject);var op : H ...
- hdu5195 二分+线段树+拓扑序
这题说的给了n个点m条边要求保证是一个有向无环图,可以删除至多k条边使得这个图的拓扑序的字典序最大,我们知道如果我们要排一个点的时候一定要考虑比他大的点是否可以.通过拆边马上拆出来,如果可以拆当然是拆 ...
- input文本框只能输入数字
HTML中的input文本框有时候需要数字的做输入检查,如果能做输入之前限定只能输入数字的话,就可以省去在提交时候的输入内容检查了. 下面是自己在火狐浏览器上调试出的实现,实现原理就是在键盘事件(on ...
- linux常用命令:ifconfig 命令
许多windows非常熟悉ipconfig命令行工具,它被用来获取网络接口配置信息并对此进行修改.Linux系统拥有一个类似的工具,也就是ifconfig(interfaces config).通常需 ...
- SNMP学习笔记之SNMP TRAP简介、流程以及使用Python实现接受Trap信息
0x00 SNMP TRAP简介 SNMP(Simple Network Management Protocol) trap是一种很有用,但是也容易让人难以理解的协议. 虽然名字叫做简单网络管理协议, ...
- windows tomcat web应用以及eclipse console乱码解决方法
在windows下,如果vm文件名为UTF-8格式,则显示乱码(velocity写出的不乱码): 改回GBK,则不再乱码.
- 20165310 Java实验五《网络编程与安全》
20165310 Java实验五<网络编程与安全> 任务一 题目:①编写MyBC.java实现中缀表达式转后缀表达式的功能:②编写MyDC.java实现从上面功能中获取的表达式中实现后缀表 ...