أنماط الصدأ Android

تحتوي هذه الصفحة على معلومات حول Android Logging ، وتوفر مثالًا لـ Rust AIDL ، وتخبرك بكيفية استدعاء Rust من C ، وتوفر إرشادات لـ Rust / C ++ Interop Using 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, 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!");
}

أي ، أضف التبعيتين الموضحين أعلاه ( liblogger و liblog_rust ) ، واستدع التابع init مرة واحدة (يمكنك تسميته أكثر من مرة إذا لزم الأمر) ، وقم بتسجيل الرسائل باستخدام وحدات الماكرو المتوفرة. انظر صندوق المسجل للحصول على قائمة بخيارات التكوين الممكنة.

يوفر صندوق المسجل واجهة برمجة تطبيقات لتحديد ما تريد تسجيله. اعتمادًا على ما إذا كان الرمز يعمل على الجهاز أو على المضيف (مثل جزء من اختبار من جانب المضيف) ، يتم تسجيل الرسائل باستخدام android_logger أو env_logger .

مثال على الصدأ AIDL

يقدم هذا القسم مثالاً على نمط Hello World لاستخدام AIDL مع الصدأ.

باستخدام قسم نظرة عامة حول AIDL في دليل مطور Android كنقطة بداية ، قم بإنشاء 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_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 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.

مثال مكتبة الصدأ

حدد ملف 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 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 .

الصدأ - جافا التشغيل المتداخل

يوفر صندوق jni إمكانية التشغيل التفاعلي بين Rust مع Java من خلال واجهة Java الأصلية (JNI). يحدد تعريفات النوع الضرورية لـ Rust لإنتاج مكتبة Rust cdylib التي يتم توصيلها مباشرة بـ JNI الخاص بـ Java ( JNIEnv و JClass و JString وما إلى ذلك). على عكس روابط C ++ التي تؤدي عملية الترميز من خلال cxx ، فإن قابلية التشغيل البيني لـ Java من خلال JNI لا تتطلب خطوة إنشاء رمز أثناء الإنشاء. لذلك لا يحتاج إلى دعم خاص لنظام البناء. تقوم شفرة Java بتحميل cdylib الذي يوفره Rust مثل أي مكتبة أصلية أخرى.

إستعمال

يتم تغطية الاستخدام في كل من كود Rust و Java في وثائق jni crate . يرجى اتباع مثال " البدء " المقدم هناك. بعد كتابة src/lib.rs ، ارجع إلى هذه الصفحة لمعرفة كيفية إنشاء المكتبة باستخدام نظام إنشاء Android.

بناء التعريف

تتطلب Java أن يتم توفير مكتبة Rust كـ cdylib بحيث يمكن تحميلها ديناميكيًا. تعريف مكتبة Rust في Soong هو كما يلي:

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 ++ Interop باستخدام CXX

يوفر صندوق CXX FFI آمنًا بين الصدأ ومجموعة فرعية من C ++. تعطي وثائق CXX أمثلة جيدة عن كيفية عملها بشكل عام. يوضح المثال التالي كيفية استخدامه في Android.

لجعل CXX تنشئ كود C ++ الذي يستدعي Rust إليه ، حدد قاعدة لاستدعاء CXX و genrule cc_library_static في مكتبة. إذا كنت تخطط للحصول على كود Rust لاستدعاء C ++ ، أو استخدام الأنواع المشتركة بين C ++ و 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 ، حدد وظائف C ++ كما تريد ، باستخدام أنواع غلاف CXX حسب الرغبة. على سبيل المثال ، يحتوي تعريف 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 ، حدد جسر 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;
}