zoukankan      html  css  js  c++  java
  • Android JNI学习(五)——Demo演示

    本系列文章如下:

    我们这里做一个简单的计算器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);
    
    
    }
    

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

    <?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,如下图:

    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 <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;
    }
    

    至此,整个代码已经全部完成。我们可以运行下试试。其实挺简单的。

    github地址



    作者:隔壁老李头
    链接:https://www.jianshu.com/p/0f34c097028a
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
  • 相关阅读:
    1032. Sharing (25)
    1031. Hello World for U (20)
    1030. Travel Plan (30)
    1029. Median (25)
    1028. List Sorting (25)
    1026. Table Tennis (30)
    win10 tortoiseSVN文件夹及文件图标不显示解决方法
    qrcode.react和jquery.qrcode生成二维码
    js来获取所有屏幕适配的总结
    handsontable整理
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/10601390.html
Copyright © 2011-2022 走看看