HIDL Java

در Android 8.0، سیستم‌عامل Android برای تعریف رابط‌های واضح بین پلاتفرم Android مستقل از دستگاه و کد مختص دستگاه و فروشنده، معماری مجدد شد. Android قبلاً بسیاری از این واسط‌ها را در قالب واسط‌های HAL تعریف کرده است که به‌عنوان سرصفحه‌های C در hardware/libhardware تعریف شده‌اند. HIDL این میاناهای HAL را با میاناهای پایدار و نسخه‌دار جایگزین کرد که می‌توانند به زبان Java (در زیر شرح داده شده است) یا میاناهای HIDL سمت کارخواه و سرور در C++ باشند.

هدف از استفاده از میاناهای HIDL در درجه اول کد بومی است و در نتیجه HIDL بر تولید خودکار کد کارآمد در C++‎ تمرکز دارد. بااین‌حال، میاناهای HIDL باید برای استفاده مستقیم از جاوا نیز دردسترس باشند، زیرا برخی‌از زیرسیستم‌های Android (مثل «خدمات تلفن») میاناهای HIDL جاوا دارند.

صفحه‌های این بخش نمای جلویی Java برای میاناهای HIDL را شرح می‌دهد، جزئیات نحوه ایجاد، ثبت، و استفاده از سرویس‌ها را توضیح می‌دهد، و نحوه تعامل HALها و کاربران HAL نوشته‌شده در Java با سیستم HIDL RPC را توضیح می‌دهد.

نمونه کارخواه

این نمونه‌ای از کارخواه برای میانای IFoo در بسته android.hardware.foo@1.0 است که به‌عنوان نام سرویس default و سرویس اضافی با نام سرویس سفارشی second_impl ثبت شده است.

افزودن کتابخانه

اگر می‌خواهید از آن استفاده کنید، باید وابستگی‌هایی را به کتابخانه چوب‌بست HIDL مربوطه اضافه کنید. معمولاً این یک کتابخانه ایستا است:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

اگر می‌دانید که ازقبل وابستگی‌های این کتابخانه‌ها را اضافه کرده‌اید، می‌توانید از پیوند مشترک نیز استفاده کنید:

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

ملاحظات اضافی برای افزودن کتابخانه‌ها در Android 10

اگر برنامه سیستم یا فروشنده‌ای دارید که Android 10 یا نسخه‌های بالاتر را هدف‌یابی می‌کند، می‌توانید این کتابخانه‌ها را به‌صورت ایستا اضافه کنید. همچنین می‌توانید (فقط) از کلاس‌های HIDL از JARهای سفارشی نصب‌شده در دستگاه با Java APIهای پایدار که بااستفاده از سازوکار uses-library موجود برای برنامه‌های سیستم دردسترس قرار گرفته‌اند استفاده کنید. رویکرد دوم باعث صرفه‌جویی در فضای دستگاه می‌شود. برای جزئیات بیشتر، پیاده‌سازی کتابخانه کیت توسعه نرم‌افزار Java را ببینید. برای برنامه‌های قدیمی‌تر، رفتار قدیمی حفظ می‌شود.

از Android 10، نسخه‌های «سطحی» این کتابخانه‌ها نیز دردسترس است. این موارد شامل کلاس موردنظر می‌شود اما شامل هیچ‌یک از کلاس‌های وابسته نمی‌شود. برای مثال، android.hardware.foo-V1.0-java-shallow شامل کلاس‌های بسته foo می‌شود، اما شامل کلاس‌های android.hidl.base-V1.0-java نمی‌شود، که حاوی کلاس پایه همه میاناهای HIDL است. اگر درحال ایجاد کتابخانه‌ای هستید که ازقبل کلاس‌های پایه میانای ترجیحی را به‌عنوان وابستگی دردسترس دارد، می‌توانید از موارد زیر استفاده کنید:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

کتابخانه‌های پایه و مدیر HIDL نیز دیگر در bootclasspath برای برنامه‌ها دردسترس نیستند (قبلاً، گاهی اوقات به‌دلیل delegate-first classloader در Android به‌عنوان میانای برنامه‌سازی کاربردی پنهان استفاده می‌شدند). درعوض، آن‌ها به فضای نام جدیدی با jarjar منتقل شده‌اند و برنامه‌هایی که از این‌ها استفاده می‌کنند (برنامه‌های لزوماً خصوصی) باید نسخه‌های جداگانه خودشان را داشته باشند. واحدهای موجود در مسیر کلاس راه‌اندازی که از HIDL استفاده می‌کنند باید از گونه‌های سطحی این کتابخانه‌های Java استفاده کنند و jarjar_rules: ":framework-jarjar-rules" را به Android.bp خود اضافه کنند تا از نسخه این کتابخانه‌ها که در مسیر کلاس راه‌اندازی وجود دارد استفاده کنند.

اصلاح کردن منبع Java

فقط یک نسخه (@1.0) از این سرویس وجود دارد، بنابراین این کد فقط آن نسخه را بازیابی می‌کند. برای نحوه مدیریت نسخه‌های مختلف سرویس، افزونه‌های میانای را ببینید.

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

ارائه سرویس

کد چارچوب در Java ممکن است نیاز داشته باشد تا میاناهایی را برای دریافت بازخوان‌های ناهم‌زمان از HAL ارائه دهد.

برای میانای IFooCallback در نسخه ۱.۰ بسته android.hardware.foo، می‌توانید میانای خود را بااستفاده از مراحل زیر در جاوا پیاده‌سازی کنید:

  1. میانای خود را در HIDL تعریف کنید.
  2. /tmp/android/hardware/foo/IFooCallback.java را به‌عنوان مرجع باز کنید.
  3. برای پیاده‌سازی Java خود، واحد جدیدی ایجاد کنید.
  4. کلاس انتزاعی android.hardware.foo.V1_0.IFooCallback.Stub را بررسی کن، سپس کلاس جدیدی بنویس تا آن را گسترش دهی و روش‌های انتزاعی را پیاده‌سازی کنی.

دیدن فایل‌های تولیدشده به‌طور خودکار

برای مشاهده فایل‌های تولیدشده به‌طور خودکار، این را اجرا کنید:

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

این فرمان‌ها دایرکتوری /tmp/android/hardware/foo/1.0 را تولید می‌کنند. برای فایل hardware/interfaces/foo/1.0/IFooCallback.hal، این دستور فایل /tmp/android/hardware/foo/1.0/IFooCallback.java را تولید می‌کند که واسط جاوا، کد پراکسی، و کد جای‌بان را دربرمی‌گیرد (هم پراکسی و هم جای‌بان با واسط سازگار هستند).

-Lmakefile قوانینی را تولید می‌کند که این فرمان را در زمان ساخت اجرا می‌کنند و به شما امکان می‌دهند android.hardware.foo-V1.0-java را اضافه کنید و با فایل‌های مناسب پیوند دهید. نوشتاری که این کار را به‌طور خودکار برای پروژه‌ای پر از میان‌ها انجام می‌دهد را می‌توانید در hardware/interfaces/update-makefiles.sh پیدا کنید. مسیرهای این مثال نسبی هستند؛ سخت‌افزار/میان‌ها می‌تواند یک دایرکتوری موقت در درخت کد شما باشد تا به شما امکان دهد قبل‌از انتشار HAL، آن را توسعه دهید.

اجرای سرویس

‫HAL میانای IFoo را ارائه می‌دهد که باید ازطریق میانای IFooCallback، فراخوان‌های برگشتی ناهمگام به چارچوب انجام دهد. واسط IFooCallback براساس نام به‌عنوان سرویس قابل‌کشف ثبت نشده است؛ درعوض، IFoo باید روشی مثل setFooCallback(IFooCallback x) داشته باشد.

برای راه‌اندازی IFooCallback از نسخه ۱٫۰ بسته android.hardware.foo، android.hardware.foo-V1.0-java را به Android.mk اضافه کنید. کد برای اجرای سرویس این است:

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

افزونه‌های میانایی

با فرض اینکه سرویس مفروض میانای IFoo را در همه دستگاه‌ها پیاده‌سازی کند، ممکن است سرویس در دستگاهی خاص قابلیت‌های بیشتری را که در افزونه میانای IBetterFoo پیاده‌سازی شده است به‌صورت زیر ارائه دهد:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

کد فراخوانی که از میانای توسعه‌یافته آگاه است می‌تواند از روش Java castFrom() برای تبدیل ایمن میانای پایه به میانای توسعه‌یافته استفاده کند:

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}