博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android JNI学习(五)——Demo演示
阅读量:4474 次
发布时间:2019-06-08

本文共 6252 字,大约阅读时间需要 20 分钟。

本系列文章如下:

我们这里做一个简单的计算器demo,其中运算的逻辑由Native实现,而且我们采用动态注册的方式来实现

样式大概如下:

image.png

里面有两个输入框,下面有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); }

然后,我们先用来看下对应的布局文件如下:

这时候,我们来看下对应的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 
#include
#include

依照前面动态注册方法的步骤,我们要重写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    := jnidemo3LOCAL_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,如下图:

image.png

然后打开Android Studio里面的Terminal,然后拖拽JNITools.classTerminal。这样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" }

这时候我们目录结构如下:

image.png
jnitools.c的内容如下:
#include 
#include
#include
#include
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
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

转载于:https://www.cnblogs.com/Free-Thinker/p/10601390.html

你可能感兴趣的文章
mac 安装php redis扩展
查看>>
css3中Animation
查看>>
JS 判断是否是手机端并跳转操作
查看>>
最短路径问题(dijkstra-模板)
查看>>
c# 导出表格 api
查看>>
使用Android NDK以及JNI编写应用
查看>>
学习笔记之-php数组数据结构
查看>>
初学者--bootstrap(六)组件中的下拉菜单----在路上(10)
查看>>
QMetaObject::connectSlotsByName 总结
查看>>
app图标
查看>>
Android 微信支付步骤
查看>>
js操作table
查看>>
JQuery学习系列篇(一)
查看>>
Centos7 minimal 系列之rabbitmq安装(八)
查看>>
英语语法(2)----点破主谓宾系表三大句型
查看>>
html如何与cgi数据交换,HTML网页与CGI之间通信的 实例分析
查看>>
html如何调用flash插件,htmlflash播放器插件如何播放 网页播放器flash插件怎么解决...
查看>>
mysql数据在html上面显示不出来的,HTML表格不能正确显示MySQL数据
查看>>
数据包和html,数据包和数据报有何区别?
查看>>
jq 异步调用一个html,聊聊如何将jQuery的$.ajax()用于异步HTTP请求
查看>>