kotlin写jni native方法_本地方法栈(java中的jni调用) kotlin - CSDN
  • Android Kotlin jni 示例

    2017-06-01 14:01:36
    Android Kotlin jni 示例本文的目的是简要介绍在Kotlin中使用jni首先新建一个Android工程,勾选 include C++ support,然后一路点击Next,直至Finish打开MainActivity.kt, 我们发现在Android Studio 3.0 Preview下,...

    Android Kotlin jni 示例

    本文的目的是简要介绍在Kotlin中使用jni
    首先新建一个Android工程,勾选 include C++ support,然后一路点击Next,直至Finish

    打开MainActivity.kt, 我们发现在Android Studio 3.0 Preview下,编译报错,其实是Preview版本的jni模板不对,函数定义和静态代码块还是Java的版本。Kotlin对应的静态代码块关键字是init

        public native String stringFromJNI();
        static {
            System.loadLibrary("native-lib");
        }

    修改后编译通过:

        init {
            System.loadLibrary("native-lib")
        }
        external fun stringFromJNI(): String

    demo额外定义了一个initFromJNI函数:

    external fun intFromJNI(): Int

    对应c++函数:

    
    extern "C"
    JNIEXPORT jint
    
    JNICALL
    Java_com_zzk_androidkotlindemo_MainActivity_intFromJNI(
            JNIEnv *env,
            jobject) {
        __android_log_write(ANDROID_LOG_DEBUG, TAG,
                            "Java_com_zzk_androidKotlindemo_MainActivity_intFromJNI");
        return 1 << 8;
    }

    TextView中显示两个函数的调用结果:

    val tv = findViewById(R.id.sample_text) as TextView
            tv.text = stringFromJNI() + "\nintFromJNI->${intFromJNI()}"

    编译运行:
    这里写图片描述

    demo很简单,Android Studio后续的正式版本很快就会修复这个模板问题。

    展开全文
  • 上一篇文章我讲了用 @CName 这个神奇的注解,可以配置 Kotlin Native 函数在符号表中的名字,进而根据 Jni 静态绑定的规则来对应到 Java nat...
        

    上一篇文章 我讲了用 @CName 这个神奇的注解,可以配置 Kotlin Native 函数在符号表中的名字,进而根据 Jni 静态绑定的规则来对应到 Java native 方法,但实际开发当中我们更喜欢用动态注册的方式,因为一方面不受名字的约束,不影响代码重构,函数名也相对美观,另一方面调用起来也相对高效,节省了静态绑定的查找过程。

    如果大家习惯用 C 写动态绑定的代码,那么 Kotlin Native 写起来思路也是很简单的,只要依样画葫芦,就可以写出来,我们先给出代码:

    1. @CName("JNI_OnLoad")

    2. fun JNI_OnLoad(vm: CPointer<JavaVMVar>, preserved: COpaquePointer): jint {

    3.    return memScoped {

    4.        val envStorage = alloc<CPointerVar<JNIEnvVar>>()

    5.        val vmValue = vm.pointed.pointed!!

    6.        val result = vmValue.GetEnv!!(vm, envStorage.ptr.reinterpret(), JNI_VERSION_1_6)

    7.        __android_log_print(ANDROID_LOG_INFO.toInt(), "Kn", "JNI_OnLoad")

    8.        if(result == JNI_OK){

    9.            val env = envStorage.pointed!!.pointed!!

    10.            val jclass = env.FindClass!!(envStorage.value, "com/example/hellojni/HelloJni".cstr.ptr)


    11.            val jniMethod = allocArray<JNINativeMethod>(1)

    12.            jniMethod[0].fnPtr = staticCFunction(::sayHello2)

    13.            jniMethod[0].name = "sayHello2".cstr.ptr

    14.            jniMethod[0].signature = "()V".cstr.ptr

    15.            env.RegisterNatives!!(envStorage.value, jclass, jniMethod, 1)


    16.            __android_log_print(ANDROID_LOG_INFO.toInt(), "Kn", "register say hello2, %d, %d", sizeOf<CPointerVar<JNINativeMethod>>(), sizeOf<JNINativeMethod>())

    17.        }

    18.        JNI_VERSION_1_6

    19.    }

    20. }

    思路很简单,就是先通过 CName 注解搞定 JNI_OnLoad 函数,让 Java 虚拟机能够在加载 so 库的时候找到这个入口函数,那么我们接下来就是纯调用 Jni 的 C 接口了。

    再说下 memScope 这个东西,C 当中内存管理是人工不智能的,Kotlin Native 则有自己的内存管理机制,因此如果我们需要在 Kotlin Native 当中访问 C 接口,并且创建 C 变量,就需要通过 memScope 来提醒 Kotlin Native 这些变量需要来统一管理。

    获取 JNIEnv 的指针时我们首先构造了一个指针的左值类型:

    1. val envStorage = alloc<CPointerVar<JNIEnvVar>>()

    这么说有些奇怪,总之在 C 的指针类型向 Kotlin Native 映射时, CPointer 的左值类型会映射成 CPointerVar,我现在对 Kotlin Native 与 C 的交互还没有仔细研究,就暂时不展开说了,等后面有机会再系统介绍 Kotlin Native 的细节。

    接下来我们看这句:

    1. val vmValue = vm.pointed.pointed!!

    C 版本的定义 JavaVM 其实本身也是一个指针:

    1. typedef const struct JNIInvokeInterface* JavaVM;

    因此两个 pointed 的调用相当于获取到了 JNIInvokeInterface 这个结构体,于是后面我们就可以用它持有的函数指针进行获取 JNIEnv 的操作了:

    1. val result = vmValue.GetEnv!!(vm, envStorage.ptr.reinterpret(), JNI_VERSION_1_6)

    再稍微提一个事儿,那就是这些类型从 C 的角度映射过来,空类型安全自然是无法保证的,因此我们会见到各种 !! 的使用,这样实际上对于开发来讲非常不友好。因此理想的状况是,我们用 Kotlin Native 对 C 接口进行封装,将这些底层的工作按照 Kotlin 的风格进行转换,这样我们使用起来就会容易得多——官方的 AndroidNativeActivity 的例子当中提供了 JniBridge 及一系列的类其实就是做了这样一件事儿,只不过还不算太完整。

    接下来我们要实现动态绑定了:

    1. val jclass = env.FindClass!!(envStorage.value, "com/example/hellojni/HelloJni".cstr.ptr)

    2. val jniMethod = allocArray<JNINativeMethod>(1)

    3. jniMethod[0].fnPtr = staticCFunction(::sayHello2)

    4. jniMethod[0].name = "sayHello2".cstr.ptr

    5. jniMethod[0].signature = "()V".cstr.ptr

    6. env.RegisterNatives!!(envStorage.value, jclass, jniMethod, 1)

    这里面也向大家展示了如何将 Kotlin 函数转为 C 的函数指针,总体来讲思路还是很简单的,毕竟我们只是照猫画虎。

    问题也是很显然的,如果你也尝试这样做了,一定被这些映射过来的接口函数的签名给搞晕过:

    1. public final var RegisterNatives: kotlinx.cinterop.CPointer<kotlinx.cinterop.CFunction<(kotlinx.cinterop.CPointer<platform.android.JNIEnvVar /* = kotlinx.cinterop.CPointerVarOf<platform.android.JNIEnv /* = kotlinx.cinterop.CPointer<platform.android.JNINativeInterface> */> */>?, platform.android.jclass? /* = kotlinx.cinterop.CPointer<out kotlinx.cinterop.CPointed>? */, kotlinx.cinterop.CPointer<platform.android.JNINativeMethod>?, platform.android.jint /* = kotlin.Int */) -> platform.android.jint /* = kotlin.Int */>>? /* compiled code */

    这其实就是 RegisterNatives 这个函数指针的签名,它接受 JNIEnv 的值,jclass,以及一个 JNINativeMethod 结构体的数组和这个数组的长度作为参数,但我们点进去看源码或者看函数前面却需要看这么一大堆东西,直接晕菜。

    这其实也是目前 Kotlin Native 比较麻烦的问题之一:开发体验。尽管 1.0-Beta 出来之后,相比过去要好了许多,但开发体验似乎仍然有待提高,这其实也会直接影响开发者的涌入。

    简单来说,这篇文章没什么太大的技术含量,只是对上一篇文章的一个补充。

    本文涉及源码参见:hello-kni / https://github.com/enbandari/hello-kni


    转载请注明出处:微信公众号 Kotlin

    640?wx_fmt=jpeg

    展开全文
  • 我在之前过一篇文章,讲如何用 Kotlin Native 编写 Native 代码通过 JNI 让 Java 调用。当时因为完全没有注意到 CName 这个神奇的东西的...
        

    我在之前写过一篇文章,讲如何用 Kotlin Native 编写 Native 代码通过 JNI 让 Java 调用。当时因为完全没有注意到 CName 这个神奇的东西的存在,所以那篇文章当中还是用 C wrapper 来做的调用。

    后来,我发现根本不需要这么麻烦啊。

    我们知道 JNI 如果不通过动态注册的话,Java native 方法与 C 函数的映射关系其实就是一个固定的命名规则:

    1. Java_包名_类名_方法名

    换句话说,如果我们在 Java 中加载的 so 库的符号表里面有这么一个函数,它的名字按照标准的 C 函数命名修饰方法修饰,并且修饰之前符合上面的规则,那么 Java 的 native 方法就可以与之对应上。

    那么假如我们有下面的 Java 类:

    1. public class HelloJni extends AppCompatActivity {


    2.    @Override

    3.    protected void onCreate(Bundle savedInstanceState) {

    4.        super.onCreate(savedInstanceState);

    5.        ...

    6.    }


    7.    public native String  stringFromJNI();


    8.    ...

    9. }

    那么我们只要保证 so 库当中存在一个函数名为 Java_com_example_hellojni_HelloJni_stringFromJNI 并且返回 jstring 函数就行,至于这个 so 库是由 C 还是 C++ 还是 golang,其实无所谓——自然,Kotlin Native也不在话下。

    我们可以用 CLion 创建一个 Kotlin Native 的工程,在 gradle 当中配置为 Android 的动态链接库:

    1. ...

    2. kotlin {

    3.    targets {

    4.        fromPreset(presets.androidNativeArm32, 'HelloWorld') // ① 配置为 Android 的工程


    5.        configure([HelloWorld]) {

    6.            compilations.main.outputKinds 'DYNAMIC' // ② 配置为动态链接库

    7.        }

    8.    }

    9.    ...

    10. }

    11. ...

    然后随便创建一个文件,写一个全局函数,并用 CName 进行标注如下:

    1. import kotlinx.cinterop.*

    2. import platform.android.*


    3. @CName("Java_com_example_hellojni_HelloJni_stringFromJNI")

    4. fun stringFromJNI(env: CPointer<JNIEnvVar>, thiz: jobject): jstring {

    5.    memScoped {

    6.        return env.pointed.pointed!!.NewStringUTF!!.invoke(env, "This is from Kotlin Native!!".cstr.ptr)!!

    7.    }

    8. }

    我们注意到,实际上 Kotlin Native 已经帮我们把 jni.h 这个头文件的互调用配置搞定了,因此我们可以直接导入 jstring 这样的类型。

    然后编译得到一个 so 库 libknlib.so(名字取决于我们的 gradle 工程名),我们可以把它放到我们的 Android 工程当中,在运行时加载它:

    1. static {

    2.    System.loadLibrary("knlib");

    3. }

    这样运行时就可以调用 stringFromJNI 这个方法啦。

    1. TextView tv = (TextView)findViewById(R.id.hello_textview);

    2. tv.setText(stringFromJNI());

    接下来我再给大家看几个例子:

    首先,在 Kotlin Native 当中使用 Android 的日志 Api 打印日志:

    1. @CName("Java_com_example_hellojni_HelloJni_sayHello")

    2. fun sayHello(){

    3.    __android_log_print(ANDROID_LOG_INFO.toInt(), "Kn", "Hello %s", "Native")

    4. }

    其次,在 Kotlin Native 当中调用 Java 的方法:

    1. @CName("Java_com_example_hellojni_HelloJni_callLoop")

    2. fun callLoop(env: CPointer<JNIEnvVar>, thiz: jobject): jstring {

    3.    memScoped {

    4.        val jniEnvVal = env.pointed.pointed!!

    5.        val jclass = jniEnvVal.GetObjectClass!!.invoke(env, thiz)

    6.        val methodId = jniEnvVal.GetMethodID!!.invoke(env, jclass, "callFromNative".cstr.ptr, "()Ljava/lang/String;".cstr.ptr)

    7.        return jniEnvVal.CallObjectMethodA!!.invoke(env, thiz, methodId, null) as jstring

    8.    }

    9. }

    其中 callFromNative 的定义如下:

    1. public String callFromNative(){

    2.    return "This is from Java!!";

    3. }

    由于 Kotlin Native 本身就是兼容 C 的,因此 C 能干的自然 Kotlin Native 也可以,这样一来我们其实可以使用 Kotlin 将 Android App 上到虚拟机下到 Native 的代码全部使用 Kotlin 来编写,真是不要太强大。

    本文涉及源码参见:hello-kni ,https://github.com/enbandari/hello-kni(阅读原文可以点击该链接~)


    转载请注明出处:微信公众号 Kotlin

    640?wx_fmt=jpeg

    展开全文
  • 首先我们声明kotlin的几个方法和属性,如下 package com.example.firstjni import android.util.Log object NDKUtil{ val name = "djfklsd" external fun updateArray(array: IntArray) external fun ...

    首先我们声明kotlin的几个方法和属性,如下

    package com.example.firstjni
    
    import android.util.Log
    
    object NDKUtil{
        val name = "djfklsd"
        external fun updateArray(array: IntArray)
        external fun updateArrayByPointer(array: IntArray)
        external fun callStaticMethod(i: Int)
        external fun callStaticMethod(i: Long)
        external fun callInstanceMethod(i: Int)
        external fun callInstanceMethod(s: String,i: Long)
    
        @JvmStatic
        fun callStaticJavaMethod(s: String?,i: Int){
            val len = s!!.length
            Log.i("native","c调用了java静态方法  $s  $i")
    
        }
    
        fun callJavaMethod(s: String,i: Int){
            Log.i("native","c调用了java方法  $s  $i")
        }
    }
    

    调用java或kotlin方法和属性基本步骤分4步
    1.找到类,实例方法和属性要创建对象
    2.找到方法或属性id
    3.调用方法
    4.释放资源

    1.C调用kotlin静态方法
    首先说一点,kotlin的object类和compaion object中,属性声明都是静态的,方法并不是静态,必须通过@JvmStatic注解,
    方法才会声明为静态
    在这里插入图片描述
    可以看到kotlin反编译出的代码,只有@JvmStatic声明的方法才真正是静态方法,然后就可以在C中调用静态的方法

    //1.首先要在C中获取jclass对象,也就是找到方法所在的类,通过完整  包名+类名
    jclass java_first = env->FindClass("com/example/firstjni/NDKUtil");
    
    /**2.找到该方法的方法ID
    *参数一:jclass
    *参数二:方法名
    *参数三:方法签名
    *方法签名,网上有详细说明,如果不想查,可以使用javap -s指令查询
    */
     jmethodID static_method_id = env->GetStaticMethodID(java_first, "callStaticJavaMethod",
                                                                "(Ljava/lang/String;I)V");
    
    /**3.调用静态方法
    *参数一:jclass
    *参数二:方法ID
    *参数三:参数三是可变长参数,也就是该方法声明时候对应的参数列表,相当于调用方法时的传参
    *这个方法声明的是(s: String, i: Int)
    */
    env->CallStaticVoidMethod(java_first, static_method_id, NULL, 2);
    
    //4.删除掉本地引用
    env->DeleteLocalRef(java_first);
    

    javap命令查询
    在这里插入图片描述
    对于kotlin来说,先通过AS下面的Termial进入build\tmp\kotlin-classes\debug目录 调用javap -s 包名+类名,就会为你显示该类所有的方法签名,就是
    descriptor:后面的部分 ,例如:()Ljava/lang/String;

    2.调用实例方法
    实例方法调用步骤类似,不过实例方法需要对象调用,而不能是直接通过类调用,需要先通过构造器创建对象

    //1.找到类
    jclass java_first = env->FindClass("com/example/firstjni/NDKUtil");```
    
    //2.找到构造器方法,这个相当于查找无参构造器
    jmethodID construct = env->GetMethodID(java_first, "<init>", "()V");
    
    //3.创建对象
    jobject NDKObj = env->NewObject(java_first, construct);
    
    //4.再回到和静态调用差不多的步骤,找到目标方法
    jmethodID jmethodID1 = env->GetMethodID(java_first, "callJavaMethod", "(Ljava/lang/String;I)V");
    
    //5.通过对象调用方法,这个方法参数是(s: String, i: Int)
    jstring message = env->NewStringUTF("调用成功了");
    env->CallVoidMethod(NDKObj, jmethodID1, message, 1);
    
    //6.释放资源
    env->DeleteLocalRef(message);
    env->DeleteLocalRef(NDKObj);
    

    3.调用属性

    //调用静态属性
    
    //1.找类
    jclass java_first = env->FindClass("com/example/firstjni/NDKUtil");
    
    //2.找属性ID
    jfieldID name_id = env->GetStaticFieldID(java_first, "name", "Ljava/lang/String;");
    
    //3.获取属性,string类型是通过GetStaticObjectField,其他的属性都有对应的方法,比如对于Int 就是GetStaticIntField
    jstring name_obj = (jstring) env->GetStaticObjectField(java_first, name_id);
    
    //至于实例属性,和调用实例方法步骤差不多,这里不多赘述
    

    NOTE:查找方法和属性ID的时候,方法签名有多个参数,要用;隔开,如果只有一个参数,结尾也要用;

    展开全文
  • Kotlin调用Jni的写法

    2020-05-10 22:33:34
    选择一个带 Native C++ 支持的工程,如下图所示 二、配置 ndk 目录 在文件 local.properties 中加入 ndk 的配置 sdk.dir=D\:\\soft\\android-sdk-windows ndk.dir=D\:\\soft\\android-sdk-windows\\ndk\\21.1....
  • 1.和java的不同 本地方法声明不同 //java声明方式 public static native void testJNI...在AS中java声明的native方法会直接提示未声明头文件,直接Alt+Enter就能生成,也可以点击下方的Terminal通过命令 生成 //...
  • Kotlin Native 详细体验,你想要的都在这儿
  • 名字有点绕口,大概意思是使用JNI反射一个Kotlin类的成员,这个成员是自定义类,并且我要调用这个类的函数。 在JAVA中调用一个static函数比较直接的,这个在百度上有比较多的例子。 如下 调用了JniHelper这个类的...
  • Android JNI中值/方法的传递与调用一级目录二级目录三级目录 按照习惯,我还是会用Kotlin做示范。 一级目录 二级目录 三级目录
  • 原文链接:在 mac os 上运行 kotlin native 的第一个程序:hello world 背景   不久前有群友在讨论kotlin,就翻看了聊天...觉得kotlin native确实很有潜力,能够零开销调用c代码,这比JNI要好很多。也许能把kot
  • 引子Kotlin Native 可以编译成 *.so 也就是我们常说的动态库,自然也就可以被 Android 的 Jni 调用,显然 Kotlin Native 官方的 ...
  • KotlinConf 大会宣布了 Kotlin 1.2 RC 版,并宣布 Kotlin/Native 已支持用于开发 iOS 应用和 Web 应用开发。 首届官方举办的 Kotlin 专题会议 KotlinConf 于昨天正式开幕,来自世界各地约 1200 名与会者齐聚...
  • android使用jni技术调用native层代码 标签(空格分隔): jni 前提 重要的业务使用c代码实现,并向第三方提供sdk。解决方案:通过jni技术实现,并打包aar提供给第三方。 设计到的知识点 1.JNI的静态和动态注册 动态...
  • AndroidStudio创建JNI方法

    2020-03-25 10:19:24
    新建Native C++项目 在MainActivity.java 中添加test01方法 1 老方式创建jni方法 选择java目录,右键-> Open in Terminal 在命令行输入以下语句: javah com.hongx.jni.MainActivity 查看生成如下文件: 2 ...
  • 生成与 native 方法对应的头文件 在 Android NDK 开发中,C/C++ 中对应于 Java 方法的函数名应该叫什么是很有讲究的,大致是形式是 Java_包名_类名_方法名 所以 C/C++ 中的函数名不能随便取,必须按照规则来。...
  • 最近一段时间在搞Android NDK 开发,就不可避免的涉及到javah 命令,ndk命令等,但是每次都要切换都要进入路径,敲命令等重复工作,就在网上找了下自动生成的配置,果不其然~但是网上很多资源,照着配置有错误。...
  • 首先在java类中创建native方法 public class JniText { public static void main(String[] args) { System.out.println("asdasdasldas;d"); } /** * JNI(Java Native Interface) * Java调用C/C...
  • 1、Jni基础知识 Jni使用场景 需要调用Java语言不支持的依赖时, 整合非Java语言开发的系统,如C、...native方法Jni映射实例 public static native String action(short s , int i, long l, float f, double d,...
1 2 3 4 5 ... 20
收藏数 817
精华内容 326
关键字:

kotlin写jni native方法