Halaman ini berisi informasi tentang Logging Android, memberikan contoh AIDL Rust, memberi tahu Anda cara memanggil Rust dari C , dan memberikan petunjuk untuk Interop Rust/C++ Menggunakan CXX.
Logging Android
Contoh berikut menunjukkan cara mencatat pesan ke logcat
(di perangkat) atau
stdout
(di host).
Di modul Android.bp
, tambahkan liblogger
dan liblog_rust
sebagai dependensi:
rust_binary {
name: "logging_test",
srcs: ["src/main.rs"],
rustlibs: [
"liblogger",
"liblog_rust",
],
}
Selanjutnya, tambahkan kode ini di sumber Rust Anda:
use log::{debug, error, LevelFilter};
fn main() {
let _init_success = logger::init(
logger::Config::default()
.with_tag_on_device("mytag")
.with_max_level(LevelFilter::Trace),
);
debug!("This is a debug message.");
error!("Something went wrong!");
}
Artinya, tambahkan dua dependensi yang ditampilkan di atas (liblogger
dan liblog_rust
),
panggil metode init
satu kali (Anda dapat memanggilnya lebih dari sekali jika perlu), dan
catat pesan menggunakan makro yang disediakan. Lihat
logger crate
untuk mengetahui daftar kemungkinan opsi konfigurasi.
Crate logger menyediakan API untuk menentukan apa yang ingin Anda catat. 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 Rust
Bagian ini memberikan contoh gaya Hello World tentang penggunaan AIDL dengan Rust.
Dengan menggunakan bagian Ringkasan AIDL dalam Panduan Developer Android sebagai titik awal, buat external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl
dengan konten berikut dalam 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, dalam file external/rust/binder_example/aidl/Android.bp
, tentukan modul
aidl_interface
. Anda harus mengaktifkan secara eksplisit backend Rust karena backend ini 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 library Rust. Modul library Rust yang dihasilkan dapat
digunakan oleh modul Rust lain sebagai dependensi. Sebagai contoh penggunaan library yang dihasilkan sebagai dependensi, rust_library
dapat ditentukan 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 library yang dihasilkan AIDL yang digunakan di rustlibs
adalah nama modul aidl_interface
yang diikuti dengan -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 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(())
}
}
Terakhir, mulai layanan dalam biner Rust seperti yang ditunjukkan di bawah:
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.as_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()
}
Contoh AIDL Rust Asinkron
Bagian ini memberikan contoh gaya Hello World tentang penggunaan AIDL dengan Rust asinkron.
Melanjutkan contoh RemoteService
, library backend AIDL yang dihasilkan mencakup antarmuka asinkron yang dapat digunakan untuk menerapkan implementasi server asinkron untuk antarmuka AIDL RemoteService
.
Antarmuka server asinkron IRemoteServiceAsyncServer
yang dihasilkan dapat diterapkan sebagai berikut:
use com_example_android_remoteservice::aidl::com::example::android::IRemoteService::{
BnRemoteService, IRemoteServiceAsyncServer,
};
use binder::{BinderFeatures, Interface, Result as BinderResult};
/// This struct is defined to implement IRemoteServiceAsyncServer AIDL interface.
pub struct MyAsyncService;
impl Interface for MyAsyncService {}
#[async_trait]
impl IRemoteServiceAsyncServer for MyAsyncService {
async fn getPid(&self) -> BinderResult<i32> {
//Do something interesting...
Ok(42)
}
async fn basicTypes(&self, _: i32, _: i64, _: bool, _: f32, _: f64,_: &str,) -> BinderResult<()> {
//Do something interesting...
Ok(())
}
}
Implementasi server asinkron dapat dimulai sebagai berikut:
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
let my_service = MyAsyncService;
let my_service_binder = BnRemoteService::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder.as_binder())
.expect("Failed to register service?");
task::block_in_place(move || {
binder::ProcessState::join_thread_pool();
});
}
Perhatikan bahwa
block_in_place
diperlukan untuk keluar dari konteks asinkron yang memungkinkan join_thread_pool
menggunakan
block_on secara internal. Hal ini karena #[tokio::main]
membungkus kode
dalam panggilan ke block_on
, dan join_thread_pool
mungkin memanggil
block_on
saat menangani transaksi masuk. Memanggil
block_on
dari dalam block_on
akan menyebabkan panik. Hal ini juga dapat dihindari dengan membuat runtime tokio secara manual, bukan menggunakan #[tokio::main]
, lalu memanggil join_thread_pool
di luar metode block_on
.
Selain itu, library yang dihasilkan backend Rust menyertakan antarmuka yang memungkinkan penerapan klien asinkron IRemoteServiceAsync
untuk RemoteService
yang dapat diterapkan sebagai berikut:
use com_example_android_remoteservice::aidl::com::example::android::IRemoteService::IRemoteServiceAsync;
use binder_tokio::Tokio;
#[tokio::main(flavor = "current_thread")]
async fn main() {
let binder_service = binder_tokio::wait_for_interface::<dyn IRemoteServiceAsync<Tokio>>("myservice");
let my_client = binder_service.await.expect("Cannot find Remote Service");
let result = my_client.getPid().await;
match result {
Err(err) => panic!("Cannot get the process id from Remote Service {:?}", err),
Ok(p_id) => println!("PID = {}", p_id),
}
}
Memanggil Rust dari C
Contoh ini menunjukkan cara memanggil Rust dari C.
Contoh library Rust
Tentukan file libsimple_printer
diexternal/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!");
}
Library Rust harus menentukan 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 export_include_dirs so cc_binary knows where the headers are.
export_include_dirs: ["."],
}
Biner Contoh C
Tentukan external/rust/c_hello_rust/main.c
seperti berikut:
#include "simple_printer.h"
int main() {
print_c_hello_rust();
return 0;
}
Tentukan external/rust/c_hello_rust/Android.bp
seperti berikut:
cc_binary {
name: "c_hello_rust",
srcs: ["main.c"],
shared_libs: ["libsimple_c_printer"],
}
Terakhir, bangun dengan memanggil m c_hello_rust
.
Interop Rust-Java
Crate jni
menyediakan interoperabilitas Rust dengan Java melalui Java Native
Interface (JNI). File ini menentukan definisi jenis yang diperlukan agar Rust dapat menghasilkan
library cdylib
Rust yang terhubung langsung ke JNI Java (JNIEnv
, JClass
,
JString
, dan sebagainya). Tidak seperti binding C++ yang melakukan pembuatan kode 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 library native lainnya.
Penggunaan
Penggunaan dalam kode Rust dan Java dibahas dalam
dokumentasi crate jni
. Ikuti contoh Memulai yang diberikan di sana. Setelah Anda menulis src/lib.rs
, kembali ke halaman ini untuk
mempelajari cara membangun library dengan sistem build Android.
Definisi build
Java memerlukan library Rust untuk disediakan sebagai cdylib
agar dapat dimuat secara dinamis. Definisi library 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"],
}
Library Java mencantumkan library Rust sebagai dependensi required
; hal ini memastikan library Rust diinstal ke perangkat bersama dengan library Java meskipun bukan dependensi waktu build:
java_library {
name: "libhelloworld",
[...]
required: ["libhellorust"]
[...]
}
Atau, jika Anda harus menyertakan library Rust dalam file AndroidManifest.xml
, tambahkan library ke uses_libs
sebagai berikut:
java_library {
name: "libhelloworld",
[...]
uses_libs: ["libhellorust"]
[...]
}
Interop Rust–C++ menggunakan CXX
Crate CXX menyediakan FFI yang aman antara Rust dan subset C++. Dokumentasi CXX memberikan contoh yang baik tentang cara kerjanya secara umum dan kami menyarankan untuk membacanya terlebih dahulu agar terbiasa dengan library dan cara menghubungkan C++ dan Rust. Contoh berikut menunjukkan cara menggunakannya di Android.
Agar CXX menghasilkan kode C++ yang dipanggil Rust, tentukan genrule
untuk memanggil CXX dan cc_library_static
untuk menggabungkannya ke dalam library. Jika Anda berencana
agar C++ memanggil kode Rust, atau menggunakan jenis yang dibagikan antara C++ dan Rust, tentukan
aturan gen 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"],
}
// Generate the C++ code that Rust calls into.
genrule {
name: "libcxx_test_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) > $(out)",
srcs: ["lib.rs"],
out: ["libcxx_test_cxx_generated.cc"],
}
// Generate a C++ header containing 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"],
}
Alat cxxbridge
digunakan di atas untuk membuat sisi C++ dari jembatan. Library statis libcxx_test_cpp
selanjutnya digunakan sebagai dependensi untuk executable Rust kita:
rust_binary {
name: "cxx_test",
srcs: ["lib.rs"],
rustlibs: ["libcxx"],
static_libs: ["libcxx_test_cpp"],
}
Dalam file .cpp
dan .hpp
, tentukan fungsi C++ sesuai keinginan Anda,
menggunakan jenis wrapper CXX sesuai keinginan.
Misalnya, definisi cxx_test.hpp
berisi hal berikut:
#pragma once
#include "rust/cxx.h"
#include "lib.rs.h"
int greet(rust::Str greetee);
Meskipun 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 menggunakannya 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;
}