Bu sayfada Android Logging, bir Rust AIDL örneği sağlar ve C'den Rust'ı arar ve talimatlar verir CXX Kullanan Rust/C++ Birlikte Çalışabilirlik içindir.
Android günlük kaydı
Aşağıdaki örnekte, mesajları logcat
(cihaz üzerinde) veya
stdout
(ana makinede).
Android.bp
modülünüzde liblogger
ve liblog_rust
değerlerini bağımlılık olarak ekleyin:
rust_binary {
name: "logging_test",
srcs: ["src/main.rs"],
rustlibs: [
"liblogger",
"liblog_rust",
],
}
Sonra, Rust kaynağınıza şu kodu ekleyin:
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!");
}
Yani yukarıda gösterilen iki bağımlılığı (liblogger
ve liblog_rust
) ekleyin.
init
yöntemini bir kez çağırın (gerekirse birden fazla kez çağırabilirsiniz) ve
iletileri günlüğe kaydeder. Bkz.
logger kasası
sayfasına bakın.
Günlük kaydedici kasası, günlüğe kaydetmek istediğinizi tanımlamak için bir API sağlar. Şuna bağlı olarak: kodun cihaz üzerinde mi yoksa ana makinede mi (örneğin, ana makine taraflı testi) mesajları, android_logger veya env_logger ekleyin.
Rust AIDL örneği
Bu bölümde, AIDL ile Rust'ın kullanımı hakkında Hello World tarzı bir örnek verilmiştir.
Android Geliştirici Kılavuzu'nu kullanma AIDL'ye Genel Bakış
bölümünde başlangıç noktası olarak external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl
oluşturun
IRemoteService.aidl
dosyasında şu içeriğe sahip olmalıdır:
// 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);
}
Ardından, external/rust/binder_example/aidl/Android.bp
dosyasında
aidl_interface
modülü. Rust arka ucunu açıkça etkinleştirmeniz gerekir, çünkü
varsayılan olarak etkin değildir.
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 arka ucu bir Rust kaynağı oluşturucu olduğundan diğer Rust kaynakları gibi çalışır
jeneratörler ve Rust kitaplığı üretiyor. Üretilen Rust kitaplığı modülü
diğer Rust modülleri tarafından bağımlılık olarak
kullanıldığını öğrendik. Oluşturulan bu
bağımlılığı varsa rust_library
aşağıdaki gibi tanımlanabilir:
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
içinde kullanılan, AIDL tarafından oluşturulan kitaplığın modül adı biçiminin olduğunu unutmayın
aidl_interface
modülü adı ve ardından -rust
; Bu örnekte
com.example.android.remoteservice-rust
.
Daha sonra AIDL arayüzüne src/lib.rs
içinde aşağıdaki şekilde referans verilebilir:
// 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(())
}
}
Son olarak, hizmeti aşağıda gösterildiği gibi bir Rust ikili programında başlatın:
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()
}
Eş zamansız Rust AIDL örneği
Bu bölümde, AIDL'nin eşzamansız Rust ile kullanımı hakkında Hello World tarzı bir örnek verilmiştir.
RemoteService
örneğinden devam edelim. Oluşturulan AIDL arka uç kitaplığı
eşzamansız bir sunucuyu uygulamak için kullanılabilecek eş zamansız arayüzler içerir
RemoteService
AIDL arayüzünün uygulanması.
Oluşturulan eş zamansız sunucu arayüzü (IRemoteServiceAsyncServer
)
aşağıdaki gibi uygulanır:
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(())
}
}
Eş zamansız sunucu uygulaması şu şekilde başlatılabilir:
#[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();
});
}
Lütfen
block_in_place
join_thread_pool
kullanıcısının kullanmasına izin veren eşzamansız bağlamı
block_on'u dahili olarak devre dışı bırakın. Bunun nedeni, #[tokio::main]
ürününün kodu sarmalamasıdır.
block_on
adlı kişiye yapılan aramada, join_thread_pool
arayabilir
block_on
. Çağrı yapılıyor:
block_on
içinden yapılan block_on
paniğe neden oluyor. Bu da
tokio çalışma zamanını oluşturarak
manuel olarak
#[tokio::main]
numaralı telefonu kullanmak yerine join_thread_pool
numaralı telefonu arayın
block_on
yönteminin dışında.
Ayrıca, pas arka uç tarafından oluşturulan kitaplık,
RemoteService
için eş zamansız istemci IRemoteServiceAsync
uygulanıyor. Bu istemci,
aşağıdaki gibi uygulanır:
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),
}
}
C'den Rust'ı ara
Bu örnekte, C'den Rust'ın nasıl aranacağı gösterilmektedir.
Örnek Rust kitaplığı
external/rust/simple_printer/libsimple_printer.rs
içinde libsimple_printer
dosyasını tanımlayın
şu şekildedir:
//! 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 kitaplığı, bağımlı C modüllerinin alabileceği başlıkları tanımlamalıdır.
dolayısıyla external/rust/simple_printer/simple_printer.h
başlığını şu şekilde tanımlayın:
#ifndef SIMPLE_PRINTER_H
#define SIMPLE_PRINTER_H
void print_c_hello_rust();
#endif
external/rust/simple_printer/Android.bp
terimini burada gösterildiği gibi tanımlayın:
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: ["."],
}
Örnek C ikili programı
external/rust/c_hello_rust/main.c
öğesini şu şekilde tanımlayın:
#include "simple_printer.h"
int main() {
print_c_hello_rust();
return 0;
}
external/rust/c_hello_rust/Android.bp
öğesini şu şekilde tanımlayın:
cc_binary {
name: "c_hello_rust",
srcs: ["main.c"],
shared_libs: ["libsimple_c_printer"],
}
Son olarak, m c_hello_rust
yöntemini çağırarak derleme yapın.
Rust-Java birlikte çalışabilirliği
jni
kasası, Java Yerel aracılığıyla Java ile Rust birlikte çalışabilirliği sağlar
Arayüz (JNI). Rust’ın üretmesi için gerekli tür tanımlarını tanımlar
Java’nın JNI'sine doğrudan bağlanan bir Rust cdylib
kitaplığı (JNIEnv
, JClass
,
JString
vb.). cxx
aracılığıyla kod oluşturma işlemi gerçekleştiren C++ bağlamalarının aksine,
JNI üzerinden Java birlikte çalışabilirlik için kod oluşturma adımı gerekmez
inceleyeceğiz. Dolayısıyla özel bir derleme sistemi desteğine ihtiyaç duymaz. Java
kodu, Rust tarafından sağlanan cdylib
öğesini diğer herhangi bir yerel kitaplık gibi yükler.
Kullanım
Hem Rust hem de Java kodundaki kullanım
jni
sandık belgeleri. Lütfen
Başlarken'deki
örneğini burada görebilirsiniz. src/lib.rs
yazdıktan sonra bu sayfaya dönerek için
Android'in derleme sistemiyle kitaplığı nasıl oluşturacağınızı öğrenin.
Derleme tanımı
Java, Rust kitaplığının cdylib
olarak sağlanmasını gerektirir ve böylece
dinamik bir şekilde yüklenir. Shortg'daki Rust kitaplığının tanımı aşağıdaki gibidir:
rust_ffi_shared {
name: "libhello_jni",
crate_name: "hello_jni",
srcs: ["src/lib.rs"],
// The jni crate is required
rustlibs: ["libjni"],
}
Java kitaplığı, Rust kitaplığını bir required
bağımlılığı olarak listeler;
bu şekilde, uygulama çalıştırılsa bile JavaScript kitaplığıyla birlikte cihaza yüklenir.
bir derleme zamanı bağımlılığı değildir:
java_library {
name: "libhelloworld",
[...]
required: ["libhellorust"]
[...]
}
Alternatif olarak, Rust kitaplığını bir AndroidManifest.xml
dosyasında, kitaplığı aşağıdaki gibi uses_libs
öğesine ekleyin:
java_library {
name: "libhelloworld",
[...]
uses_libs: ["libhellorust"]
[...]
}
CXX kullanarak Rust–C++ birlikte çalışabilirlik
CXX kasası güvenli FFI sağlar C++ alt kümesi arasında 30 kilometre kaldı. CXX belgeleri işin geneline dair güzel örnekler veriyor. Önce okumanızı öneririz. kitaplığını ve C++ ile Rust'ı birbirine nasıl bağladığını öğrenin. İlgili içeriği oluşturmak için kullanılan aşağıdaki örnekte, bunun Android'de nasıl kullanılacağı gösterilmektedir.
CXX'nin Rust'ın çağırdığı C++ kodunu oluşturmasını sağlamak için bir genrule
tanımlayın
CXX ve cc_library_static
çağrılarını bir kitaplıkta gruplandırmak için kullanılır. Planınız:
C++ ile Rust kodunu çağırmak veya C++ ile Rust arasında paylaşılan türleri kullanmak için bir
ikinci nesil (Paslama bağlamalarını içeren bir C++ başlığı oluşturmak için).
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"],
}
cxxbridge
aracı
değeri, köprünün C++ tarafını oluşturmak için kullanılır. libcxx_test_cpp
statik kitaplıktan yararlanırız:
rust_binary {
name: "cxx_test",
srcs: ["lib.rs"],
rustlibs: ["libcxx"],
static_libs: ["libcxx_test_cpp"],
}
.cpp
ve .hpp
dosyalarında C++ işlevlerini istediğiniz gibi tanımlayın.
CXX sarmalayıcı türlerini tercih edebilirsiniz.
Örneğin, bir cxx_test.hpp
tanımı şunları içerir:
#pragma once
#include "rust/cxx.h"
#include "lib.rs.h"
int greet(rust::Str greetee);
cxx_test.cpp
şunu içerirken:
#include "cxx_test.hpp"
#include "lib.rs.h"
#include <iostream>
int greet(rust::Str greetee) {
std::cout << "Hello, " << greetee << std::endl;
return get_num();
}
Bunu Rust'tan kullanmak için lib.rs
içinde aşağıdaki gibi bir CXX köprüsü tanımlayın:
#[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;
}