تحتوي هذه الصفحة على معلومات حول تسجيل الدخول إلى Android مثال Rust AIDL، ويوضح لك كيفية الاتصال بـ Rust من C ، وتقديم التعليمات التشغيل التفاعلي Rust/C++ باستخدام CXX
تسجيل Android
يوضح المثال التالي كيفية تسجيل الرسائل في logcat
(على الجهاز) أو
stdout
(مضيف)
في وحدة Android.bp
، أضِف liblogger
وliblog_rust
كملحقات:
rust_binary {
name: "logging_test",
srcs: ["src/main.rs"],
rustlibs: [
"liblogger",
"liblog_rust",
],
}
بعد ذلك، في مصدر Rust، أضف هذه التعليمة البرمجية:
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!");
}
وهذا يعني إضافة الاعتمادين التابعين الموضحين أعلاه (liblogger
وliblog_rust
)،
استدعينا طريقة init
مرة واحدة (يمكنك استدعاؤها أكثر من مرة إذا لزم الأمر)
تسجيل الرسائل باستخدام وحدات الماكرو المتوفرة. يمكنك الاطّلاع على
قفص لسجلات تسجيل الدخول
للحصول على قائمة بخيارات الضبط الممكنة.
يوفر صندوق المسجِّل واجهة برمجة تطبيقات لتحديد ما تريد تسجيله. استنادًا إلى ما إذا كان الرمز يعمل على الجهاز فقط أو على المضيف (مثل جزء من من جهة المضيف)، يتم تسجيل الرسائل باستخدام أمر android_logger أو env_logger.
مثال Rust AIDL
يقدّم هذا القسم مثالاً على طريقة Hello World لاستخدام AIDL مع Rust.
استخدام دليل مطوّري برامج Android نظرة عامة على AIDL
كنقطة بداية، أنشئ external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl
مع المحتوى التالي في ملف 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",
],
}
يُرجى العِلم أنّ تنسيق اسم الوحدة للمكتبة التي يتم إنشاؤها من خلال AIDL المستخدَمة في rustlibs
هو اسم الوحدة aidl_interface
متبوعًا بـ -rust
؛ وهو في هذه الحالة،
com.example.android.remoteservice-rust
بعد ذلك، يمكن الإشارة إلى واجهة AIDL في src/lib.rs
على النحو التالي:
// 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(())
}
}
أخيرًا، ابدأ الخدمة في برنامج 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.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()
}
مثال على Async Rust AIDL
يقدّم هذا القسم مثالاً على طريقة Hello World لاستخدام AIDL مع Rust غير المتزامن.
بالمتابعة في المثال RemoteService
، تم إنشاء مكتبة الواجهة الخلفية AIDL.
تتضمن واجهات غير متزامنة يمكن استخدامها لتنفيذ خادم غير متزامن
تنفيذ لواجهة AIDL RemoteService
.
يمكن أن تكون واجهة الخادم غير المتزامنة IRemoteServiceAsyncServer
التي تم إنشاؤها
على النحو التالي:
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(())
}
}
يمكن بدء تنفيذ الخادم غير المتزامن على النحو التالي:
#[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();
});
}
لاحظ أن
block_in_place
مطلوبة لترك السياق غير المتزامن الذي يسمح لـ join_thread_pool
باستخدام
block_on داخليًا. يرجع السبب في ذلك إلى أنّ #[tokio::main]
تلفّ الرمز.
في مكالمة مع رقم block_on
، وقد يتم الاتصال من "join_thread_pool
"
block_on
عند التعامل مع معاملة واردة. الاتصال بـ
block_on
من داخل block_on
يؤدي إلى حالة من الذعر. يمكن أن يؤدي هذا أيضًا إلى
من خلال إنشاء بيئة تشغيل Tokio
يدويًا
بدلاً من استخدام "#[tokio::main]
" ثم الاتصال بـ "join_thread_pool
"
خارج طريقة block_on
.
علاوة على ذلك، تتضمن المكتبة التي تم إنشاؤها في Rut backend واجهة تسمح
تنفيذ برنامج غير متزامن IRemoteServiceAsync
لـ RemoteService
والذي يمكنه
على النحو التالي:
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),
}
}
الاتصال بـ Rust من C
يوضح هذا المثال كيفية استدعاء Rust من C.
مثال على مكتبة Rust
تحديد الملف libsimple_printer
في external/rust/simple_printer/libsimple_printer.rs
على النحو التالي:
//! 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 export_include_dirs so cc_binary knows where the headers are.
export_include_dirs: ["."],
}
المثال ج الثنائي
تعريف 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
إمكانية التشغيل التفاعلي مع JavaScript مع Java من خلال Java Native.
الواجهة (JNI). يحدد تعريفات النوع اللازمة لإنتاج Rust
مكتبة Rust cdylib
تصل مباشرةً إلى JNI في Java (JNIEnv
، JClass
،
JString
وما إلى ذلك). على عكس عمليات ربط C++ التي تنفِّذ عملية ترميز من خلال cxx
،
لا تتطلب إمكانية التشغيل التفاعلي لـ Java من خلال JNI خطوة لإنشاء الرمز
أثناء عملية الإنشاء. وبالتالي، لا يحتاج إلى دعم خاص في نظام إنشاء المحتوى. لغة Java
يُحمِّل الرمز cdylib
الذي تم تقديمه من خلال Rust، مثل أي مكتبة أخرى أصلية.
الاستخدام
يتم تناول الاستخدام في كل من رمز Rust وJava في
مستندات jni
حول الصناديق من فضلك
عليك اتّباع تعليمات البدء
المثال المذكور هنا. بعد كتابة src/lib.rs
، ارجع إلى هذه الصفحة إلى
تعرّف على كيفية إنشاء المكتبة باستخدام نظام تصميم Android.
إنشاء التعريف
تتطلب لغة Java تقديم مكتبة Rust باعتبارها cdylib
بحيث يمكن استخدامها
بشكل ديناميكي. تعريف مكتبة Rust في Sayg هو ما يلي:
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"]
[...]
}
بدلاً من ذلك، إذا كان عليك تضمين مكتبة Rust في AndroidManifest.xml
، أضف المكتبة إلى uses_libs
على النحو التالي:
java_library {
name: "libhelloworld",
[...]
uses_libs: ["libhellorust"]
[...]
}
إمكانية التشغيل التفاعلي Rust–C++ باستخدام CXX
قفص CXX يوفّر معلومات مالية آمنة بين Rust ومجموعة فرعية من C++. مستندات CXX أمثلة جيدة عن كيفية عملها بشكل عام، ونقترح عليك قراءتها أولاً للتعرف على المكتبة والطريقة التي تربط بها بين C++ وRust. تشير رسالة الأشكال البيانية المثال التالي كيفية استخدامه في Android.
لتمكين CXX من إنشاء رمز C++ الذي يستدعي Rust إليه، حدِّد genrule
:
باستدعاء CXX وcc_library_static
لتجميع ذلك في مكتبة. إذا كنت تخطط
أو استخدام C++ لاستدعاء لغة Rust، أو استخدام الأنواع المشتركة بين C++ وRust، حدد
الجيل الثاني (لإنشاء عنوان C++ يحتوي على روابط 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"],
}
أداة cxxbridge
أعلاه لإنشاء جانب C++ من الجسر. libcxx_test_cpp
يتم استخدام المكتبة الثابتة بعد ذلك كتبعية لملف Rust التنفيذي:
rust_binary {
name: "cxx_test",
srcs: ["lib.rs"],
rustlibs: ["libcxx"],
static_libs: ["libcxx_test_cpp"],
}
في الملفين .cpp
و.hpp
، حدد دوال C++ كما تريد،
باستخدام أنواع برامج تضمين CXX على النحو المطلوب.
على سبيل المثال، يحتوي تعريف cxx_test.hpp
على ما يلي:
#pragma once
#include "rust/cxx.h"
#include "lib.rs.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، حدِّد جسر CXX كما هو موضّح أدناه في 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;
}