Android Rust 模式

本頁包含有關Android Logging的信息,提供Rust AIDL 示例,告訴您如何從 C 調用 Rust ,並提供Rust/C++ Interop Using CXX的說明。

安卓日誌

以下示例顯示瞭如何將消息記錄到logcat (設備上)或stdout (主機上)。

在您的Android.bp模塊中,添加libloggerliblog_rust作為依賴項:

rust_binary {
    name: "logging_test",
    srcs: ["src/main.rs"],
    rustlibs: [
        "liblogger",
        "liblog_rust",
    ],
}

接下來,在您的 Rust 源代碼中添加以下代碼:

use log::{debug, error, Level};

fn main() {
    let init_success = logger::init(
        logger::Config::default()
            .with_tag_on_device("mytag")
            .with_min_level(Level::Trace),
    );
    debug!("This is a debug message.");
    error!("Something went wrong!");
}

也就是說,添加上面顯示的兩個依賴項( libloggerliblog_rust ),調用一次init方法(如果需要,可以多次調用它),並使用提供的宏記錄消息。有關可能的配置選項列表,請參閱logger crate

logger crate 提供了一個 API 來定義你想要記錄的內容。根據代碼是在設備上運行還是在主機上運行(例如主機端測試的一部分),使用android_loggerenv_logger記錄消息。

Rust AIDL 示例

本節提供了一個 Hello World 風格的示例,將 AIDL 與 Rust 結合使用。

使用 Android 開發人員指南AIDL 概述部分作為起點,在IRemoteService.aidl文件中創建external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl並包含以下內容:

// IRemoteService.aidl
package com.example.android;

// Declare any non-default types here with import statements

/** Example service interface */
interface IRemoteService {
    /** Request the process ID of this service, to do evil things with it. */
    int getPid();

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}

然後,在external/rust/binder_example/aidl/Android.bp文件中,定義aidl_interface模塊。您必須顯式啟用Rust 後端,因為默認情況下未啟用它。

aidl_interface {
    name: "com.example.android.remoteservice",
    srcs: [ "aidl/com/example/android/*.aidl", ],
    unstable: true, // Add during development until the interface is stabilized.
    backend: {
        rust: {
            // By default, the Rust backend is not enabled
            enabled: true,
        },
    },
}

AIDL 後端是一個 Rust 源代碼生成器,因此它像其他 Rust 源代碼生成器一樣運行並生成一個 Rust 庫。生成的 Rust 庫模塊可以被其他 Rust 模塊用作依賴項。作為使用生成庫作為依賴項的示例, rust_library可以在external/rust/binder_example/Android.bp中定義如下:

rust_library {
    name: "libmyservice",
    srcs: ["src/lib.rs"],
    crate_name: "myservice",
    rustlibs: [
        "com.example.android.remoteservice-rust",
        "libbinder_rs",
    ],
}

請注意, rustlibs中使用的 AIDL 生成庫的模塊名稱格式是aidl_interface模塊名稱後跟-rust ;在這種情況下, com.example.android.remoteservice-rust

然後可以在src/lib.rs中引用 AIDL 接口,如下所示:

// Note carefully the AIDL crates structure:
// * the AIDL module name: "com_example_android_remoteservice"
// * next "::aidl"
// * next the AIDL package name "::com::example::android"
// * the interface: "::IRemoteService"
// * finally, the 'BnRemoteService' and 'IRemoteService' submodules

//! This module implements the IRemoteService AIDL interface
use com_example_android_remoteservice::aidl::com::example::android::{
  IRemoteService::{BnRemoteService, IRemoteService}
};
use com_example_android_remoteservice::binder::{
    BinderFeatures, Interface, Result as BinderResult, Strong,
};

/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyService;

impl Interface for MyService {}

impl IRemoteService for MyService {
    fn getPid(&self) -> BinderResult<i32> {
        Ok(42)
    }

    fn basicTypes(&self, _: i32, _: i64, _: bool, _: f32, _: f64, _: &str) -> BinderResult<()> {
        // Do something interesting...
        Ok(())
    }
}

最後,在 Rust 二進製文件中啟動服務,如下所示:

use myservice::MyService;

fn main() {
    // [...]
    let my_service = MyService;
    let my_service_binder = BnRemoteService::new_binder(
        my_service,
        BinderFeatures::default(),
    );
    binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
    // Does not return - spawn or perform any work you mean to do before this call.
    binder::ProcessState::join_thread_pool()
}

從 C 調用 Rust

這個例子展示瞭如何從 C 中調用 Rust。

示例 Rust 庫

external/rust/simple_printer/libsimple_printer.rs中定義libsimple_printer文件如下:

//! A simple hello world example that can be called from C

#[no_mangle]
/// Print "Hello Rust!"
pub extern fn print_c_hello_rust() {
    println!("Hello Rust!");
}

Rust 庫必須定義依賴 C 模塊可以拉入的頭文件,因此定義external/rust/simple_printer/simple_printer.h頭文件如下:

#ifndef SIMPLE_PRINTER_H
#define SIMPLE_PRINTER_H

void print_c_hello_rust();


#endif

定義external/rust/simple_printer/Android.bp ,如下所示:

rust_ffi {
    name: "libsimple_c_printer",
    crate_name: "simple_c_printer",
    srcs: ["libsimple_c_printer.rs"],

    // Define include_dirs so cc_binary knows where the headers are.
    include_dirs: ["."],
}

示例 C 二進製文件

定義external/rust/c_hello_rust/main.c如下:

#include "simple_printer.h"

int main() {
  print_c_hello_rust();
  return 0;
}

定義external/rust/c_hello_rust/Android.bp如下:

cc_binary {
    name: "c_hello_rust",
    srcs: ["main.c"],
    shared_libs: ["libsimple_c_printer"],
}

最後,通過調用m c_hello_rust構建。

Rust–Java 互操作

jni crate 通過 Java Native Interface (JNI) 提供 Rust 與 Java 的互操作性。它為 Rust 定義了必要的類型定義,以生成直接插入 Java 的 JNI( JNIEnvJClassJString等)的 Rust cdylib庫。與通過cxx執行代碼生成的 C++ 綁定不同,通過 JNI 的 Java 互操作性在構建期間不需要代碼生成步驟。因此它不需要特殊的構建系統支持。 Java 代碼像任何其他本地庫一樣加載 Rust 提供的cdylib

用法

jni crate 文檔中介紹了 Rust 和 Java 代碼中的用法。請按照那裡提供的入門示例進行操作。編寫src/lib.rs後,返回此頁面了解如何使用 Android 的構建系統構建庫。

構建定義

Java 要求將 Rust 庫作為cdylib提供,以便可以動態加載。 Soong 中的 Rust 庫定義如下:

rust_ffi_shared {
    name: "libhello_jni",
    crate_name: "hello_jni",
    srcs: ["src/lib.rs"],

    // The jni crate is required
    rustlibs: ["libjni"],
}

Java 庫將 Rust 庫列為required的依賴項;這確保它與 Java 庫一起安裝到設備上,即使它不是構建時依賴項:

java_library {
        name: "libhelloworld",
        [...]
        required: ["libhellorust"]
        [...]
}

或者,如果您必須在AndroidManifest.xml文件中包含 Rust 庫,請將該庫添加到uses_libs ,如下所示:

java_library {
        name: "libhelloworld",
        [...]
        uses_libs: ["libhellorust"]
        [...]
}

使用 CXX 的 Rust–C++ 互操作

CXX crate 在 Rust 和 C++ 子集之間提供安全的 FFI。 CXX 文檔給出了它一般如何工作的很好的例子。以下示例展示瞭如何在 Android 中使用它。

要讓 CXX 生成 Rust 調用的 C++ 代碼,定義一個genrule來調用 CXX 和一個cc_library_static將它捆綁到一個庫中。如果您計劃讓 C++ 調用 Rust 代碼,或使用 C++ 和 Rust 之間共享的類型,請定義第二個 genrule(以生成包含 Rust 綁定的 C++ 標頭)。

cc_library_static {
    name: "libcxx_test_cpp",
    srcs: ["cxx_test.cpp"],
    generated_headers: [
        "cxx-bridge-header",
        "libcxx_test_bridge_header"
    ],
    generated_sources: ["libcxx_test_bridge_code"],
}

genrule {
    name: "libcxx_test_bridge_code",
    tools: ["cxxbridge"],
    cmd: "$(location cxxbridge) $(in) >> $(out)",
    srcs: ["lib.rs"],
    out: ["libcxx_test_cxx_generated.cc"],
}

// This defines a second genrule to generate a C++ header.
// * The generated header contains the C++ bindings to the Rust exported
// * functions in lib.rs.

genrule {
    name: "libcxx_test_bridge_header",
    tools: ["cxxbridge"],
    cmd: "$(location cxxbridge) $(in) --header >> $(out)",
    srcs: ["lib.rs"],
    out: ["lib.rs.h"],
}

然後將其鏈接到 Rust 庫或可執行文件:

rust_binary {
    name: "cxx_test",
    srcs: ["lib.rs"],
    rustlibs: ["libcxx"],
    static_libs: ["libcxx_test_cpp"],
}

.cpp.hpp文件中,根據需要使用CXX 包裝器類型定義 C++ 函數。例如, cxx_test.hpp定義包含以下內容:

#pragma once

#include "rust/cxx.h"

int greet(rust::Str greetee);

cxx_test.cpp包含

#include "cxx_test.hpp"
#include "lib.rs.h"

#include <iostream>

int greet(rust::Str greetee) {
  std::cout << "Hello, " << greetee << std::endl;
  return get_num();
}

要在 Rust 中使用它,請在lib.rs中定義一個 CXX 橋,如下所示:

#[cxx::bridge]
mod ffi {
    unsafe extern "C++" {
        include!("cxx_test.hpp");
        fn greet(greetee: &str) -> i32;
    }
    extern "Rust" {
        fn get_num() -> i32;
    }
}

fn main() {
    let result = ffi::greet("world");
    println!("C++ returned {}", result);
}

fn get_num() -> i32 {
    return 42;
}