zoukankan      html  css  js  c++  java
  • Android JNI编程(八)——体验AS2.2.2编写Jni程序、Java调C、C调Java函数、将C代码中的Log打印至Logcat

    目录(?)[+]

    不得不说在AS2.2以上的版本进行开发就一个字——爽,在2.0上使用jni出现各种问题现在果断使用新版本开发了。

    使用AS2.0创建Jni项目: 使用AndroidStudio编写第一个JNI程序

    一:编写Jni程序

    1. 我们创建一个项目,将Include C++ Support勾选一路Next直到Finish 这样就可以全自动为我们创建好一个带有Jni的项目了

    这里写图片描述

    编译过程中需要你为这个工程配置Ndk的路径,只需在Project Sturcture配置即可(不懂看文章上面的链接文章)。如果你第一次使用,那么他会提示你需要下CMake你也只需下载即可

    2. 在AS2.2以上版本中,使用的是CMake来编译我们的C代码

    这里写图片描述

    CMake官网:https://cmake.org/
    Google官方文档:Jni开发文档

    CMakeLists.txt

    //以#号开头都是注释,这里把它们都删了也就更清楚了
    cmake_minimum_required(VERSION 3.4.1)
                 #生成的库名
    add_library( native-lib
                 #编译的文件路径
                 src/main/cpp/native-lib.c )
    find_library( log-lib
                  log )
    target_link_libraries( native-lib
                           ${log-lib} )

    3. 默认创建的是cpp文件,这里将它改成.c文件同时修改CMakeLists.txt中的src/main/cpp/native-lib.c文件后缀名,改完之后记得sync一下

    4. 在build.gradle中配置生成的so库类型,记得sync一下

    android {
        //。。。
        defaultConfig {
            //。。。
            ndk {
                moduleName "native-lib"//生成so库的名字,与CMakeLists中的一致
                abiFilters "armeabi", "armeabi-v7a", "x86"//cpu类型
            }
        }
    }

    二:Java调用C代码

    1. 创建一个类,存放native函数

    public class JniInterface {
    
        //加载so文件
        static {
            System.loadLibrary("native-lib");
        }
    
        /*返回一个字符串*/
        public native String stringFromJNI();
    }

    2. 使用Alt+Enter来自动生成对应的Jni函数,确定之后会在main/jni目录中创建一个C文件,我们只需要将里面的方法复制到我们的native-lib.c文件中

    这里写图片描述

    1. native-lib.c文件

    #include <jni.h>
    
    /**
     * jstring: 返回值
     * Java_全类名_方法名
     * JNIEnv* env: 里面有很多方法
     * jobject jobj: 谁调用了这个方法就是谁的实例
     */
    jstring Java_com_zsy_ajni_JniInterface_stringFromJNI(JNIEnv *env, jobject jobj) {
        char *str = "Hello from C";
        return (*env)->NewStringUTF(env, str);
    }

    3. 在我们的MainActivity中调用stringFromJNI()便可以调用成功了。

    这里写图片描述
    然后在项目的build/intermediates/cmake/debug/obj/这里就可以看到编译生成的so库了
    这里写图片描述

    三:C调用Java代码

    1. 在JniInterface类中继续增加我们的native方法,让java调用callbackAdd();函数,然后Jni调用add(int a,int b);函数

    public class JniInterface {
    
        //加载so文件
        static {
            System.loadLibrary("native-lib");
        }
    
        /**
         * 返回一个字符串
         */
        public native String stringFromJNI();
    
        /**
         * 当执行这个函数的时候,让C代码调用add(int a, int b)
         */
        public native void callbackAdd();
    
        /**
         * 这个函数让C调用
         */
        public int add(int a, int b) {
            Log.d("TAG", "我是add函数被C调用了a = " + a + ";b = " + b);
            return a + b;
        }
    }

    2. MainActivity中调用callbackAdd()函数

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            JniInterface jni = new JniInterface();
    
            TextView tv = (TextView) findViewById(R.id.sample_text);
            tv.setText(jni.stringFromJNI());
            //调用这个函数,让C调用我们的add函数
            jni.callbackAdd();
        }
    }

    3. 接下来就是重点了,在native-lib.c文件继续中生成callbackAdd()对应的Jni函数代码中来调用我们的add(int a, int b)函数

    /**
     * C代码调用Java JniInterface类中的add(int a, int b)函数
     */
    void Java_com_zsy_ajni_JniInterface_callbackAdd(JNIEnv *env, jobject jobj) {
        //1、得到字节码 FindClass(env,类的全路径);
        jclass class = (*env)->FindClass(env, "com/zsy/ajni/JniInterface");
        //2、得到方法 GetMethodID(env,jclass,方法名,方法签名);
        //方法签名是要调用的方法名对应的签名
        jmethodID methodID = (*env)->GetMethodID(env, class, "add", "(II)I");
        //3、实例化该类
        jobject obj = (*env)->AllocObject(env, class);
        //4.调用Java类中的方法
        //成功调用add方法;第二三个参数是我们调用add方法传入的参数。value就是add方法的返回值
        jint value = (*env)->CallIntMethod(env, obj, methodID, 45, 23);
    }

    4. 上面代码中,我们需要去获取一个方法的签名那我们来获取一下:

    将工程build让类生成对应的.class文件。然后使用Terminal进入到appuildintermediatesclassesdebug>目录下,执行javap -s 类的全路径 descriptor就是方法签名。
    这里写图片描述
    类的全路径可以通过在类名上右键,点击Copy Reference即可

    5. 运行我们的程序,就可以在控制看到Log了也就证明方法调用成功

    这里写图片描述

    四:在C代码中打印Log至Logcat

    上面我们是在Java函数中打印的Log,现在我们来让C中也可以打印Log

    1.配置C代码打印Log

    1. 在build.gradle中添加日志打印so库
    ndk {
         //....
         //就是依赖ndk工具包中的一个liblog.so文件
         ldLibs "log"
     }

    1. 在.c文件中添加如下代码,看注释就明白了
    #include <android/log.h>
    //日志标签,随意定义
    #define LOG_TAG "TAG"
    //Debug等级
    #define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
    //Info等级
    #define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    //Error等级
    #define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    1. 配置好了,我们就可以来用了
    #include <jni.h>
    #include <android/log.h>
    //日志标签,随意定义
    #define LOG_TAG "TAG"
    //Debug等级
    #define LOGD(...)__android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
    //Info等级
    #define LOGI(...)__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
    //Error等级
    #define LOGE(...)__android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
    
    
    /**
     * jstring: 返回值
     * Java_全类名_方法名
     * JNIEnv* env: 里面有很多方法
     * jobject jobj: 谁调用了这个方法就是谁的实例
     */
    jstring Java_com_zsy_ajni_JniInterface_stringFromJNI(JNIEnv *env, jobject jobj) {
        char *str = "Hello from C";
        return (*env)->NewStringUTF(env, str);
    }
    
    /**
     * C代码调用Java JniInterface类中的add(int a, int b)函数
     */
    void Java_com_zsy_ajni_JniInterface_callbackAdd(JNIEnv *env, jobject jobj) {
    
        //此处代码省略,与上面代码一致
        jint value = (*env)->CallIntMethod(env, obj, methodID, 45, 23);
        //调用Log输出,使用与printf函数一致
        LOGD("value=%d", value);
    }
    

    2. 成功运行后机可以在Logcat查看到日志信息了

    这里写图片描述

    在2.2.2版本上可以关联到源码跟踪源码非常方便,写Jni也是个不错的体验。Demo地址

  • 相关阅读:
    [ jquery 选择器 :nth-last-child ] 选取属于其父元素的不限类型的第 n 个子元素的所有元素,从最后一个子元素开始计数
    [ jquery 选择器 :nth-child ] 选取匹配其父元素下的第N个子或奇偶元素
    [ jquery 选择器 :first :first-child ] 实例分析: :first :first-child 之间在元素选取方面的区别
    [ jquery 位置选择器 :first-child :last-child ] 强化说明:选取属于其父元素和所有兄弟元素中子元素集合中处于第一个(最后一个)位置上符合条件的元素
    [ jquery 位置选择器 :first :last ] 强化说明:获取匹配的第一个(最后第一个)元素
    [ jquery 过滤器 is(expr | jqObj | ele | function) ] 此方法用于在选择器的基础之上根据选择器 DOM元素或 jQuery 对象来检测匹配元素集合
    PAT 甲级 1009 Product of Polynomials
    PAT 甲级 1003 Emergency DFS
    PAT 甲级 1002 A+B for Polynomials
    PAT 甲级 1001 A+B Format
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6169602.html
Copyright © 2011-2022 走看看