当前位置: 移动技术网 > IT编程>开发语言>Java > Android 4.0使用Kotlin调用C语言以及汇编语言

Android 4.0使用Kotlin调用C语言以及汇编语言

2020年08月02日  | 移动技术网IT编程  | 我要评论
如今,Google早已将Kotlin编程语言作为了头等语言(first-class programming language)用于Android开发中,并且在Android Studio中获得了非常全面的支持。与此同时,我们看到Google从Android Studio 3.0开始就已经支持了Java 8,过了这么多年仍然不对Java语言进行升级就能看到Google当前对Java已经持有相当冷淡的态度了,预计Java 8将是Android Studio最高能支持的Java版本了(*^_^*)。或许这跟Ora

如今,Google早已将Kotlin编程语言作为了头等语言(first-class programming language)用于Android开发中,并且在Android Studio中获得了非常全面的支持。与此同时,我们看到Google从Android Studio 3.0开始就已经支持了Java 8,过了这么多年仍然不对Java语言进行升级就能看到Google当前对Java已经持有相当冷淡的态度了,预计Java 8将是Android Studio最高能支持的Java版本了(*^_^*)。或许这跟Oracle之前的官司有关,不过Kotlin这种类Rust语言确实已经成为现代化编程语言的代名词了,小巧、轻便、简洁、安全。而且Kotlin 1.3还增加了Java所没有的无符号整数类型,尽管是实验阶段(由于动用了实验性的内联类),不过在1.4版本起就能正式开始使用了。

好了,废话不多说了,下面进入正题。上一节已经提供了Android开发相关链接还有Android Studio下载地址,各位下载完之后,Android Studio的包管理器会再自动下载一些必要的组件,然后我们开始创建一个新项目,笔者这里给项目起的名称为 KotlinTest。然后项目模板选择“Native C++”。这个项目模板会自动创建本地C/C++语言所需的各种属性以及CMakeList.txt文件,并且会使用空的activity作为起始Android启动框架。下面我们先来看一下笔者编辑好的 MainActivity.kt 这一Kotlin源文件。

package com.example.kotlintest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

private external fun packageMethod(i: Int)

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Example of a call to a native method
        sample_text.text = stringFromJNI()

        packageMethod(100)
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     * which is packaged with this application.
     */
    private external fun stringFromJNI(): String

    companion object {
        // Used to load the 'native-lib' library on application startup.
        init {
            System.loadLibrary("native-lib")
        }
    }
} 

上述代码很简单,基本就是项目向导自动生成的代码。这里声明了两个需要在native端实现的方法。一个是向导自动生成的 stringFromJNI() 成员方法,还有一个是笔者自己添加的 packageMethod(i: Int) 函数。Java中使用 native 关键字来声明一个本地端实现的方法;而Kotlin中则使用 external 关键字。Kotlin的成员方法桥接到本地代码的签名方式与Java完全一致,详细可见(Java JNI接口的详细描述)这篇博文;而Kotlin尽管没有Java那种纯粹的静态方法(或称为类方法),但它有文件作用域的函数,其签名方式比起静态方法大致相同,并且更为简单。两者不同的是静态方法的签名末尾是 <包名> . <类名> . <静态方法名> 的方式;而函数签名则是 <包名> . <kotlin源文件名> . <函数名> 的方式。我们知道,Kotlin的源文件名都是以 kt 作为文件后缀名的,因此这里的 <kotlin源文件名> 其实就是去除后缀的源文件名加上 _kt 。像上述 packageMethod(i: Int) 的完整函数签名为:Java_com_example_kotlintest_MainActivityKt_packageMethod

接着我们就可以来看一下项目向导自动创建的 native-lib.c 源文件名的代码了:

#include <jni.h>
#include <syslog.h>
#include <cpu-features.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#ifndef let
#define let     __auto_type
#endif

extern int MyASMTest(int a, int b);

JNIEXPORT jstring JNICALL
Java_com_example_kotlintest_MainActivity_stringFromJNI(JNIEnv* env, jobject object)
{
    let const value = 100;
    const char *s = "Hello from C";
    syslog(LOG_INFO, "The value is: %d\n", value);
    return (*env)->NewStringUTF(env, s);
}

JNIEXPORT void JNICALL
Java_com_example_kotlintest_MainActivityKt_packageMethod(JNIEnv *env, jclass clazz, jint i)
{
    // TODO: implement packageMethod()
    let a = MyASMTest(5, 3) + i;
    syslog(LOG_INFO, "a = %d\n", a);
} 

大家可以注意到,上述代码与向导自动生成的不太一样。这是由于向导自动生成的是庞杂且臃肿的C艹代码,而笔者这里已经改为轻便且优美的C语言代码了。而这里的 MyASMTest 函数则是用汇编来实现的,后面会详细介绍。这里大家能看到 stringFromJNI()packageMethod(i: Int) 桥接到本地代码后的完整签名方式,包括两者参数类型也是所有不同的。由于定义在文件作用域的函数后续无论是在成员方法中调用,还是在companion object块中的静态方法中被调用,它的调用者都是这些方法所属的,而不是对象实例!因此 packageMethod 的第二个参数类型铁定是 jclass ,而不是 jobject,尽管从JNI类型架构上,jclass是jobject的子类型。

这里再提一点,有些朋友会问,那我们能不能在companian object中声明一个本地方法呢?这是完全可以的,但不推荐。因为companion object其实是一个匿名模块,因此当你使用这里面的“静态方法”桥接到本地端时,其方法签名会带有此匿名模块的编号,比如对于上述例子,可能会是:Java_com_example_kotlintest_MainActivity_0012_packageMethod 这种形式,一方面很别扭,另一方面可能会有二进制兼容、移植上等问题。

上面两个主要源文件看完之后,我们接下去就要准备设置项目,对整个工程进行编译了。在此之前,我们先关闭当前的Android Studio。然后,到文件管理器找到创建的这一项目的目录,在 app/src/main/cpp 文件夹中我们可以找到向导自动生成的 native-lib.cpp 和 CMakeList.txt 这两个文件。我们先把 native-lib.cpp 改为:native-lib.c。然后创建文件夹,命名为 asm 。然后在此文件夹中新增三个文件,分别为:x86_asmtest.asmarm32_asmtest.sarm64_armtest.s

上面已经给出了 native-lib.c 源文件的内容,下面给出先给出三个汇编文件的内容。

x86_asmtest.asm

; 这是一个汇编文件
; YASM的注释风格使用分号形式

global MyASMTest

section .text

MyASMTest:

    sub     edi, esi
    mov     eax, edi
    ret 

arm32_asmtest.s

.text
.align 4

.globl MyASMTest

MyASMTest:

    sub     r0, r0, r1
    bx      lr 

arm64_asmtest.s

.text
.align 4

.globl MyASMTest

MyASMTest:

    sub     w0, w0, w1
    ret 

各位也可以选择在编辑完CMakeList.txt文件之后在Android Studio中进行编辑。Android Studio 4.0已经能支持汇编语言的语法高亮了。

下面是比较关键的,对CMakeList.txt的编辑,请看以下代码:

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

if(${ANDROID_ABI} STREQUAL "x86_64")
    enable_language(ASM_NASM)
    set(asm_SRCS ${PROJECT_SOURCE_DIR}/asm/x86_asmtest.asm)
elseif(${ANDROID_ABI} STREQUAL "arm64-v8a")
    enable_language(ASM)
    set(asm_SRCS ${PROJECT_SOURCE_DIR}/asm/arm64_asmtest.s)
elseif(${ANDROID_ABI} STREQUAL "armeabi-v7a")
    enable_language(ASM)
    set(asm_SRCS ${PROJECT_SOURCE_DIR}/asm/arm32_asmtest.s)
endif()

include(AndroidNdkModules)
android_ndk_import_module_cpufeatures()

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library(
        # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        ${asm_SRCS}
        native-lib.c)

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library(
        # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log
        android)

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries(
        # Specifies the target library.
        native-lib
        cpufeatures
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib}) 

这里对x86_64汇编源文件使用NASM汇编器进行编译,而ARM的代码均由GAS汇编器进行编译。此外,这里还引入了 cpufeatureandroid 这两个额外的库,有需要的话后面可以直接使用。

上述文件都编辑完之后,我们重新打开Android Studio,进入刚才创建的KotlinTest这一i项目,然后在左侧导航栏中鼠标右键点击 app 这个文件夹,然后选择“Reload from disk”,进行文件同步。这么一来,我们就能在 cpp -> native-lib 下看到 native-lib.c、还有新建的 asm 文件夹,包括里面所有文件了。

我们随后可以编辑 build.gradle (Module: app) 这一文件,添加相关的编译选项。我们找到 externalNativeBuild 这一符号,将这一语句块替换为如下所示的代码:

externalNativeBuild {
            cmake {
                // 对于ARMv7架构使用NEON技术,并且对于32位的ARM执行模式使用ARM指令集而不是默认的Thumb指令集
                arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_ARM_MODE=arm"

                // C语言使用GNU17标准
                cFlags "-std=gnu17", "-Os"
            }
            ndk {
                // Specifies the ABI configurations of your native
                // libraries Gradle should build and package with your APK.
                abiFilters 'x86_64', 'armeabi-v7a', 'arm64-v8a'
            }
        }

这里对C语言启用了GNU17这一最新的C18标准外加GNU语法扩展。然后对ARMv7架构启用了NEON指令集扩展。同时,指定了当前本地二进制代码只生成x86_64、ARMv7以及ARM64-v8三种架构,以减少不必要的架构二进制。而且Google也已经声明了ARMv7以下的架构即将作废,后面将最低支持ARMv7的处理器。而32位的x86架构当然也是不需要的。

设置完之后我们就可以启动模拟器或是真机进行运行了。运行模拟器的童鞋请注意,如果是第一次启用,Google会默认安装带有Google Play的模拟器,这比较麻烦,需要apk签名,因此笔者的做法是新建一个模拟器,不带Google Play的,然后将处理器架构设置为x86_64,这样就能直接跑app了。

此外,笔者也建议各位使用Windows系统的童鞋可以将文件编码都改为无BOM的UTF-8编码格式。这样项目源文件具有更好的跨平台性,尤其是带有中文等其他语言文字,因为国内Windows系统上默认会使用古老且非现代化标准的GBK编码。方法是在上方菜单栏中的左侧点击“File”,然后点击“Settings...”,进入对话框后展开“Editor”,然后点击“File Encodings”,将这里面的所有编码方式设置为UTF-8。

本文地址:https://blog.csdn.net/zenny_chen/article/details/107732197

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网