Pola Karat Android

Halaman ini berisi informasi tentang Android Logging , memberikan contoh Rust AIDL , memberi tahu Anda cara memanggil Rust dari C , dan memberikan instruksi untuk Rust/C++ Interop Menggunakan CXX .

Pencatatan Android

Contoh berikut menunjukkan bagaimana Anda dapat mencatat pesan ke logcat (di perangkat) atau stdout (di host).

Di modul Android.bp Anda, tambahkan liblogger dan liblog_rust sebagai dependensi:

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

Selanjutnya, di sumber Rust Anda tambahkan kode ini:

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!");
}

Yaitu, tambahkan dua dependensi yang ditunjukkan di atas ( liblogger dan liblog_rust ), panggil metode init sekali (Anda dapat memanggilnya lebih dari sekali jika perlu), dan mencatat pesan menggunakan makro yang disediakan. Lihat peti logger untuk daftar kemungkinan opsi konfigurasi.

Peti logger menyediakan API untuk menentukan apa yang ingin Anda log. Bergantung pada apakah kode berjalan di perangkat atau di host (seperti bagian dari pengujian sisi host), pesan dicatat menggunakan android_logger atau env_logger .

Contoh AIDL karat

Bagian ini memberikan contoh gaya Hello World dalam menggunakan AIDL dengan Rust.

Menggunakan bagian Ikhtisar AIDL Panduan Pengembang Android sebagai titik awal, buat external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl dengan konten berikut di file 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);
}

Kemudian, di dalam file external/rust/binder_example/aidl/Android.bp , tentukan modul aidl_interface . Anda harus secara eksplisit mengaktifkan backend Rust karena tidak diaktifkan secara default.

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,
        },
    },
}

Backend AIDL adalah generator sumber Rust, sehingga beroperasi seperti generator sumber Rust lainnya dan menghasilkan perpustakaan Rust. Modul pustaka Rust yang dihasilkan dapat digunakan oleh modul Rust lainnya sebagai dependensi. Sebagai contoh penggunaan library yang dihasilkan sebagai dependensi, rust_library dapat didefinisikan sebagai berikut di 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",
    ],
}

Perhatikan bahwa format nama modul untuk perpustakaan yang dihasilkan AIDL yang digunakan di rustlibs adalah nama modul aidl_interface diikuti oleh -rust ; dalam hal ini, com.example.android.remoteservice-rust .

Antarmuka AIDL kemudian dapat dirujuk di src/lib.rs sebagai berikut:

// 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(())
    }
}

Akhirnya, mulai layanan dalam biner Rust seperti yang ditunjukkan di bawah ini:

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()
}

Memanggil Rust dari C

Contoh ini menunjukkan cara memanggil Rust dari C.

Contoh Perpustakaan Karat

Definisikan file libsimple_printer di external/rust/simple_printer/libsimple_printer.rs sebagai berikut:

//! 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!");
}

Pustaka Rust harus mendefinisikan header yang dapat ditarik oleh modul C dependen, jadi tentukan header external/rust/simple_printer/simple_printer.h sebagai berikut:

#ifndef SIMPLE_PRINTER_H
#define SIMPLE_PRINTER_H

void print_c_hello_rust();


#endif

Tentukan external/rust/simple_printer/Android.bp seperti yang Anda lihat di sini:

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: ["."],
}

Contoh C biner

Definisikan external/rust/c_hello_rust/main.c sebagai berikut:

#include "simple_printer.h"

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

Definisikan external/rust/c_hello_rust/Android.bp sebagai berikut:

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

Terakhir, bangun dengan memanggil m c_hello_rust .

Rust–Java interop

jni menyediakan interoperabilitas Rust dengan Java melalui Java Native Interface (JNI). Ini mendefinisikan definisi tipe yang diperlukan untuk Rust untuk menghasilkan perpustakaan cdylib Rust yang dihubungkan langsung ke JNI Java ( JNIEnv , JClass , JString , dan seterusnya). Tidak seperti binding C++ yang menjalankan codegen melalui cxx , interoperabilitas Java melalui JNI tidak memerlukan langkah pembuatan kode selama build. Oleh karena itu tidak memerlukan dukungan sistem build khusus. Kode Java memuat cdylib yang disediakan Rust seperti pustaka asli lainnya.

Penggunaan

Penggunaan dalam kode Rust dan Java tercakup dalam dokumentasi jni crate . Silakan ikuti contoh Memulai yang disediakan di sana. Setelah Anda menulis src/lib.rs , kembali ke halaman ini untuk mempelajari cara membangun perpustakaan dengan sistem pembangunan Android.

Membangun definisi

Java membutuhkan perpustakaan Rust untuk disediakan sebagai cdylib sehingga dapat dimuat secara dinamis. Definisi perpustakaan Rust di Soong adalah sebagai berikut:

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

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

Pustaka Java mencantumkan pustaka Rust sebagai dependensi yang required ; ini memastikan itu diinstal ke perangkat di samping perpustakaan Java meskipun itu bukan ketergantungan waktu pembuatan:

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

Atau, jika Anda harus menyertakan pustaka Rust dalam file AndroidManifest.xml , tambahkan pustaka ke uses_libs sebagai berikut:

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

Rust–C++ Interop Menggunakan CXX

Peti CXX menyediakan FFI yang aman antara Rust dan subset dari C++. Dokumentasi CXX memberikan contoh yang baik tentang cara kerjanya secara umum. Contoh berikut menunjukkan cara menggunakannya di Android.

Agar CXX menghasilkan kode C++ yang dipanggil oleh Rust, tentukan genrule untuk memanggil CXX dan cc_library_static untuk menggabungkannya ke dalam perpustakaan. Jika Anda berencana untuk memanggil kode Rust C++, atau menggunakan tipe yang dibagikan antara C++ dan Rust, tentukan genrule kedua (untuk menghasilkan header C++ yang berisi binding Rust).

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"],
}

Kemudian tautkan ini ke perpustakaan Rust atau yang dapat dieksekusi:

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

Pada file .cpp dan .hpp , tentukan fungsi C++ sesuai keinginan, menggunakan jenis pembungkus CXX sesuai keinginan. Misalnya, definisi cxx_test.hpp berisi berikut ini:

#pragma once

#include "rust/cxx.h"

int greet(rust::Str greetee);

Sementara cxx_test.cpp berisi

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

#include <iostream>

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

Untuk menggunakan ini dari Rust, tentukan jembatan CXX seperti di bawah ini di lib.rs :

#[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;
}