Android Paslanma kalıpları

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