الگوهای زنگ زدگی اندروید

این صفحه حاوی اطلاعاتی درباره Android Logging است، یک مثال Rust AIDL را ارائه می دهد، به شما می گوید که چگونه Rust را از C فراخوانی کنید، و دستورالعمل هایی را برای Rust/C++ Interop با استفاده از CXX ارائه می دهد.

ورود به سیستم اندروید

مثال زیر نشان می دهد که چگونه می توانید پیام ها را به 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 را فراخوانی کنید (در صورت لزوم می توانید بیش از یک بار آن را فراخوانی کنید) و پیام ها را با استفاده از ماکروهای ارائه شده ثبت کنید. برای لیستی از گزینه های پیکربندی ممکن، جعبه لاگر را ببینید.

جعبه لاگر یک API برای تعریف آنچه می خواهید ثبت کنید فراهم می کند. بسته به اینکه کد روی دستگاه اجرا می‌شود یا روی میزبان (مانند بخشی از آزمایش سمت میزبان)، پیام‌ها با استفاده از android_logger یا env_logger ثبت می‌شوند .

Rust AIDL مثال

این بخش نمونه ای به سبک Hello World از استفاده از AIDL با Rust را ارائه می دهد.

با استفاده از بخش AIDL Developer Guide به عنوان نقطه شروع، 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 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()
}

فراخوانی Rust از سی

این مثال نحوه فراخوانی 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 .

Rust-Java interop

جعبه jni قابلیت همکاری Rust را با جاوا از طریق Java Native Interface (JNI) فراهم می کند. این تعاریف نوع لازم را برای Rust تعریف می کند تا یک کتابخانه Rust cdylib تولید کند که مستقیماً به JNI جاوا ( JNIEnv ، JClass ، JString ، و غیره) وصل می شود. برخلاف پیوندهای C++ که کدژن را از طریق cxx انجام می‌دهند، قابلیت همکاری جاوا از طریق JNI نیازی به مرحله تولید کد در طول ساخت ندارد. بنابراین نیازی به پشتیبانی سیستم ساخت خاصی ندارد. کد جاوا cdylib ارائه شده توسط Rust را مانند هر کتابخانه بومی دیگری بارگیری می کند.

استفاده

استفاده در کدهای Rust و Java در مستندات جعبه jni پوشش داده شده است. لطفاً مثال شروع ارائه شده در آنجا را دنبال کنید. پس از نوشتن src/lib.rs ، به این صفحه بازگردید تا نحوه ساخت کتابخانه با سیستم ساخت اندروید را بیاموزید.

تعریف بسازید

جاوا نیاز دارد که کتابخانه 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"],
}

کتابخانه جاوا کتابخانه Rust را به عنوان یک وابستگی required فهرست می کند. این اطمینان حاصل می کند که در کنار کتابخانه جاوا روی دستگاه نصب شده است، حتی اگر یک وابستگی زمان ساخت نیست:

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

از طرف دیگر، اگر باید کتابخانه Rust را در فایل AndroidManifest.xml قرار دهید، کتابخانه را به صورت زیر به uses_libs اضافه کنید:

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

Rust–C++ Interop با استفاده از CXX

جعبه CXX FFI ایمن را بین Rust و زیر مجموعه ای از C++ فراهم می کند. مستندات CXX مثال های خوبی از نحوه عملکرد آن به طور کلی ارائه می دهد. مثال زیر نحوه استفاده از آن را در اندروید نشان می دهد.

برای اینکه CXX کد C++ را که Rust به آن فراخوانی می‌کند تولید کند، یک genrule برای فراخوانی CXX و یک cc_library_static برای بسته‌بندی آن در یک کتابخانه تعریف کنید. اگر قصد دارید کد Rust را فراخوانی C++ داشته باشید، یا از انواع مشترک بین 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"],
}

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