एंड्रॉइड जंग पैटर्न

इस पृष्ठ में एंड्रॉइड लॉगिंग के बारे में जानकारी है, एक रस्ट एआईडीएल उदाहरण प्रदान करता है, आपको बताता है कि सी से रस्ट को कैसे कॉल करें , और सीएक्सएक्स का उपयोग करके रस्ट/सी++ इंटरऑप के लिए निर्देश प्रदान करता है।

एंड्रॉइड लॉगिंग

निम्नलिखित उदाहरण दिखाता है कि आप संदेशों को logcat (ऑन-डिवाइस) या stdout (ऑन-होस्ट) पर कैसे लॉग कर सकते हैं।

अपने Android.bp मॉड्यूल में, निर्भरता के रूप में liblogger और liblog_rust जोड़ें:

rust_binary {
    name: "logging_test",
    srcs: ["src/main.rs"],
    rustlibs: [
        "liblogger",
        "liblog_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 का उपयोग करके लॉग किया जाता है।

जंग एआईडीएल उदाहरण

यह अनुभाग रस्ट के साथ एआईडीएल का उपयोग करने का हैलो वर्ल्ड-शैली का उदाहरण प्रदान करता है।

एंड्रॉइड डेवलपर गाइड एआईडीएल अवलोकन अनुभाग को शुरुआती बिंदु के रूप में उपयोग करते हुए, IRemoteService.aidl फ़ाइल में निम्नलिखित सामग्री के साथ external/rust/binder_example/aidl/com/example/android/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 मॉड्यूल को परिभाषित करें। आपको रस्ट बैकएंड को स्पष्ट रूप से सक्षम करना होगा क्योंकि यह डिफ़ॉल्ट रूप से सक्षम नहीं है।

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

एआईडीएल बैकएंड एक रस्ट सोर्स जनरेटर है, इसलिए यह अन्य रस्ट सोर्स जनरेटर की तरह काम करता है और एक रस्ट लाइब्रेरी तैयार करता है। उत्पादित रस्ट लाइब्रेरी मॉड्यूल का उपयोग अन्य रस्ट मॉड्यूल द्वारा निर्भरता के रूप में किया जा सकता है। उत्पादित लाइब्रेरी को निर्भरता के रूप में उपयोग करने के उदाहरण के रूप में, एक 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",
    ],
}

ध्यान दें कि 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(())
    }
}

अंत में, रस्ट बाइनरी में सेवा शुरू करें जैसा कि नीचे दिखाया गया है:

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

एसिंक रस्ट एआईडीएल उदाहरण

यह अनुभाग एसिंक रस्ट के साथ एआईडीएल का उपयोग करने का हैलो वर्ल्ड-शैली का उदाहरण प्रदान करता है।

RemoteService उदाहरण को जारी रखते हुए, जेनरेट की गई एआईडीएल बैकएंड लाइब्रेरी में एसिंक इंटरफेस शामिल हैं जिनका उपयोग एआईडीएल RemoteService के लिए एसिंक सर्वर कार्यान्वयन को लागू करने के लिए किया जा सकता है।

उत्पन्न async सर्वर इंटरफ़ेस 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();
    });
}

ध्यान दें कि async संदर्भ को छोड़ने के लिए ब्लॉक_इन_प्लेस की आवश्यकता होती है जो join_thread_pool आंतरिक रूप से ब्लॉक_ऑन का उपयोग करने की अनुमति देता है। ऐसा इसलिए है क्योंकि #[tokio::main] कोड को block_on पर कॉल में लपेटता है, और आने वाले लेनदेन को संभालते समय join_thread_pool block_on कॉल कर सकता है। block_on के भीतर से block_on कॉल करने से घबराहट होती है। #[tokio::main] का उपयोग करने के बजाय मैन्युअल रूप से टोकियो रनटाइम बनाकर इससे बचा जा सकता है और फिर block_on विधि के बाहर join_thread_pool कॉल करें।

इसके अलावा, रस्ट बैकएंड जेनरेटेड लाइब्रेरी में एक इंटरफ़ेस शामिल है जो RemoteService के लिए एक एसिंक क्लाइंट IRemoteServiceAsync को लागू करने की अनुमति देता है जिसे निम्नानुसार लागू किया जा सकता है:

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::get_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 से रस्ट को कॉल करना

यह उदाहरण दिखाता है कि 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!");
}

रस्ट लाइब्रेरी को उन हेडर को परिभाषित करना होगा जिन्हें आश्रित सी मॉड्यूल खींच सकते हैं, इसलिए 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: ["."],
}

उदाहरण सी बाइनरी

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 क्रेट जावा नेटिव इंटरफेस (जेएनआई) के माध्यम से जावा के साथ रस्ट इंटरऑपरेबिलिटी प्रदान करता है। यह रस्ट के लिए रस्ट cdylib लाइब्रेरी बनाने के लिए आवश्यक प्रकार की परिभाषाओं को परिभाषित करता है जो सीधे जावा के JNI ( JNIEnv , JClass , JString , इत्यादि) में प्लग हो जाता है। C++ बाइंडिंग के विपरीत, जो cxx के माध्यम से कोडजन निष्पादित करता है, JNI के माध्यम से जावा इंटरऑपरेबिलिटी को निर्माण के दौरान कोड-जनरेशन चरण की आवश्यकता नहीं होती है। इसलिए इसे विशेष बिल्ड-सिस्टम समर्थन की आवश्यकता नहीं है। जावा कोड किसी अन्य मूल लाइब्रेरी की तरह रस्ट-प्रदत्त cdylib लोड करता है।

प्रयोग

रस्ट और जावा कोड दोनों का उपयोग jni क्रेट दस्तावेज़ में शामिल है। कृपया वहां दिए गए प्रारंभ करने के उदाहरण का अनुसरण करें। src/lib.rs लिखने के बाद, एंड्रॉइड के बिल्ड सिस्टम के साथ लाइब्रेरी बनाने का तरीका जानने के लिए इस पृष्ठ पर वापस लौटें।

परिभाषा बनाएं

जावा को रस्ट लाइब्रेरी को cdylib के रूप में प्रदान करने की आवश्यकता है ताकि इसे गतिशील रूप से लोड किया जा सके। सूंग में रस्ट लाइब्रेरी की परिभाषा निम्नलिखित है:

rust_ffi_shared {
    name: "libhello_jni",
    crate_name: "hello_jni",
    srcs: ["src/lib.rs"],

    // The jni crate is required
    rustlibs: ["libjni"],
}

जावा लाइब्रेरी रस्ट लाइब्रेरी को एक required निर्भरता के रूप में सूचीबद्ध करती है; यह सुनिश्चित करता है कि यह जावा लाइब्रेरी के साथ डिवाइस पर इंस्टॉल है, भले ही यह बिल्ड-टाइम निर्भरता नहीं है:

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

वैकल्पिक रूप से, यदि आपको AndroidManifest.xml फ़ाइल में रस्ट लाइब्रेरी को शामिल करना है, तो लाइब्रेरी को इस प्रकार uses_libs में जोड़ें:

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

रस्ट-सी++ इंटरऑप सीएक्सएक्स का उपयोग कर रहा है

CXX क्रेट रस्ट और C++ के सबसेट के बीच सुरक्षित FFI प्रदान करता है। CXX दस्तावेज़ीकरण अच्छे उदाहरण देता है कि यह सामान्य रूप से कैसे काम करता है और हमारा सुझाव है कि लाइब्रेरी से परिचित होने के लिए पहले इसे पढ़ें और यह C++ और रस्ट को कैसे जोड़ता है। निम्नलिखित उदाहरण दिखाता है कि एंड्रॉइड में इसका उपयोग कैसे करें।

CXX को C++ कोड जनरेट करने के लिए जिसे रस्ट कॉल करता है, CXX को लागू करने के लिए एक genrule परिभाषित करें और उसे लाइब्रेरी में बंडल करने के लिए एक cc_library_static परिभाषित करें। यदि आप C++ कॉल रस्ट कोड की योजना बना रहे हैं, या C++ और रस्ट के बीच साझा किए गए प्रकारों का उपयोग करते हैं, तो एक दूसरा जेनरूल परिभाषित करें (रस्ट बाइंडिंग वाले 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"],
}

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

ब्रिज के C++ साइड को जेनरेट करने के लिए ऊपर cxxbridge टूल का उपयोग किया गया है। libcxx_test_cpp स्थिर लाइब्रेरी का उपयोग हमारे रस्ट निष्पादन योग्य के लिए निर्भरता के रूप में किया जाता है:

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

.cpp और .hpp फ़ाइलों में, इच्छानुसार CXX रैपर प्रकारों का उपयोग करके C++ फ़ंक्शंस को अपनी इच्छानुसार परिभाषित करें। उदाहरण के लिए, 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();
}

रस्ट से इसका उपयोग करने के लिए, नीचे lib.rs में CXX ब्रिज को परिभाषित करें:

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