2019-08-08 09:19:12 One_Month 阅读数 61
  • 征服Kotlin视频教程

    该课程包括Kotlin开发环境搭建、Kotlin基础知识、类和接口、枚举类、扩展、泛型、函数、lambdas表达式、对象、标准API等。

    8144 人正在学习 去看看 李宁

1.和java的不同

  • 本地方法声明不同
//java声明方式
public static native void testJNI();

//kotlin声明方式
external fun testJNI()
  • 生成c头文件方式不同
    JAVA
    在AS中java声明的native方法会直接提示未声明头文件,直接Alt+Enter就能生成,也可以点击下方的Terminal通过命令
    生成

在这里插入图片描述

//格式  javah -d 存放头文件的目录  完整包名和类名 一般到app/src/main路径下执行

//示例
javah -d cpp com.example.firstjni.NDKUtil

Kotlin
kotlin代码AS不会提醒创建头文件,而且一开始不会生出class文件,需要先执行编译再使用javah,生成的class文件路径为
在这里插入图片描述

//格式  javah -d 存放头文件的目录 -cp 加载类的目录  完整包名和类名

//示例
javah -d src/main/cpp -cp /app/build/tmp/kotlin-classes/debug/ com.example.firstjni.NDKUtil

这样就可以看到cpp目录下生成了头文件
在这里插入图片描述

2017-06-01 11:22:00 zhuangzaiku 阅读数 4324
  • 征服Kotlin视频教程

    该课程包括Kotlin开发环境搭建、Kotlin基础知识、类和接口、枚举类、扩展、泛型、函数、lambdas表达式、对象、标准API等。

    8144 人正在学习 去看看 李宁

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后续的正式版本很快就会修复这个模板问题。

2018-12-17 08:00:00 qq_23626713 阅读数 624
  • 征服Kotlin视频教程

    该课程包括Kotlin开发环境搭建、Kotlin基础知识、类和接口、枚举类、扩展、泛型、函数、lambdas表达式、对象、标准API等。

    8144 人正在学习 去看看 李宁

我在之前写过一篇文章,讲如何用 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

2018-12-31 07:54:38 qq_23626713 阅读数 348
  • 征服Kotlin视频教程

    该课程包括Kotlin开发环境搭建、Kotlin基础知识、类和接口、枚举类、扩展、泛型、函数、lambdas表达式、对象、标准API等。

    8144 人正在学习 去看看 李宁

上一篇文章 我讲了用 @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

2017-11-08 09:10:16 kwame211 阅读数 1741
  • 征服Kotlin视频教程

    该课程包括Kotlin开发环境搭建、Kotlin基础知识、类和接口、枚举类、扩展、泛型、函数、lambdas表达式、对象、标准API等。

    8144 人正在学习 去看看 李宁

KotlinConf 大会宣布了 Kotlin 1.2 RC 版,并宣布 Kotlin/Native 已支持用于开发 iOS 应用和 Web 应用开发。

近日,首届官方举办的 Kotlin 专题会议 KotlinConf 正式开幕,来自世界各地约 1200 名与会者齐聚旧金山,在这场盛大的 KotlinConf 大会主题演讲中,Kotlin 的首席语言设计师 Andrey Breslav 公布了一系列与 Kotlin 紧密相关的重大发展成果。接下来,我们不妨关注一下。

Kotlin 1.2 RC

首先宣布了 Kotlin 1.2 RC 版(即 1.2 首个候选发行版)。该版本引入了一系列新功能,包括对多平台项目的实验性支持 —— 允许开发者在针对 JVM 和 JavaScript 的模块之间共享代码,以及多项对语言方面的改进 —— 包括支持在注释中使用数组字面量。更多关于 1.2 版本新功能的内容,可点击此处查看:https://blog.jetbrains.com/kotlin/2017/09/kotlin-1-2-beta-is-out

此外,值得关注的就是协程这个特性了。虽然协程仍然被标记为实验性状态,但官方特意说明了这里“实验性”代表的含义。官方表示协程已经完全准备好用于生产环境,他们也已使用协程进行开发,而且也没发现在使用当中出现任何重大问题。之所以仍保持实验性状态,是为了能够对 Kotlin 继续进行设计迭代。根据目前的计划,Kotlin 1.3 将会删除协程的实验性状态。

Kotlin/Native 对 iOS 开发的支持

另外一个重大消息就是 Kotlin/Native 已支持用于开发 iOS 应用,这也将是 Kotlin/Native 0.4 的特性之一。虽然对 iOS 开发的支持仍处于早期阶段,但确实已经实现了,这是在所有平台上使用 Kotlin 进行开发的重要一步。

官方还特意展示了利用 Kotlin/Native 开发的两款应用(Spinner app (GitHub)、KotlinConf app (GitHub)),它们都可以运行于 iOS 和 Android 平台。Android 和 iOS 平台共享了不少代码,其中包括大多数图形处理、声音播放和用户输入响应代码。

两款应用都已开源,你可以将它们作为模板,仅使用 Kotlin 构建自己的跨平台移动应用。点此此处可了解其中一款应用:https://www.oschina.net/p/kotlinconf-spinner

IDE 对 Kotlin/Native 的支持

Kotlin/Native 也将迎来 IDE 的支持。官方表示首先将在 C/C++ IDE CLion 中试用该功能,目前正在发布一个名为 Kotlin/Native plugin for CLion 的插件,该插件支持使用 CMake 作为构建系统。其中包含来自 IntelliJ IDEA 的 Kotlin 插件中全部代码编辑功能集,同时初步支持项目创建、测试与调试等操作。

使用 React 和 Kotlin 创建现代 Web 应用

对于使用 Kotlin 进行 Web 前端开发的开发者,Andrey Breslav 在大会上还公布了另外一个重大消息 —— 正式发布了针对 React.js 的官方 Kotlin 封装工具。此外,还有一个名为 create-react-kotlin-app 的工具集 —— 通过 Kotlin 使用 React.js 创建现代 Web 应用。

使用 create-react-kotlin-app,可让开发者无需费心于项目设置和配置构建这些工作,能专注于快速开始客户端应用开发,同时体会到静态类型语言的优势以及由 JavaScript 生态系统带来的强大配套资源。详情点此了解:https://github.com/JetBrains/create-react-kotlin-app

未来的发展方向

Kotlin 语言的未来发展方向主要是在 Kotlin 支持的平台之间实现更好、更广泛的代码重用。官方表示计划通过使用同一 API 以扩展全部平台上的可用库资源集合,其中涵盖 I/O、网络、序列化以及日期处理等。

编译器方面,1.3 版本仍集中关注内部层面的变更,而不是外部可见的语言特性。内部的改变能提高性能、改进类型推断、为所有目标平台生成更高效的代码,以及带来更好的 IDE 插件响应能力。

关于 Kotlin/Native

说了这么多,Kotlin/Native 到底是什么?Kotlin/Native 能将 Kotlin 编译成机器码,目前还处于技术预览版阶段,离商用估计还需要至少一年的时间。

Kotlin/Native 不是 Jni 的概念,它不仅仅是要与底层代码比如 C、C++ 交互,而且还要绕过 JVM 直接编译成机器码供系统运行。这说明了什么?Kotlin 准备扔掉 Java 这根拐杖了。

第一次看到 Native 这个名字或许会很自然地想到 Jni,Kotlin 跑在 JVM 上面,使用 Jni 与底层代码交互。不过详细深入研究后可以发现,Kotlin 开发团队的野心不可小觑,Java 诞生这么多年,也没有涉足过编译成除 Java 虚拟机字节码以外的字节码的事情,Kotlin 才这么年轻,不得不说十分具有革命性。

所以如果有人再问你什么是 Kotlin,你应该回答,Kotlin 是一门很厉害的静态语言(而不是之前常说的 Kotlin 是一门运行在 JVM 之上的静态语言)。反正你能想到的,Kotlin 的开发团队都想干。

时钟拨回到到五个月前,2017 年 5 月 17 日 Google I/O 大会第一天,谷歌宣布 Kotlin 成为 Android 开发的官方支持语言。有了 JetBrains 和 Google 的背书,自此,对于“Kotlin 即将取代 Java ”的声音从未停止过。曾经,CSDN 也曾发表过一篇题为“5 个月策反,Kotlin 将取代 Java 成 Android 开发第一语言?”的文章,文中提到,Realm 公司调查报告给出的预测表示,“到 2018 年底,在 Android 开发领域,Kotlin 将超越 Java,成为开发 Android 应用的第一语言。”

不过,再看回在 KotlinConf 大会中官方已公布的关于 Kotlin 的发展成果和未来方向,Kotlin 的目标可能并不是撼动常青藤 Java 在 Android 开发领域中的地位,他们的征途或许是星辰大海。

Kotlin Native

阅读数 14

没有更多推荐了,返回首页