অ্যান্ড্রয়েড মরিচা প্যাটার্নস

এই পৃষ্ঠাটিতে অ্যান্ড্রয়েড লগিং সম্পর্কে তথ্য রয়েছে, একটি রাস্ট এআইডিএল উদাহরণ প্রদান করে, কীভাবে সি থেকে রাস্টকে কল করতে হয় তা আপনাকে বলে এবং CXX ব্যবহার করে Rust/C++ ইন্টারপ-এর জন্য নির্দেশাবলী প্রদান করে।

অ্যান্ড্রয়েড লগিং

নিম্নলিখিত উদাহরণ দেখায় কিভাবে আপনি 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 পদ্ধতিটি একবার কল করুন (প্রয়োজনে আপনি এটিকে একাধিকবার কল করতে পারেন), এবং প্রদত্ত ম্যাক্রো ব্যবহার করে বার্তাগুলি লগ করুন। সম্ভাব্য কনফিগারেশন বিকল্পগুলির একটি তালিকার জন্য লগার ক্রেট দেখুন।

লগার ক্রেট আপনি কি লগ করতে চান তা সংজ্ঞায়িত করার জন্য একটি API প্রদান করে। কোডটি ডিভাইসে চলছে নাকি অন-হোস্ট (যেমন একটি হোস্ট-সাইড পরীক্ষার অংশ) এর উপর নির্ভর করে বার্তাগুলি android_logger বা env_logger ব্যবহার করে লগ করা হয়।

মরিচা এআইডিএল উদাহরণ

এই বিভাগটি মরিচা সহ AIDL ব্যবহারের একটি হ্যালো ওয়ার্ল্ড-স্টাইলের উদাহরণ প্রদান করে।

অ্যান্ড্রয়েড ডেভেলপার গাইড AIDL ওভারভিউ বিভাগটিকে শুরুর পয়েন্ট হিসেবে ব্যবহার করে, 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,
        },
    },
}

AIDL ব্যাকএন্ড হল একটি রাস্ট সোর্স জেনারেটর, তাই এটি অন্যান্য রাস্ট সোর্স জেনারেটরের মতো কাজ করে এবং একটি মরিচা লাইব্রেরি তৈরি করে। উত্পাদিত মরিচা লাইব্রেরি মডিউল অন্যান্য মরিচা মডিউল দ্বারা নির্ভরতা হিসাবে ব্যবহার করা যেতে পারে। একটি নির্ভরতা হিসাবে উত্পাদিত লাইব্রেরি ব্যবহার করার একটি উদাহরণ হিসাবে, একটি 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-এ ব্যবহৃত 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(())
    }
}

অবশেষে, নীচে দেখানো হিসাবে একটি মরিচা বাইনারিতে পরিষেবাটি শুরু করুন:

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

সি থেকে মরিচা কল করা হচ্ছে

এই উদাহরণটি দেখায় কিভাবে সি থেকে মরিচা কল করতে হয়।

উদাহরণ মরিচা লাইব্রেরি

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 ক্রেট জাভা নেটিভ ইন্টারফেস (জেএনআই) এর মাধ্যমে জাভার সাথে মরিচা ইন্টারঅপারেবিলিটি প্রদান করে। এটি জাভা-এর JNI ( JNIEnv , JClass , JString , এবং তাই) তে সরাসরি প্লাগ করে একটি Rust cdylib লাইব্রেরি তৈরি করতে Rust-এর প্রয়োজনীয় টাইপ সংজ্ঞা নির্ধারণ করে। C++ বাইন্ডিংয়ের বিপরীতে যা cxx এর মাধ্যমে cxx সম্পাদন করে, JNI-এর মাধ্যমে জাভা ইন্টারঅপারেবিলিটির জন্য একটি বিল্ডের সময় কোড-জেনারেশন পদক্ষেপের প্রয়োজন হয় না। তাই এটির বিশেষ বিল্ড-সিস্টেম সমর্থনের প্রয়োজন নেই। জাভা কোড অন্যান্য নেটিভ লাইব্রেরির মতোই জং-প্রদত্ত cdylib লোড করে।

ব্যবহার

মরিচা এবং জাভা কোড উভয় ক্ষেত্রেই ব্যবহার jni ক্রেট ডকুমেন্টেশনে কভার করা হয়েছে। অনুগ্রহ করে সেখানে দেওয়া শুরু করার উদাহরণ অনুসরণ করুন। আপনি src/lib.rs লেখার পরে, Android এর বিল্ড সিস্টেমের সাথে লাইব্রেরি কীভাবে তৈরি করবেন তা শিখতে এই পৃষ্ঠায় ফিরে যান।

সংজ্ঞা তৈরি করুন

জাভা-এর জন্য জং লাইব্রেরি একটি 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++ ইন্টারপ

CXX ক্রেট মরিচা এবং C++ এর একটি উপসেটের মধ্যে নিরাপদ FFI প্রদান করে। CXX ডকুমেন্টেশন সাধারণভাবে কিভাবে কাজ করে তার ভালো উদাহরণ দেয়। নিম্নলিখিত উদাহরণটি Android এ এটি কীভাবে ব্যবহার করবেন তা দেখায়।

সিএক্সএক্স সি++ কোড জেনারেট করতে যা রাস্ট কল করে, একটি genrule সংজ্ঞায়িত করুন যাতে সিএক্সএক্স চালু করা যায় এবং একটি লাইব্রেরিতে বান্ডেল করার জন্য একটি cc_library_static । আপনি যদি C++ কল রাস্ট কোডের পরিকল্পনা করেন, অথবা C++ এবং Rust এর মধ্যে ভাগ করা প্রকারগুলি ব্যবহার করেন, তাহলে একটি দ্বিতীয় জেনরুল সংজ্ঞায়িত করুন (Rust bindings ধারণকারী 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_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();
}

মরিচা থেকে এটি ব্যবহার করতে, 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;
}