Android Pas Kalıpları

Bu sayfa Android Logging hakkında bilgiler içerir, bir Rust AIDL örneği sağlar, size Rust'u C'den nasıl çağıracağınızı anlatır ve CXX Kullanarak Rust/C++ Birlikte Çalışma talimatları sağlar.

Android Günlüğü

Aşağıdaki örnek, mesajları logcat (cihazda) veya stdout (ana bilgisayar üzerinde) nasıl kaydedebileceğinizi gösterir.

Android.bp modülünüzde liblogger ve liblog_rust bağımlılıklar olarak ekleyin:

rust_binary {
    name: "logging_test",
    srcs: ["src/main.rs"],
    rustlibs: [
        "liblogger",
        "liblog_rust",
    ],
}

Ardından, Rust kaynağınıza şu kodu ekleyin:

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!");
}

Yani, yukarıda gösterilen iki bağımlılığı ekleyin ( liblogger ve liblog_rust ), init yöntemini bir kez çağırın (gerekirse bir kereden fazla çağırabilirsiniz) ve sağlanan makroları kullanarak mesajları günlüğe kaydedin. Olası yapılandırma seçeneklerinin bir listesi için kaydedici kasasına bakın.

Kaydedici kasası, neyi günlüğe kaydetmek istediğinizi tanımlamak için bir API sağlar. Kodun cihazda mı yoksa ana bilgisayarda mı çalıştığına bağlı olarak (örneğin, ana bilgisayar tarafı testinin bir parçası), mesajlar android_logger veya env_logger kullanılarak günlüğe kaydedilir.

Rust AIDL örneği

Bu bölüm, Rust ile AIDL kullanımına ilişkin Merhaba Dünya tarzı bir örnek sağlar.

Başlangıç ​​noktası olarak Android Geliştirici Kılavuzu AIDL'ye Genel Bakış bölümünü kullanarak, IRemoteService.aidl dosyasında aşağıdaki içeriklerle external/rust/binder_example/aidl/com/example/android/IRemoteService.aidl oluşturun:

// 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ı içinde aidl_interface modülünü tanımlayın. Varsayılan olarak etkinleştirilmediğinden Rust arka ucunu açıkça etkinleştirmelisiniz .

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 kaynak oluşturucudur, bu nedenle diğer Rust kaynak oluşturucuları gibi çalışır ve bir Rust kitaplığı oluşturur. Üretilen Rust kütüphane modülü, diğer Rust modülleri tarafından bağımlılık olarak kullanılabilir. Üretilen kitaplığın bir bağımlılık olarak kullanılmasına bir örnek olarak, bir rust_library , external/rust/binder_example/Android.bp içinde aşağıdaki gibi tanımlanabilir:

rust_library {
    name: "libmyservice",
    srcs: ["src/lib.rs"],
    crate_name: "myservice",
    rustlibs: [
        "com.example.android.remoteservice-rust",
        "libbinder_rs",
    ],
}

Rustlibs'de kullanılan rustlibs tarafından oluşturulan kitaplık için modül adı biçiminin, aidl_interface modül adının ardından -rust ; bu durumda, com.example.android.remoteservice-rust .

AIDL arayüzüne daha sonra src/lib.rs içinde aşağıdaki gibi başvurulabilir:

// 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(())
    }
}

Son olarak, hizmeti aşağıda gösterildiği gibi bir Rust ikili dosyası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()
}

C'den Rust'ı çağırmak

Bu örnek, Rust'ın C'den nasıl aranacağını gösterir.

Örnek Rust Kitaplığı

libsimple_printer dosyasını external/rust/simple_printer/libsimple_printer.rs içinde aşağıdaki gibi tanımlayın:

//! 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 çekebileceği başlıkları tanımlamalıdır, bu nedenle external/rust/simple_printer/simple_printer.h başlığını aşağıdaki gibi tanımlayın:

#ifndef SIMPLE_PRINTER_H
#define SIMPLE_PRINTER_H

void print_c_hello_rust();


#endif

Burada gördüğünüz gibi external/rust/simple_printer/Android.bp tanımlayın:

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: ["."],
}

Örnek C ikili dosyası

external/rust/c_hello_rust/main.c aşağıdaki gibi tanımlayın:

#include "simple_printer.h"

int main() {
  print_c_hello_rust();
  return 0;
}

external/rust/c_hello_rust/Android.bp aşağıdaki gibi tanımlayın:

cc_binary {
    name: "c_hello_rust",
    srcs: ["main.c"],
    shared_libs: ["libsimple_c_printer"],
}

Son olarak, m c_hello_rust çağırarak oluşturun.

Rust-Java birlikte çalışması

jni kasası, Java Native Interface (JNI) aracılığıyla Rust ile Java ile birlikte çalışabilirlik sağlar. Rust'ın doğrudan Java'nın JNI'sine ( JNIEnv , JClass , JString vb.) bağlanan bir Rust cdylib kitaplığı oluşturması için gerekli tür tanımlarını tanımlar. cxx aracılığıyla cxx gerçekleştiren C++ bağlamalarından farklı olarak, JNI aracılığıyla Java birlikte çalışabilirliği, derleme sırasında bir kod oluşturma adımı gerektirmez. Bu nedenle özel yapı sistemi desteğine ihtiyaç duymaz. Java kodu, Rust tarafından sağlanan cdylib diğer yerel kitaplıklar gibi yükler.

kullanım

Hem Rust hem de Java kodundaki kullanım, jni sandık belgelerinde ele alınmıştır. Lütfen orada verilen Başlarken örneğini takip edin. src/lib.rs sonra, kitaplığın Android'in derleme sistemiyle nasıl oluşturulacağını öğrenmek için bu sayfaya dönün.

yapı tanımı

Java, dinamik olarak yüklenebilmesi için Rust kitaplığının cdylib olarak sağlanmasını gerektirir. Soong'daki Rust kitaplığı tanımı şu şekildedir:

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ı required bir bağımlılık olarak listeler; bu, derleme zamanı bağımlılığı olmasa bile, Java kitaplığının yanında cihaza yüklenmesini sağlar:

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

Alternatif olarak, Rust kitaplığını bir AndroidManifest.xml dosyasına eklemeniz gerekiyorsa, kitaplığı uses_libs aşağıdaki gibi ekleyin:

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

CXX Kullanarak Rust–C++ Birlikte Çalışma

CXX kasası, Rust ve bir C++ alt kümesi arasında güvenli FFI sağlar. CXX belgeleri , genel olarak nasıl çalıştığına dair iyi örnekler verir. Aşağıdaki örnek, Android'de nasıl kullanılacağını gösterir.

CXX'in Rust'ın çağırdığı C++ kodunu oluşturmasını sağlamak için, genrule çağıracak bir tür ve bunu bir kitaplıkta paketleyecek bir cc_library_static tanımlayın. C++'ın Rust kodunu çağırmasını veya C++ ile Rust arasında paylaşılan türleri kullanmayı planlıyorsanız, ikinci bir tür tanımlayın (Rust 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"],
}

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"],
}

Ardından bunu bir Rust kitaplığına veya yürütülebilir dosyaya bağlayın:

rust_binary {
    name: "cxx_test",
    srcs: ["lib.rs"],
    rustlibs: ["libcxx"],
    static_libs: ["libcxx_test_cpp"],
}

.cpp ve .hpp dosyalarında, CXX sarmalayıcı türlerini istediğiniz gibi kullanarak C++ işlevlerini istediğiniz gibi tanımlayın. Örneğin, bir cxx_test.hpp tanımı şunları içerir:

#pragma once

#include "rust/cxx.h"

int greet(rust::Str greetee);

cxx_test.cpp 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 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;
}