0.6 kotlin native_kotlin-native-utils-1.3.31.jar - CSDN
  • Kotlin Native实战开发

    2018-11-18 09:15:36
    Kotlin Native是一种将Kotlin源码编译成不需要任何VM支持的目标平台二进制数据的技术,编译后的二进制数据可以直接运行在目标平台上,它主要包含一个基于LLVM的后端编译器的和一个Kotlin本地运行时库。设计Kotlin ...

    注:本部分内容来源于《Kotlin入门与实战》,预计9月上市。

    16.1 Kotlin Native

    16.1.1 Kotlin Native简介

    Kotlin Native是一种将Kotlin源码编译成不需要任何VM支持的目标平台二进制数据的技术,编译后的二进制数据可以直接运行在目标平台上,它主要包含一个基于LLVM的后端编译器的和一个Kotlin本地运行时库。设计Kotlin Native的目的是为了支持在非JVM环境下进行编程,如在嵌入式平台和iOS环境下,如此一来,Kotlin就可以运行在非JVM平台环境下。

    LLVM是Low Level Virtual Machine的缩写,是一种比较底层的虚拟机技术,LLVM由C++编写而成,主要用来优化应用程序的编译时间(compile-time)、链接时间(link-time)、运行时间(run-time)以及空闲时间(idle-time)。LLVM可以有效的解决编译器重复编译代码的问题,并且LLVM制定了LLVM IR这种中间代码表示语言,LLVM IR充分考虑了各种应用场景,有效的提高了代码的编译效率。

    在讲解Kotlin Native具体的知识之前,先来看一下计算机高级语言常见两种流派:编译型语言和解释型语言。

    所谓编译型语言,是指使用专门的编译器、针对特定平台/操作系统将某种高级语言源代码一次性编译成该平台硬件能够执行的机器码,编译生成的可执行程序可以脱离开发环境,在特定的平台上独立运行。因为编译型语言是一次性编译成机器码的,所以可以脱离开发环境独立运行,而且通常运行效率较高。不过,正因为编译型语言只能被编译成特定平台上的机器码,所以生成的可执行程序通常无法移植到其他平台上运行。例如,现在比较流行的C、C++等高级编程语言都属于编译型语言。

    而所谓解释型语言,是指使用专门的解释器对源程序进行逐行解释,并生成特定平台的机器码并立即执行的编程语言。解释型语言通常不需要进行整体的编译和链接处理,解释型语言会把编译型语言中的编译和解释过程混合在一起执行。虽然解释型语言运行效率较低且不能脱离释器独立运行,但解释型语言可以很方便的实现源程序的移植和运行。

    ##16.1.2 Kotlin Native编译器
    目前,Kotlin Native主要提供了Mac、Linux和Windows三个主流平台的编译器,使用该编译器可以很轻松的编译出运行在树莓派、iOS、OS X、Windows以及Linux系统上的程序。Kotlin Native支持平台和版本如下表所示。

    支持的系统平台	支持的版本
    Windows	x86_64
    Linux	x86_64、arm32、MIPS、MIPS小端
    MacOS	x86_64
    iOS	arm64
    Android	arm32、arm64
    WebAssembly	wasm32
    

    表16-1 Kotlin Native支持平台及其版本

    编译Kotlin Native项目,首先需要到Github上下载Kotlin Native的编译器软件包,下载地址为:https://github.com/JetBrains/kotlin-native/releases,使用前下载对应的平台版本即可,下载后解压下载的Kotlin Native编译器软件包,其目录结构如图16-1所示。
    这里写图片描述
    图16-1 Kotlin Native编译器目录结构图
    当然,也可以通过克隆Kotlin Native编译器的源码进行编译,编译需要先到Github上下载编译器源码,下载地址为:https://github.com/JetBrains/kotlin-native。下载完成后,使用如下命令下载依赖关系,命令如下:

    ./gradlew dependencies:update
    

    然后,建立编译器和库的关联。

    ./gradlew bundle
    

    如果需要构建整个项目可能需要很长的时间。然后,使用以下的命令即可编译项目。

    ./gradlew dist distPlatformLibs
    

    到此,就可以得到Kotlin的Native编译器了,它通常位于项目的./dist/bin目录下,打开bin文件可以看到Native编译器的相关信息,它有7个可执行程序构成,如图15-2所示。通过对比发现,Native编译器的目录结构和Kotlin Native官方提供的编译器的内容是一样的。然后,就可以利用Native编译器编译应用程序了。例如:

    export PATH=./dist/bin:$PATH
    kotlinc hello.kt -o hello
    

    如果需要进行优化编译,可以使用-opt参数。

    kotlinc hello.kt -o hello -opt
    

    如果需要对应用程序进行测试,可以使用类似于下面的命令。

    ./gradlew backend.native:tests:run
    

    这里写图片描述

    图16-2 Kotlin的Native编译器目录结构
    在Kotlin Native官方提供的示例中,系统自带了针对不同平台的例子,并且这些例子都是可以直接编译运行的。由于Kotlin Native本身是一个gradle构建的项目,所以可以使用idea直接打开Kotlin Native目录下的samples文件,idea会自动识别该项目。
    ##16.1.3 编译器konan
    打开kotlin-native-macos-0.6文件,其目录结构如图15-3所示。其中,bin目录包含众多的与Kotlin Native相关的执行命令,klib目录则主要包含Kotlin的标准库的关联元数据文件以及针对各个目标平台的bc文件,konan主要包含编译器依赖的一些jar包和一些已经编译好的项目实例,可以使用IntelliJ IDEA直接导入。
    这里写图片描述
    图16-3 编译器konan目录结构
    打开Kotlin Native编译器的bin目录可以发现,bin文件主要由cinterop、jsinterop、klib、konanc、kotlinc、kotlinc-native、run_konan等7个可执行文件组成。其中,run_konan是编译器真正的入口,源码如下。

    TOOL_NAME="$1"
    shift
    
    if [ -z "$JAVACMD" -a -n "$JAVA_HOME" -a -x "$JAVA_HOME/bin/java" ]; then
        JAVACMD="$JAVA_HOME/bin/java"
    else
        JAVACMD=java
    fi
    [ -n "$JAVACMD" ] || JAVACMD=java
    //省略部分代码
    
    LIBCLANG_DISABLE_CRASH_RECOVERY=1 \
    $TIMECMD "$JAVACMD" "${java_opts[@]}" "${java_args[@]}" -cp "$KONAN_CLASSPATH" "$TOOL_CLASS" "$TOOL_NAME" "${konan_args[@]}"
    

    可以发现,Kotlin Native编译器konan的运行环境还是需要JVM环境支持的,但是它生成的机器码的可执行程序是不需要JVM环境支持的,可以直接运行在对应的平台系统上。

    16.2 Kotlin Native实例

    ##16.2.1 构建Kotlin Native项目
    首先,在IDEA中依次选择【File】→【New】→【Project】创建一个普通的 Gradle工程。
    这里写图片描述
    图16-4 创建Gradle工程
    ##16.2.2 添加konan插件配置
    创建完成之后,需要修改build.gradle文件配置。打开build.gradle文件并添加如下配置。

    buildscript {
        repositories {
            mavenCentral()
            maven {
                url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
            }
        }
        dependencies {
            classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.5"
        }
    }
    apply plugin: 'konan'
    

    其中,kotlin-native-gradle-plugin:0.5是Gradle构建Kotlin Native工程所使用的DSL插件,这个插件发布在https://dl.bintray.com/jetbrains/kotlin-native-dependencies仓库里。除此之外,还需要应用konan插件,konan插件是用来将Kotlin代码编译为native代码的插件,可以通过如下地址来获取更多konan相关的信息。

    https://github.com/JetBrains/kotlin-native/blob/master/GRADLE_PLUGIN.md
    

    此时,还需要创建一个kotliner.def文件,该文件主要用来配置C源码到Kotlin的映射关系。

    headers=cn_kotliner.h
    

    ##16.2.3 编写源代码
    接下来,在工程的src目录下新建一个c目录,此目录专门用来存放C代码。首先,创建两个c文件:cn_kotliner.h和cn_kotliner.c。其中,C头文件声明的代码如下。

    #ifndef CN_KOTLINER_H
    #define CN_KOTLINER_H
    
    void printHello();
    int factorial(int n);
    #endif
    

    在上面的代码中,主要声明了两个函数,打印HelloWorld的 printHello函数和用来计算阶乘的factorial函数。cn_kotlinor.c的源代码如下:

    #include "cn_kotliner.h"
    #include <stdio.h>
    
    void printHello(){
        printf("[C]HelloWorld\n");
    }
    
    int factorial(int n){
        printf("[C]calc factorial: %d\n", n);
        if(n == 0) return 1;
        return n * factorial(n - 1);
    }
    

    接下来,还需要创建一个kotlin文件,该文件主要是调用C层的代码,实现跨平台调用。该文件的源码如下:

    import kotliner.*
    
    fun main(args: Array<String>) {
        printHello()
        (1..5).map(::factorial).forEach(::println)
    }
    

    其中,导入的kotlinor.*包是C语言代码经过clang编译后对应的C接口的包路径,可以在项目的build.gradle配置文件中的konanInterop中配置这个路径。
    这里写图片描述
    图16-5 Kotlin Native项目目录结构图

    ##16.2.4 添加konanInterop与konanArtifacts配置
    接下来,还需要添加konanInterop和konanArtifacts相关的配置信息。其中,konanInterop主要用来配置Kotlin调用C的接口。相关的源码如下:

    konanInterop {
        ckotlinor {
            defFile 'kotlinor.def'     // interop配置文件
            includeDirs "src/c"      // C头文件目录,可以传入多个
        }
    }
    

    在上面的配置文件中,ckotlinor是插件中的KonanInteropConfig对象,在konanArtifacts配置中会引用这个ckotlinor。而kotlinor.def是Kotlin Native与C 语言互操作的配置文件,可以在kotlinor.def里面配置C源码到Kotlin的映射关系,该配置文件的内容如下。

    headers=cn_kotlinor.h
    compilerOpts=-Isrc/c
    

    除此上面使用的选项之外,konanInterop还提供了如下常用的选项。

    konanInterop {
           pkgName {
               defFile <def-file>  
               pkg <package with stubs>
               target <target: linux/macbook/iphone/iphone_sim>
               compilerOpts <Options for native stubs compilation>
               linkerOpts <Options for native stubs >
               headers <headers to process> 
               includeDirs <directories where headers are located> 
               linkFiles <files which will be linked with native stubs>
               dumpParameters <Option to print parameters of task before execution>
           }   
     }
    

    konanInterop配置参数选项对应的具体含义如下表所示。

    配置选项	选项说明
    defFile	互操作映射关系配置文件
    pkg	C头文件编译后映射为Kotlin的包名
    target	编译目标平台:linux/macbook/iphone等
    compilerOpts	编译选项
    linkerOpts	链接选项
    headers	需要处理的头文件
    includeDirs	包括头文件的目录
    linkFiles	与native stubs链接的文件
    dumpParameters	打印Gradle任务参数的选项配置
    

    表16-2 konanInterop配置选项说明表
    接下来,需要为项目添加konanArtifacts相关的配置,该配置主要用来处理编译任务的执行。

    konanArtifacts { 
        KotlinorClient {   
            inputFiles fileTree("src/kotlin")  //kotlin代码配置,项目入口main()
            useInterop 'ckotlinor'   //前面的interop配置
            nativeLibrary fileTree('src/c/cn_kotlinor.bc')   //本地库文件配置
            target 'macbook'   // 编译的目标平台
        }
    } 
    

    konan编译任务配置的处理类是KonanCompileTask.kt,可以在Kotlin Native的kotlin-native-gradle-plugin插件中找到该类。可以通过以下地址来获取更详细的konan插件配置信息。

    https://github.com/JetBrains/kotlin-native/blob/master/GRADLE_PLUGIN.md
    

    16.2.5 编译与执行

    接下来,在项目的src/c目录下面,用命令行编译上面的代码,命令如下。

    clang -std=c99 -c cn_kotliner.c -o cn_kotliner.bc -emit-llvm
    

    其中,clang是一个由C++编写的基于LLVM的C/C++/Objective-C/Objective-C++编译器。如果提示找不到clang命令,可以在编译器的dependencies目录中找到相关的内容。当然,还可以使用shell脚本(名称为kclang.sh)来简化clang编译的命令行输入参数。

    #!/usr/bin/env bash clang -std=c99 -c $1 -o $2 -emit-llvm
    

    接着把kclang.sh放到C代码目录下,然后使用脚本来编译C代码。例如:

    kclang.sh cn_kotlinor.c cn_kotlinor.bc
    

    通过上面的命令编译之后,将得到一个名为cn_kotlinor.bc的库文件。最后,在执行Gradle构建之前,还需要指定konan编译器主目录。具体的,在工程根目录下面新建一个gradle.properties属性配置文件,该文件格式如下。

    konan.home=<编译器路径>
    

    例如:

    konan.home=/Users/xiangzhihong /kotlin native/kotlin-native-macos-0.5
    

    当然,也可以不添加gradle.properties配置文件,那样的话,只需要在编译的时候使用本地的编译器即可。
    然后,在IDEA的Gradle工具栏依次点击【Tasks】→【build】执行构建操作,如图15-5所示。等待项目构建完成,会在项目的build/konan/bin/目录下面生成一个KotlinorClient.kexe的可执行程序,它可以直接运行在Mac OS系统上而不再需要依赖JVM环境。
    这里写图片描述
    图16-5 使用Gradle工具栏编译项目
    然后,在命令行中执行KotlinorApp.kexe命令,即可看到输出结果,对应的命令如下。
    build/konan/bin/KotlinorApp.kexe
    可以看到,作为一款致力于跨平台开发的编程语言,Kotlin Native非常注重语言平台的互操作性,可以说,使用Kotlin Native进行跨平台开发优势是非常明显的。

    16.2.6 命令行方式编译Kotlin Native

    对于Kotlin Native项目来说,除了允许Gradle方式构建编译外,还可以使用命令行的方式来编译项目。具体来说,编写完Kotlin源码之后,采用shell脚本的方式来构建,或者使用Makefile或build.sh的方式来构建,官方推荐使用shell脚本构建方式,本篇采用与之类似的Makefile脚本方式。例如:

    build : src/kotlin/main.kt kotliner.kt.bc
        konanc src/kotlin/main.kt -library build/kotliner/kotliner.kt.bc -nativelibrary build/kotliner/cn_kotliner.bc -o build/kotliner/kotliner.kexe
    
    kotliner.kt.bc : kotliner.bc kotliner.def
        cinterop -def ./kotliner.def -o build/kotliner/kotliner.kt.bc
    
    kotliner.bc : src/c/cn_kotliner.c src/c/cn_kotliner.h
        mkdir -p build/kotliner
        clang -std=c99  -c src/c/cn_kotliner.c -o build/kotliner/cn_kotliner.bc -emit-llvm
    
    clean:
          rm -rf build/kotliner
    

    采用命令行方式编译Kotlin Native时,需要先把编译器<konan.home>/bin目录加入系统的path环境中,然后再执行make命令,编译完成之后就可以在项目的build/kotliner目录中找到kotliner.kexe文件。

    在这里插入图片描述

    展开全文
  • Kotlin Native编程探索

    2018-04-14 13:02:02
    Kotlin简介 Kotlin是JetBains开发一种基于JVM的新的编程语言。Kotlin可以编译成字节码运行在JVM上,与Java完美兼容,并在...Kotlin Native利用LLVM来将Kotlin代码编译成本地机器代码,使得Kotlin可以脱离JVM运行。...

    Kotlin简介

    Kotlin是JetBains开发一种基于JVM的新的编程语言。Kotlin可以编译成字节码运行在JVM上,与Java完美兼容,并在Java的基础上添加了很多好用的特性。也正因为kotlin的种种优点,Google将Kotlin选为Android开发的一级语言。

    Kotlin Native利用LLVM来将Kotlin代码编译成本地机器代码,使得Kotlin可以脱离JVM运行。借助Kotlin Native,Kotlin也能被打包成lib、exe、dll等格式、运用于嵌入式或其他对性能要求较高的场景。

    Kotlin编译器准备

    这个编译器和Kotlin/JVM工程里的不一样,Kotlin/Native工程里是直接把代码编译成机器码而不是运行在虚拟机上的字节码。下载对应的编译器,或者直接从github上下载源码编译。直接下载release目录下的编译器可以略过下面的编译过程。

    首先运行下面的命令下载依赖包。

    ./gradlew dependencies:update
    

    然后运行下面的命令build编译器,因为编译整包的时间较长,所以建议使用下面的第二种方式或直接下载编译器。

    ./gradlew bundle     // 1.编译整包
    ./gradlew dist       // 2.只编译当前系统对应的包

    将Kotlin编译成机器码

    新建一个kotlin工程,选择使用gradle来构建。
    这里写图片描述
    在项目根目录下新建一个gradle.properties文件,配置编译器的路径。其中Windows下要注意路径分割符,Linux和mac因为分隔符是”/”所以正常写路径就行了。

    konan.home=D\:\\KotlinNative\\kotlin-native-windows-0.6.2         // 1.windows直接下载编译器
    konan.home=D\:\\KotlinNative\\kotlin-native\\dist                 // 2.windows下载源码编译

    在根目录下新建src/main/kotlin目录用于存放Kotlin代码,然后随手就是一个hello world。

    // Main.kt
    fun main(args: Array<String>) {
        println("Hello Kotlin Native")
    }

    编辑build.gradle的内容(其中我注释掉的内容为Kotlin/JVM的配置,可以让Kotlin代码在Intellij上运行,调试Kotlin代码的时候可以用),在把Kotlin编译成机器码的过程中只需要有Kotlin/Native的相关配置就行了。

    buildscript {
    //    ext.kotlin_version = '1.1.4'
    
        repositories {
            mavenCentral()
            maven {
                url "https://dl.bintray.com/jetbrains/kotlin-native-dependencies"
            }
        }
        dependencies {
            classpath "org.jetbrains.kotlin:kotlin-native-gradle-plugin:0.6"
    //        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        }
    }
    
    apply plugin: 'konan'
    //apply plugin: 'kotlin'
    
    repositories {
        mavenCentral()
    }
    
    //dependencies {
    //    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    //}
    
    konanArtifacts {
        // 这里只是把src/main/kotlin/Main.kt编译成一个名为foo的可执行文件
        program('foo') {
            srcFiles 'src/main/kotlin/Main.kt'
        }
    }

    上面build.gradle中的配置,konanArtifacts这个括号里的内容就是控制打包的相关配置,是我们主要关注的,主要有以下配置项,可以根据自己的需要选择(个人翻译,可以去github看英文原版)。

    // 默认编译的平台
    konan.targets = ['macbook', 'linux', 'wasm32']
    
    // kotlin和api的版本
    konan.languageVersion = 'version'
    konan.apiVersion = 'version'
    
    konanArtifacts {
        // 编译的目标平台 (如果没有的话就是上面konan.targets里面的平台)
        // 生成foo.exe(windows)/foo.kexe(linux)
        program('foo', targets: ['android_arm32', 'android_arm64']) {
    
            // 源代码路径,默认路径为 src/main/kotlin.
            srcDir 'src/other'
    
            // 源文件.
            srcFiles project.fileTree('src')
            srcFiles 'foo.kt', 'bar.kt'
    
            // 生成文件的名字,如果没有就是上面小括号中的foo.
            artifactName 'customName'
    
            // 文件输出的路径,默认为 build/konan/bin
            baseDir 'path/to/output/dir'
    
            libraries {
                // 库文件
                file 'foo.klib'
                files 'file1.klib', file2.klib
                files project.files('file3.klib', 'file4.klib')
    
                // 当前工程中生成的其他文件
                artifact konanArtifacts.bar
                artifact 'baz'
    
                // 其他工程中的生成文件
                artifact project(':path:to:a:project'), 'artifcatName'
    
                // 其他某个工程中生成的所有库文件
                allLibrariesFrom project(':some:project')
    
                // 其他某个工程中的所有的互操作类型的库文件
                allInteropLibrariesFrom project(':some:interop:project')
    
                // 根据名字查找的.klib库文件
                klib 'foo'
                klib 'bar', 'baz'
            }
    
            // 需要链接的native库 (*.bc).
            nativeLibrary project.file('path/to/native/library.bc')
            nativeLibraries 'library1.bc', 'library2.bc'
    
            noStdLib true             // 不链接stdlib (true/false).
            enableOptimizations true  // 开启编译优化 (true/false).
            enableAssertions true     // 在生成二进制文件的时候开启断言 (true/false).
            enableDebug true          // 在生成二进制文件的时候开启debug (true/false).
            noDefaultLibs true        // 不链接默认库
    
            // link参数.
            linkerOpts 'Some linker option', 'More linker options'
    
            // build时打印所有参数.
            dumpParameters true
    
            // 计算编译各阶段时间.
            measureTime true
    
            // 编译过程中依赖的其他任务.
            dependsOn anotherTask
    
            // 传递给编译器的额外命令行参数.
            extraOpts '--time', '--verbose', 'linker'
    
            // 额外编译Linux平台的.
            target('linux') {
                // 输出路径,默认为 <baseDir>/<target>
                destinationDir 'exact/output/path'
    
                // 可以添加和上面类似的其他配置.
            }
        }
    
        library('bar') {
            // 其他参数的配置与上面一样
            // 生成bar.klib文件,默认输出路径为 build/konan/libs
        }
    
        bitcode('baz') {
            // 其他参数的配置与上面一样
            // 生成baz.bc文件,默认输出路径为 build/konan/bitcode
        }
    
        dynamic('quux') {
            // 其他参数的配置与上面一样
            // 生成quux.dll和quux_api.h文件,默认输出路径为 is build/konan/bin
        }
    
        framework('quuux') {
            // 其他参数的配置与上面一样
            // Object-C framework文件,Windows下不支持,默认输出路径为 build/konan/bin
        }
    
        interop('qux') {
            // native API的描述文件,默认路径为 src/main/c_interop/<interop-name>.def
            defFile project.file("deffile.def")
    
             // 头文件.
            headers project.files('header1.h', 'header2.h')
    
            // 其他属性配置同上
    
            // 头文件目录.
            includeDirs {
                allHeaders 'path1', 'path2'
    
                // 根据.def文件中定义的 'headerFilter' 筛选出来的头文件.
                headerFilterOnly 'path1', 'path2'
            }
            // 目录下的所有头文件.
            includeDirs "include/directory" "another/directory"
    
            // 需要额外链接的文件.
            link <files which will be linked with native stubs>
        }
    }

    运行下面的命令或双击Intellij中的build选项进行编译

    ./gradlew build

    这里写图片描述
    编译完之后就可以在build/konan目录下发现生成的目标文件了。
    这里写图片描述
    因为是直接把kotlin代码直接编译成了机器代码,因此可以脱离虚拟机直接运行。
    这里写图片描述

    使用Kotlin和C混合编程

    新建一个src/main/c目录用于放置c文件和.h头文件,用c语言编写一个函数给kotlin调用,代码如下:
    这里写图片描述

    //kotlinor.h
    #ifndef KOTLINOR_H
    #define KOTLINOR_H
    int add(int a, int b);
    #endif
    //kotlinor.c
    #include "kotlinor.h"
    #include <stdio.h>
    int add(int a, int b){
        return a + b;
    }
    //Main.kt
    
    //导入的包名和下面konanArtifacts中配置的一致
    import myPkg.*
    fun main(args: Array<String>) {
        println(add(1, 3))
    }

    使用下面的命令通过c代码生成.bc(BitCode)文件供下面使用

    clang -std=c99 -c kotliner.c(c文件) -o kotliner.bc(生成的bc文件) -emit-llvm

    根据上面的介绍配置build.gradle,修改konanArtifacts中的内容

    konanArtifacts {
        // 这个名字下面会用到,必须保持一致
        interop('myInterop') {
            // kotlin代码中import的包名
            packageName 'myPkg'
            compilerOpts '-Isrc/c'
            // null.def 中没有添加配置,可以按自己需求添加
            defFile 'null.def'
            headers "src/main/c/kotlinor.h"
            includeDirs "src/main/c"
        }
    
        program('foo') {
            srcFiles 'src/main/kotlin/Main.kt'
            // 上一个步骤生成的bc文件
            nativeLibraries 'src/main/c/kotlinor.bc'
    
            libraries {
                // interop的名字
                artifact 'myInterop'
            }
        }
    }

    同样是使用./gradlew build命令编译,就可以在build目录下顺利生成目标文件了。
    这里写图片描述

    这里写图片描述

    PS

    • 第一手最新资料请参考jetbrains的github
      https://github.com/JetBrains/kotlin-native

    • 因为Kotlin/Native编译器是将kotlin编译成机器码的编译器,而不是运行在JVM上的字节码,所以在这里的kotlin代码中不能使用Java sdk里的API。

    • 生成的可执行文件,windows下后缀为exe、Linux下后缀为kexe。windows下可以用直接双击运行,如果程序运行时间很短的话只会看到一个黑框一闪而过。

    展开全文
  • 解读 Kotlin/Native 技术预览版


    https://www.qingjingjie.com/blogs/48


    很高兴Kotlin在前两天发布了Kotlin/Native的Tech Preview版本。Kotlin/Native能把Kotlin编译成机器码,也就是C/C++、Go和Rust的层次,于是这个领域又添一位竞争对手。

    JetBrains是一家务实的公司,它家的各种IDE让人赞不绝口。它创造的Kotlin也是一门务实的编程语言,借鉴Java和Scala的精华,以实用性为导向。

    Kotlin的宣传语是“100%可与Java互操作”,事实证明确实如此,极低的学习成本和采用成本,极高的开发效率提升,在Android上已经火得不得了了,我在服务器上也用得很开心。现在Kotlin编译到机器码,不运行在JVM,就不能沿用Java的生态系统了,而是要融入系统编程的生态系统。Kotlin也的确是想做一个高性能的“胶水”。在这个领域,失去了JVM的庇佑,却得到了新的力量。

    Kotlin已经实现了编译到JavaScript,现在又编译到机器码,野心不可谓不大。它的实力能否匹配野心呢?从JetBrains的 博客 来看,他们对Kotlin/Native也是很务实的态度,所以这个项目是值得期待的。技术预览版来了,Alpha和Beta还会远吗?欢欣鼓舞!

    Kotlin/Native利用LLVM来编译到机器码,现在已支持4个平台:

    • Linux (暂时只是Ubuntu),
    • Mac OS
    • iOS
    • Raspberry Pi

    这意味着开发者们已经可以愉快地开始体验了!

    Kotlin/Native仍然很看重互操作性(Interoperability)。它能高效地调用C函数,甚至还能从C头文件自动生成对应的Kotlin接口,发扬了JetBrains为开发者服务的良好传统!

    关于内存管理,现在的计划是在不同平台使用各自适合的内存管理技术,例如在服务器和桌面平台倾向于使用追踪式垃圾回收(tracing GC),而在iOS则倾向于使用iOS已有的ARC技术。在某些平台则可能手动管理内存。现在的Tech Preview版本只提供了一种附带循环检测的引用计数式内存管理,但并没有决定最终会怎样选择内存管理技术。

    这个版本基本上没做什么优化,所以在benchmark中可能会表现得不好。但是一定会努力优化,用实际数据说话,而不会拿理论来忽悠人。标准库和反射功能也还远未完成,但是愿意做这些东西已经让我觉得很良心了。

    博客中列出的未来计划包括:

    • 进军iOS
    • 进军嵌入式系统和物联网
    • 进军数据分析和科学计算
    • 进军服务端和微服务
    • 进军游戏开发

    进军iOS意味着要在移动端全制霸,而且还想进军这么多领域,似乎盘子有点太大了。我希望他们能把JavaScript先放下,毕竟很多语言都在这里折戟了,还记得当年的GWT吗?不如在Native层多多发力,先定一个小目标——抢Go语言的盘子。


    展开全文
  • 最近买了一本书,ReactNative开发原生应用,以前都是直接使用Kotlin开发,但是感觉自己是一个前端,不如用JS开发 吧,然后就边看书边开发了,写了一个简单的语音天气APP,基本上该遇到的坑和功能都使用了,tips:应用很小,...

    最近买了一本书,ReactNative开发原生应用,以前都是直接使用Kotlin开发,但是感觉最近一直写JS,不如用JS开发
    吧,然后就边看书边开发了,写了一个简单的语音天气APP,基本上该遇到的坑和功能都使用了,tips:应用很小,所以没有使用Redux.

    ReactNative和一些套壳的框架相比好多了,编译后是原生的代码,这个就是很有意思了,还能和原生混合开发.

     

    前排提示一个很坑的问题:RN0.6开始拆分了特定平台的组件到公共库里了,比如android的Toolbar,你直接在React-native引入是没用的.需要专门安装,一会会提到.

    看看成品吧,会在酷安上架哦,我的酷安ID:IWH暖

     

     

     

    本文总的结构如下:

    1.安装RN环境并解决一些坑

    2.开始编写android应用并使用android特定的组件

    3.打包APK,精简体积,签名,发布应用.

     

    1.准备开发环境

     

    安装RN脚手架

    yarn global add react-native-cli

    创建项目

    react-native init demo 

    如果创建项目特别慢,甚至出现网络超时,更换yarn源

    yarn config set registry https://registry.npm.taobao.org -g
    
    yarn config set sass_binary_site http://cdn.npm.taobao.org/dist/node-sass -g
    
    

     

    启动项目,报错没有命令的,查看自己是否全局安装了.

    react-native run-android

    为了预览与调试,你需要一个模拟器,我这里是直接用的android手机开启USB调试.

    使用 ADB devices 看看有没有你的设备.

    如果你没有安装android的SDK工具,会报错,你要安装好SDK(如果你用了androidStudio,那么进入demo项目的android目录

        项目android/local.properties
        追加 sdk.dir = /sdk目录 linux /home/userName/android...

    如果你遇到license错误

        android 许可错误
        进入sdk目录的 Sdk/tools/bin 执行 ./sdkmanager --licenses 同意许可...

     

    建议也检查以下你的bash配置,尤其是用ZSH的同学..

    nano ~/.bash_profile
    
    export ANDROID_HOME=/home/qiuguo/Android/Sdk
    export PATH=$PATH:$ANDROID_HOME/platform-tools
    export PATH=$PATH:$ANDROID_HOME/platform-tools/adb

    如果手机设备出现 服务连接失败,一般都是没启动服务

    项目根目录 yarn start 即可,注意默认端口8081不要被其他应用用了.或者你可以摇晃设备弹出调试菜单,手动更改ip与地址,然后

    adb reverse tcp:8081 tcp:8081 来自定义

    如果你遇到资源文件出错,或者未找到,执行以下命令

    react-native bundle --platform android --dev false --entry-file index.js  --bundle-output android/app/sr
    c/main/assets/index.android.bundle  --assets-dest android/app/src/main/res/

    既然可以预览了,那我们开始编写应用了.

    编写前,其实我画了一个简单的原型图

    查看原型演示:布谷天气墨刀原型演示

    2.开始编写

    这里不会讲解React如何编写,只说说RN的东西

    看一下初始化的项目的结构:其中Android是RN编译后的原生目录

    看到有 APP.js Index.js 这两个是入口文件,其他的和平时一样,自己创建组件目录或者资源目录就行了和web开发一样.

    看看index.js这是入口,其中registerComponent()是不是很熟悉,和web开发里面.APP是我们的最外层组件

    /**
     * @format
     */
    
    import {AppRegistry} from 'react-native';
    import App from './App';
    import {name as appName} from './app.json';
    
    AppRegistry.registerComponent(appName, () => App);
    

     

    看看 APP.js

    import React from 'react';
    //style.js 是一个json格式的css样式
    import {APP_STYLE} from './styles';
    //导入常用组件
    import {
        SafeAreaView,
        StyleSheet,
        ScrollView,
        Modal,
        ToastAndroid,
        View,
    
        Animated,
        TouchableNativeFeedback,
        Touchable,
        RefreshControl,
        TouchableHighlight,
        Image,
        Text,
        NativeEventEmitter,
        StatusBar,
    } from 'react-native';
    
    //这里是初始化项目自带的引导组件,你可以删除掉
    import {
        Header,
        LearnMoreLinks,
        Colors,
        DebugInstructions,
        ReloadInstructions,
    } from 'react-native/Libraries/NewAppScreen';
    
    //组件
    class App extends React.Component {
    
        state = {
            isSpeaching: false,
            helpStatus: false,
            refresh:false,
            voiceText: '',
            //是否选择地区
            isSelect: false,
            //语音状态
            voiceHide: true,
            //天气信息
            weatherData: {},
        };
        ....
        ....
        //样式使用styles
        render(){
            return(
                    //你也可以直接使用行内写法
                  <View style={styles.weatherBox}>
            )
        }
    
        
    }
    //创建样式表对象
    const styles = StyleSheet.create(APP_STYLE);
    export default App;

    APP_STYLE.js 样式表

    const APP_STYLE ={
    
        weatherBig: {
            fontSize: 70,
            marginTop: 30,
            marginLeft: 30,
            zIndex: 99
    
        }
    }

    1.你应该注意到了,全是驼峰写法,格式是json格式,这里有一个可能会遇到的困惑,就是 层级显示,用两个:Zindex和Elevation在android里面.

    其中 elevation优先.这个也是原生Android里面的z方向,会出现阴影,具体css你可以去RN官方文档查看哦,这里提一下默认的布局,你可以使用position为relative和absolute,不可以用fixed,还有就是RN的基础组件,如View这个组件,都是使用Flexbox,但是又有区别,比如,你想让子组件填充父组件,设置父组件 Flex:1 就行了.其他属性比如width支持百分比,记得加引号.

    2.样式基本了解后,我们看看如何编写事件

    和web有区别的是你需要在以下的组件内部编写按压事件

     TouchableNativeFeedback
     Touchable
     TouchableHighlight
     <TouchableHighlight onPress={this.props.closeHelp}>
                                <Text style={{marginTop: 20, color: '#BA1904',fontSize:20}}>关闭说明书</Text>
                            </TouchableHighlight>

    3.引入公共组件,看原型,好像我们用到了一个toolbar组件,如果你在React-native引入,会发现,无法使用,百度一圈,都是从rn包导入,那都是旧的版本,新版本你需要额外安装新的toolbar包

    facebook公共组件仓库 这里有额外的所有包,如video,netInfo等,这里演示一个toolbar,如果你的RN是0.6 不需要使用link ,网上说link是旧版的.

    yarn add @react-native-community/toolbar-android

    然后就可以愉快的使用了.

     <ToolbarAndroid
                            style={{ height: 50, backgroundColor: '#fff', color: '#000',elevation:2}}
                            actions={[{
                                title: '说明书',
                                icon: require('./res/images/help2.png'),
                                show: 'always',
                                showWithText: true,
                            },
                                {title: '关于', icon: require('./icon.png'), show: 'never'}]}
                            onActionSelected={this.onActionSelected.bind(this)}>
                            <Image source={require('./icon.png')} style={{width: 40, height: 30}}/>
                            <Text>布谷天气语音版</Text>
                        </ToolbarAndroid>

    4. 使用Gif与图片资源

    上面的代码里会发现用的 require().这里提一下一个坑,RN不支持路径使用动态拼接,也就是说你没有办法使用变量拼接!

    然后我们处理GIF的问题

    进入android/app/目录 找到 gradle文件 在dependencis 添加依赖

    implementation 'com.facebook.fresco:animated-gif:2.0.0'

    一些小伙伴发现图片不动,升级你的依赖,网上很多都是旧的..

    5.使用权限管理

    android有一些权限是需要申请使用的,fb也提供了在上面的公共库里哦,用法,这里不说了,提一句就是,没开发过Android的小伙伴经常会忘记在manifest文件声明权限!

    6.使用存储

    如果你用AsyncStorage 会遇到警告,因为它也被剥离出来了,你需要专门安装哦

    这里是一个封装片段

     async setData(key,value){
            try{
                await AsyncStorage.setItem(key,value);
                console.log('保存数据成功.')
                return  true;
            }catch (e) {
                console.log('保存错误:' + e)
                return false;
            }
        },

    7.使用网络

    RN使用默认支持Fetch,你也可以用axios,不过很遗憾jQuery不可以,因为它有涉及DOM的功能...

    注意RN不存在跨域的,你可以看看postman就知道了.

    8.动画 等有空再写.

    3.打包

    下面说说最重要的一项,打包APK出来,因为主要是说说遇到的错误,至于如何生成你的开发者秘钥,请自行百度哦,因为我以前用AndroidStudio开发应用,所以有一个jks的文件,如果你用的命令行创建会有好几个文件.

    配置你的秘钥,进入android/app/gradle.properties 设置几个变量,一会要用到的.jks文件也是在同级目录

    
    android.useAndroidX=true
    android.enableJetifier=true
    MYAPP_RELEASE_STORE_FILE=xxx.jks
    MYAPP_RELEASE_KEY_ALIAS=xx
    MYAPP_RELEASE_STORE_PASSWORD=xx
    MYAPP_RELEASE_KEY_PASSWORD=xxx

    然后修改构建文件

    android/app/build.gradle

    找到这个块,替换

    signingConfigs {
            release {
                storeFile file(MYAPP_RELEASE_STORE_FILE)
                storePassword MYAPP_RELEASE_STORE_PASSWORD
                keyAlias MYAPP_RELEASE_KEY_ALIAS
                keyPassword MYAPP_RELEASE_KEY_PASSWORD
            }
        }

    然后进入android根目录

    执行./gradlew assembleRelease 

    Rn打包的时候,会经常遇到以下错误,资源文件重复,部分依赖包出错(比如本应用使用的讯飞语音SDK,由于gradle不兼容,总是报错),正常来说,打包后,资源文件重复,要么是你的资源文件重名了,要么就是你打包之前,没有clean build后的项目.

    资源文件重复后,如果你执行了clean也是一样,手动把app/build/genera... 删除

    进入 android目录执行 ./gradlew clean 清理项目,再次 ./gradlew assembleRelease 构建APK

    当你打包后,你会发现,安装包非常大,不到1000行的代码有30M,因为RN默认打包了全平台版本,你可以自己选择是否支持其他平台

    我们修改一下 gradle文件 我只保留了arm架构版本,不过也生成一个通用的安装包,单独版本的大小现在是10M左右

    def enableSeparateBuildPerCPUArchitecture = true
    ...
    
    splits {
            abi {
                reset()
                enable enableSeparateBuildPerCPUArchitecture
                universalApk true  // If true, also generate a universal APK
                include "armeabi-v7a","arm64-v8a"
            }
        }
    ...
     universalApk true

    最后最重要的提示就是:一定要看清楚自己的RM版本,构建APK失败,先Clean一下,如果是第三方SDK可以去看看依赖包的gradle...

    ...等待更新

    公众号:秋果科技工作室

    酷安ID:IWH暖 来玩啊

     

    展开全文
  • 原标题: Kotlin : Slow List and Lazy Sequence 原文地址: https://medium.com/@elye.project/kotlin-slow-list-and-lazy-sequence-61691fc974c5 原文作者: Elye 自从Kotlin可以兼容Java7上后,很高兴的是我们...

    翻译说明:

    原标题: Kotlin : Slow List and Lazy Sequence

    原文地址: https://medium.com/@elye.project/kotlin-slow-list-and-lazy-sequence-61691fc974c5

    原文作者: Elye

    自从Kotlin可以兼容Java7上后,很高兴的是我们可以轻松地在List上使用各种集合操作符并且可以链式调用它们。但是我们知道在某些情况下使用List的迭代器并不是最好的方式,那么还有另一种方式就是使用序列(sequence)

    没有背景只能辛苦工作的List列表

    在我们了解序列在某些情况下为什么更好之前,让我告诉你一些关于List的内容。

    List内部使用Iterator进行操作。这是一个非常勤奋的群体,我链式调用它的每一个操作,它都能确保没有任何遗漏的完成。

    val list = listOf(1, 2, 3, 4, 5, 6)
    list.map{ it * 2 }.filter { it % 3  == 0 }.average()
    

    正如你在上面的插图中看到的,对于每一步操作,List的每个元素都需要被处理。

    为了证明这一点,让我们输出一些log日志:

    val list = listOf(1, 2, 3, 4, 5, 6)
    val result = list
            .map{ println("In Map"); it * 2 }
            .filter { println("In Filter");it % 3  == 0 }
    println("Before Average")
    println(result.average())
    

    结果如下:

    In Map
    In Map
    In Map
    In Map
    In Map
    In Map
    In Filter
    In Filter
    In Filter
    In Filter
    In Filter
    In Filter
    Before Average
    9.0
    

    很棒。勤奋努力地工作,并完成所有的过程。

    懒惰的家伙,Sequence序列…

    好的,现在让我们通过调用asSequence()扩展函数来将List转化成一个序列(Sequence)。

    val list = listOf(1, 2, 3, 4, 5, 6)
    val result = list.asSequence()
            .map{ println("In Map"); it * 2 }
            .filter { println("In Filter");it % 3  == 0 }
    println("Before Average")
    println(result.average())
    

    结果如下:

    Before Average
    In Map
    In Filter
    In Map
    In Filter
    In Map
    In Filter
    In Map
    In Filter
    In Map
    In Filter
    In Map
    In Filter
    9.0
    

    哇,有趣…,注意到 “Before Average” 是最先输出的,换句话说,如果我不调用 average() 函数,那么序列(sequence)就没有做任何操作。

    它很懒,不想做任何工作,直到终端连接到它。终端就像是一种操作,实际上就是一个操作符扩展函数,会返回其他类型结果(除了Sequence<T>之外),例如 sum(),average(),first()等…。甚至toList()用于将Sequence转换为List

    除此之外,你会注意到它输出的In MapIn Filter交叉出现。这意味着它会在通过链条之前一个接一个地通过链条,直到它通过终端,即平均操作,然后通过下一个元素。

    那么,序列Sequence到底有什么好处呢?

    如果你这样想,想象你想要拿到集合变换后的第一个元素。

    让我们看下List处理方式:

    val list = listOf(1, 2, 3, 4, 5, 6)
    val result = list
            .map{ println("In Map $it"); it * 2 }
            .filter { println("In Filter $it");it % 3  == 0 }
    println(result.first())
    

    结果如下:

    In Map 1
    In Map 2
    In Map 3
    In Map 4
    In Map 5
    In Map 6
    In Filter 2
    In Filter 4
    In Filter 6
    In Filter 8
    In Filter 10
    In Filter 12
    6
    

    所有在一起总共13行,这意味着13次操作。

    让我们看下Sequence处理方式:

    val sequence = sequenceOf(1, 2, 3, 4, 5, 6)
    val result = sequence
            .map{ println("In Map $it"); it * 2 }
            .filter { println("In Filter $it");it % 3  == 0 }
    println(result.first())
    

    结果是:

    In Filter 2
    In Map 2
    In Filter 4
    In Map 3
    In Filter 6
    6
    

    仅仅7行即7次操作。这意味着它只要找到第一个元素的那一刻,就会终止整个过程。

    你可以想像,这会加快整个运行的过程。

    加速仅仅只适用于first()操作吗?

    让我们做一些试验。

    试验Map操作

    val sequence = generateSequence(1) { it + 1 }.take(50000000)
    val list = sequence.toList()
    
    println("List Map Sum= " 
            + measureNanoTime { list.map { it * 2 }.sum() })
    println("Sequence Map Sum " 
            + measureNanoTime { sequence.map { it * 2 }.sum() })
    
    println("List Map Average " 
            + measureNanoTime { list.map { it * 2 }.average() })
    println("Sequence Map Average " 
            + measureNanoTime { sequence.map { it * 2 }.average() })
    

    结果是:

    List Map Sum 14727907362
    Sequence Map Sum 2074397969
    List Map Average 11460520785
    Sequence Map Average 3268960487
    
    • List: 在Map:Sum操作上花费了14.7s,在Map:Average操作上花费了11.5s
    • Sequence: 在Map:Sum操作上花费了2.1s, 在Map:Average操作上花费了3.3s

    看上去像前面的有一个Map操作时,Sequence的性能会比List更快。也许它不需要像List那样存储map操作后的中间结果,从而会更快。

    试验Filter操作

    val sequence = generateSequence(1) { it + 1 }.take(50000000)
    val list = sequence.toList()
    
    println("List Filter Sum " 
            + measureNanoTime { list.filter { it % 3 == 0 }.sum() })
    println("Sequence Filter Sum " 
            + measureNanoTime { sequence.filter { it % 3 == 0 }.sum() })
    
    println("List Filter Average " 
            + measureNanoTime { list.filter { it % 3 == 0 }.average() })
    println("Sequence Filter Average " 
            + measureNanoTime { sequence.filter { it % 3 == 0 }.average() })
    

    结果是:

    List Filter Sum 506351694
    Sequence Filter Sum 873175271
    List Filter Average 391790033
    Sequence Filter Average 838510968
    
    • List: 在Filter:Sum操作上花费了0.5s,在Filter:Average操作上花费了0.4s
    • Sequence: 在Filter:Sum操作上花费了0.9s, 在Filter:Average操作上花费了0.8s

    对于前面的Filter操作,Sequence比List更慢。 深入了解函数,看起来像Sequence的Filter操作需要有更多的开销来检查某些状态,而List的Filter则是一个简单的检查并收集新的元素。

    试验Map和Filter操作

    val sequence = generateSequence(1) { it + 1 }.take(50000000)
    val list = sequence.toList()
    
    println("List Map Filter Sum\t\t " + measureNanoTime { 
        list.map { it * 2 }.filter { it % 3 == 0 }.sum() })
    println("Sequence Map Filter Sum\t " + measureNanoTime { 
        sequence.map { it * 2 }.filter { it % 3 == 0 }.sum() })
    
    println("List Map Filter Average\t\t " + measureNanoTime { 
        list.map { it * 2 }.filter { it % 3 == 0 }.average() })
    println("Sequence Map Filter Average\t " + measureNanoTime { 
        sequence.map { it * 2 }.filter { it % 3 == 0 }.average() })
    

    结果是:

    List Map Filter Sum 34845242323
    Sequence Map Filter Sum 2820436086
    List Map Filter Average 2328258876
    Sequence Map Filter Average 18618444560
    
    • List: 在Map:Filter:Sum操作上花费了34.8s,在Map:Filter:Average操作上花费了2.3s
    • Sequence: 在Map:Filter:Sum操作上花费了2.8s, 在Map:Filter:Average操作上花费了18.6s

    一个相对令人惊讶的结果,如Map:Filter:Sum,Sequence比List快得多,而Map:Filter:Average,List比Sequence要快得多。

    试验直接使用Sequence和List

    val sequence = generateSequence(1) { it + 1 }.take(50000000)
    val list = sequence.toList()
    
    println("List Sum " + measureNanoTime { list.sum() })
    println("Sequence Sum " + measureNanoTime { sequence.sum() })
    
    println("List Average " + measureNanoTime { list.average() })
    println("Sequence Average " + measureNanoTime { sequence.average() })
    

    结果是:

    List Sum 91726022
    Sequence Sum 592771887
    List Average 101141460
    Sequence Average 622616340
    
    • List: 在Sum操作上花费了0.1s,在Average操作上花费了0.1s
    • Sequence: 在Sum操作上花费了0.5s, 在Average操作上花费了0.6s

    没有任何中间操作,明显列表List比序列Sequence要快。

    总结:

    • 1、当不需要中间操作时,使用List
    • 2、当仅仅只有map操作时,使用sequence
    • 3、当仅仅只有filter操作时,使用List
    • 4、如果末端操作是first时,使用sequence
    • 5、对于没有提及的其他操作符或者其他操作符的组合,请尝试使用例子去验证一下

    译者有话说:

    首先,说下为什么要翻译这篇博客?关于Kotlin中的Sequence和List的使用以及源码解析相关的文章我已经写过两篇了,这篇博客主要吸引我的一点就是以更多运行的例子试验和相关幽默的配图更加形象地描述了Sequence,List的区别以及各自的使用场景。

    然而,这篇博客并没有深入源码去讲解Sequence的实现,这篇之前写的博客 浅谈Kotlin中的序列(Sequences)源码完全解析(十) 从源码角度带你一步步分析Sequence序列背后的原理,关于如何正确使用Sequence和List以及各自使用场景,之前翻译的一篇博客 [译]Kotlin中是应该使用序列(Sequences)还是集合(Lists)? 会有更加全面的介绍。

    最后,有了这三篇文章应该更加全面理解了Sequence的原理和使用。

    欢迎关注Kotlin开发者联盟,这里有最新Kotlin技术文章,每周会不定期翻译一篇Kotlin国外技术文章。如果你也喜欢Kotlin,欢迎加入我们~~~

    Kotlin系列文章,欢迎查看:

    Kotlin邂逅设计模式系列:

    数据结构与算法系列:

    翻译系列:

    原创系列:

    Effective Kotlin翻译系列

    实战系列:

    展开全文
  • 友情提醒:如果没搞过注解处理器,这篇文章你看起来可能会比较迷。。什么是注解处理器话说,最近尝试了一下写了个注解处理器,也就是我们常见的 apt,在 Kotlin 当中有个...
  • 真是不要太给脸,就在前几天发布的 1.1 Beta 2 当中,所有协程的 API 包名后面都加了一个 experimental,这意味着 Kotlin 官方在 1.1 当中还是倾向于将 Coroutine 作为一个实验性质的特性的,不过,这也没关系,我们...
  • 上周我们把 Kotlin Coroutine 的基本 API 挨个讲了一下,也给出了一些简单的封装。真是不要太给脸,就在前几天发布的 1.1 Beta 2 当中,所有协程...
  • 简述: 又是一段新的开始,Dart这门语言相信很多人都是通过Flutter这个框架才了解的,因为Flutter相比Dart更被我们所熟知。很多人迟迟不愿尝试Flutter原因大多数是因为学习成本高,显然摆在面前的是需要去重新学习...
  • Docker是一个非常跨时代的工具,是在继虚拟化之后的新一个好用易用的工具,可以极大提升开发和部署的效率。本文将解决一个在Mac上部署Docker之时碰到的问题。 环境介绍 操作系统: MacOS 10.13.2 (17C205) ...
  • React RN移动端开发

    2018-12-27 23:21:39
    文章目录React RN移动端开发了解React-Native了解React-Native工作流程ReactNative中文网(查看流程和必须)创建第一个React-Native项目了解React-Native项目及结构开发资料配置reactnative文件模板项目开发路由(react...
  • public static Dialog creatDialog(final Context context, String tip){ Dialog dialog = new Dialog(context, R.style.appDialog); dialog.setContentView(R.layout.common_wdailog); dialog.setCanceledO
  • 好了,前面主要讲了Animation,Animator 的使用,以及桌面火箭效果和水波纹效果,分别使用Android框架和自己绘制实现,俗话说,工欲善其事,必先利其器,接下来几篇文章主要讲绘制中我们需要常使用的一些利器...
  • 作为忠实与较资深的Android汪, 最近抽出了一些时间研究了一下Google的亲儿子Flutter, 尚属皮毛, 只能算是个简单的记录吧. Google自2017年第一次提出Flutter, 到2018年Beta, 再加之RN的各种风波与问题, 使得Flutter...
1 2 3
收藏数 47
精华内容 18
关键字:

0.6 kotlin native