English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

توضيح ميكانيكية اتصال AIDL بين العمليات في Android

AIDL للـ Android، آلية اتصال العمليات في الأندرويد، سأقوم هنا بجمع معلومات حول AIDL لمساعدتكم على فهم هذا الجزء من المعرفة!

ما هو AIDL

AIDL هو اختصار لـ Android Interface Definition Language، أي لغة تعريف واجهات الأندرويد. يبدو هذا الأمر معقدًا، ولكن جوهره هو أداة مساعدة لإنشاء واجهات اتصال بين العمليات. شكل وجوده هو ملف .aidl، وكل ما يجب على المطورين فعله هو تعريف واجهات الاتصال بين العمليات في هذا الملف، وعند التجميع، ينتج IDE ملفات .java القابلة للاستخدام في المشروع، وهذا يشبه ما نسميه "حلاً سحريًا".

لغة جملة AIDL هي نفس لغة جملة Java،مع بعض الاختلافات في عملية التضمين.إذا كانت كلاسيان في نفس المجموعة في Java،فلا يتطلب الأمر عملية تضمين،لكن في AIDL،فمن الضروري إجراء إعلان التضمين.

 تفسير AIDL

تخيل سيناريو:لدينا نظام إدارة الكتب،ويتم تنفيذ هذا النظام عبر نموذج CS.وتم تنفيذ الوظائف الإدارية من قبل عملية الخادم،ويكفي للعميل استدعاء الواجهات المطلوبة.

إذن،أولاً،نحن نقوم بتعريف واجهة ADIL هذا النظام.

نحن نقوم بإنشاء مكتبة aidl في /rc،ويحتوي هذا الملف على ثلاثة ملفات Book.java،Book.aidl،IBookManager.aidl.

package com.example.aidl book
public class Book implements Parcelable {
 int bookId;
 String bookName;
 public Book(int bookId, String bookName) {
   this.bookId = bookId;
   this.bookName = bookName;
 }
 ...
}

package com.example.aidl;

Parcelable Book;

package com.example.aidl;
import com.example.aidl.Book;
inteface IBookManager {
  List<Book> getBookList();
  void addBook(in Book book);
}

سنقوم بالشرح أدناه لهذه الثلاثة ملفات:

Book.java هي الفئة المادية التي قمنا بتعريفها،وتم تحقيقها لـ Parcelable،بالتالي يمكن للفئة Book نقل البيانات بين العمليات.
Book.aidl هي إعلان هذه الفئة المادية في AIDL.
IBookManager هي واجهة الاتصال بين الخادم والعميل.(ملاحظة:في واجهات AIDL،على إضافة الاتجاه قبل المعطيات الأساسية،in تعني المعطيات المدخلة،out تعني المعطيات الصادرة،inout تعني المعطيات المدخلة والمصدرة)

مؤشر التجميع،يقوم Android Studio تلقائيًا بإنشاء ملف .java الخاص بمشروعنا بعد التجميع،ويحتوي هذا الملف على ثلاثة كلاسات،وهي IBookManager،Stub وProxy،وكل هذه الكلاسات من النوع الثابت،ويمكننا تقسيمها بشكل كامل،والتعريف الثلاثة للكلاسات كالتالي:

IBookManager

public interface IBookManager extends android.os.IInterface {
  public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException;
  public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException;
}

Stub

public static abstract class Stub extends android.os.Binder implements net.bingyan.library.IBookManager {
    private static final java.lang.String DESCRIPTOR = "net.bingyan.library.IBookManager";
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    /**
     * Construct the stub at attach it to the interface.
     */
    public Stub() {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an net.bingyan.library.IBookManager interface,
     * generating a proxy if needed. http://www.manongjc.com/article/1501.html
     */
    public static net.bingyan.library.IBookManager asInterface(android.os.IBinder obj) {
      if ((obj == null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && (iin instanceof net.bingyan.library.IBookManager))) {
        return ((net.bingyan.library.IBookManager) iin);
      }
      return new net.bingyan.library.IBookManager.Stub.Proxy(obj);
    }
    @Override
    public android.os.IBinder asBinder() {
      return this;
    }
    @Override
    public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
      switch (code) {
        case INTERFACE_TRANSACTION: {
          reply.writeString(DESCRIPTOR);
          return true;
        }
        case TRANSACTION_addBook: {
          data.enforceInterface(DESCRIPTOR);
          net.bingyan.library.Book _arg0;
          if ((0 != data.readInt())) {
            _arg0 = net.bingyan.library.Book.CREATOR.createFromParcel(data);
          } else {
            _arg0 = null;
          }
          this.addBook(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getBookList: {
          data.enforceInterface(DESCRIPTOR);
          java.util.List<net.bingyan.library.Book> _result = this.getBookList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
      }
      return super.onTransact(code, data, reply, flags);
    }
}

Proxy

private static class Proxy implements net.bingyan.library.IBookManager {
      private android.os.IBinder mRemote;
      Proxy(android.os.IBinder remote) {
        mRemote = remote;
      }
      @Override
      public android.os.IBinder asBinder() {
        return mRemote;
      }
      public java.lang.String getInterfaceDescriptor() {
        return DESCRIPTOR;
      }
      /**
       * يُظهر بعض الأنواع الأساسية التي يمكنك استخدامها كمعلمات
       * وعدد القيم في AIDL.  http://www.manongjc.com/article/1500.html
       */
      @Override
      public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          if ((book != null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0);
          } else {
            _data.writeInt(0);
          }
          mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
          _reply.readException();
        }
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override
      public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<net.bingyan.library.Book> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
          _reply.readException();
          _result = _reply.createTypedArrayList(net.bingyan.library.Book.CREATOR);
        }
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
    }

شرح للثلاثة كلاسات المُنتجة كالتالي:

  1. كلاس IBookManager هو واجهة نحن نحددها، يضيف له android studio فئة آباء، يُمتد إلى واجهة android.os.interface، هذه الواجهة تحتوي على طريقة واحدة هي IBinder asBinder(). بهذا يصبح لدينا ثلاث طرق مُحققة في IBookManager، إنه نافذة للاتصال بين عملية الجانب الخادم والجانب العميل.
  2. Stub هو كلاس مجرد، يُمتد إلى كلاس android.os.Binder ويحقق واجهة IBookManager. في Stub، تم تحقيق طريقة asBinder() للواجهة، بالإضافة إلى طريقتين آخرتين نحن نُعدهما للاستخدام من قبل فرعتهما. يتم استخدامه في الجانب الخادم، لذا يجب على الجانب الخادم تحقيق هاتين الطريقتين.
  3. Proxy كما يُدعى، هو كلاس وسيط، إنه وسيط للجانب الخادم في الجانب العميل، وهو أيضًا يُحقق واجهة IBookManager ويحقق جميع الطرق في IBookManager. يتم استخدامه في الجانب العميل، وهو وسيط للجانب الخادم في الجانب العميل. الآن دعونا نحلل هذه الثلاثة كلاسات بشكل فردي:
  4. لا يوجد شيء يمكن قوله عن كلاس IBookManager، إنه يُمتد فقط إلى واجهة asInterface، والغرض منه هو تحويل IBookManager إلى IBinder.
  5. Proxy هذا الكلاس تم ذكره مسبقًا، إنه كلاس تعبئة لميكانيكية الاتصال بين العمليات، والذي يُستخدم بنية تحتية Binder، ويمكننا رؤية ذلك بسهولة من خلال طريقة بنائه. تأخذ طريقة بنائه متغيرًا من نوع IBinder، يُدعى remote، مما يعني أنه يمثل الجانب الخادم. لنلق نظرة على الطريقة addBook() وgetBookList() في هذا الكلاس:
@Override
public void addBook(net.bingyan.library.Book book) throws android.os.RemoteException {
   android.os.Parcel _data = android.os.Parcel.obtain();
   android.os.Parcel _reply = android.os.Parcel.obtain();
   try {
      _data.writeInterfaceToken(DESCRIPTOR)
      if ((book != null)) {
        _data.writeInt(1);
        book.writeToParcel(_data, 0);
      } else {
        _data.writeInt(0);
      }
      mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
      _reply.readException();
    }
      _reply.recycle();
      _data.recycle();
    }
}
@Override /* http://www.manongjc.com/article/1547.html */
public java.util.List<net.bingyan.library.Book> getBookList() throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.util.List<net.bingyan.library.Book> _result;
    try {
       _data.writeInterfaceToken(DESCRIPTOR);
       mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
       _reply.readException();
       _result = _reply.createTypedArrayList(net.bingyan.library.Book.CREATOR);
    }
      _reply.recycle();
      _data.recycle();
    }
    return _result;
}

هي التي يتم تنفيذها تلقائيًا بواسطة مُعالج البرمجيات، وهذه الطريقتان تحتويان على الكثير من التشابهات، يمكنني أن أُفصح عنها الآن: هاتان الطريقتان هما نافذة إتصال عملية العميل بعملية الخادم. في بداية هاتين الطرق، كلاهما يُحددان كائنات Parcel (الترجمة الصينية: الحزمة). يبدو لنا أن كائن Parcel هذا مألوف، نعم، معامل writeToParcel() في كائن Book وكائن CREATOR في Book هي من نوع Parcel، ويمكن العثور على شرح هذا الكائن في الوثائق كما يلي:

حاوية لرسالة (بيانات ومراجع) يمكن إرسالها عبر IBinder. يمكن أن يحتوي Parcel على بيانات مسطحة سيتم تفريغها عند الجانب الآخر من IPC (باستخدام الأساليب المختلفة هنا لكتابة أنواع محددة أو بواسطة واجهة Parcelable العامة)، بالإضافة إلى مراجع إلى IBinder Objects��ة ستؤدي إلى أن الجانب الآخر يتلقى Proxy IBinder مرتبط بالIBinder الأصلي في الحزمة.

Proxy هو حاوية يمكنها نقل الرسائل (بيانات ومراجع) من خلال IBinder. يمكن أن يحتوي Parcel على بيانات قابلة للتسلسل، سيتم فك التسلسل لهذه البيانات عند الجانب الآخر من IPC (باستخدام مختلف الأساليب هنا للكتابة على أنواع محددة أو بواسطة واجهة Parcelable العامة)، كما يمكن أن يحتوي على مراجع إلى IBinder Objects مما يؤدي إلى أن الجانب الآخر يتلقى Proxy IBinder مرتبط بالIBinder الأصلي في Parcel.

سأستخدم الشكل للشرح بوضوح:

آلية اتصال عملية اندرويد AIDL

كما هو موضح في الشكل، يمكننا رؤية بوضوح كيف يعتمد الخادم على Parcel كحزمة بيانات للتواصل مع العميل باستخدام Binder. الحزمة البيانية هي الهدف من التسلسل بعد التسلسل.

كما ذكرت سابقًا، كلا الطريقتين تعرفان علىParcel Objects إثنين، يُدعى الأول _data والآخر _reply، يمكن القول بوضوح، من وجهة نظر العميل، _data هو الحزمة البيانية التي يرسلها العميل إلى الخادم، _reply هي الحزمة البيانية التي يرسلها الخادم إلى العميل.

بعد ذلك، تبدأ باستخدام هذين العنصرين في التواصل مع الخادم، يمكننا ملاحظة أن كلا الطريقتين يحتويان على هذه الدالة mRemote.transact()، لديها أربعة معامل، معامل الأول سنتحدث عنه لاحقًا، المعامل الثاني _data يتحمل إرسال حزمة البيانات إلى الخادم مثل معاملات واجهة البرمجة، المعامل الثالث _reply يتحمل استقبال حزمة البيانات من الخادم مثل القيم المرجعة للواجهات البرمجية. هذه السطر الواحد من الدالة بسيط، ولكنه جزء من أغرب جزء من اتصال AIDL، حيث يحدث هذا الاستدعاء البعيد للوظيفة (يتم استدعاء الوظيفة المماثلة في Stub من خلال واجهة البرمجة Proxy المحلية)، لذا يمكن التفكير في أنه عملية تتطلب وقتًا طويلاً.

في مثالنا:

1. الحصول على كتاب addBook(Book) يتطلب الاستعانة ب _data لإرسال المعامل Book:book إلى الخادم، والطريقة التي يتم بها هذا هي تضمين Book عبر طريقة writeToParcel(Parcel out) التي يتم تحويلها إلى _data، كما يمكنك التفكير، _data هي في الواقع المعامل out، هل تتذكر كيفية تنفيذ هذه الطريقة في Book؟ نحن نقوم بتجميع جميع حقول Book إلى Parcel.

2. الحصول على قائمة الكتب List<Book> يتطلب الاستعانة ب _reply لاستقبال القيمة المرجعة List<Book>:books من الخادم، والطريقة التي يتم بها هذا هي استخدام الحقل الثابت CREATOR في Book كمعامل لطريقة createTypedArrayList() في _reply، هل تتذكر CREATOR في Book؟ هل كنت تتساءل كيف يجب استخدام هذا الحقل الثابت؟ الآن كل شيء واضح، نحن بحاجة إلى هذا العنصر (لإفساح المجال لفهمه يمكننا تسميته "مفكك التسلسل" لتحويل بيانات الخادم إلى بيانات قابلة للتسلسل أو مجموعة من العناصر. من الواضح أن CREATOR يستخدم _reply لإنشاء List<Book>:books.

بالطبع، لا تقتصر هذه الطريقتان على نقل الأجسام، بل تنقلان أيضًا بعض معلومات التحقق، هذا يمكننا أن لا نبحث فيه، ولكن يجب أن نلاحظ أن ترتيب الت打包 والتجميع يجب أن يكون متطابقًا. على سبيل المثال، إذا كان أول شيء يتم ت打包ه هو int:i، فإن أول شيء يتم تجميعه يجب أن يكون هذا القيمة الصحيحة. أي أن إذا تم استدعاء Parcel.writeInt(int) أولاً عند الت打包، فإن يجب أن يتم استدعاء Parcel.readInt() أولاً عند التجميع.

إلى هنا، تم انتهاء شرح Proxy للعميل، دعونا الآن نرى Stub للخادم.

تم تنفيذ واحدة من طرق IBookManager في Stub، وهو بسيط، وهو إرجاع الذات ببساطة، لأن Stub يرث من Binder، والذي يرث من IBinder، لذا لا يوجد أي مشكلة. ستعلمون: ماذا عن الطريقتين المتبقيتين؟ هاتان الطريقتان هي طرق واجهة التي قمنا بتعريفها، ويجب أن تترك لتكون مكتوبة في عملية الخادم، أي أننا بحاجة إلى تعريف مكتوب لـ Stub في عملية الخادم. سأقوم بتحليل الطريقتين المهمتين في Stub أدناه:

IBookManager asInterface(IBinder obj)

public static net.bingyan.library.IBookManager asInterface(android.os.IBinder obj) {
      if ((obj == null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin != null) && (iin instanceof net.bingyan.library.IBookManager))) {
        return ((net.bingyan.library.IBookManager) iin);
      }
      return new net.bingyan.library.IBookManager.Stub.Proxy(obj);
    }

هذا الطريقة تستخدم لتحويل فئة Stub إلى واجهة IBookManager، يحتوي الطريقة على تحقق: إذا كان عملية الخادم وعمليه العميل نفس العمليه، فإنه يتم تحويل فئة Stub إلى IBookManager من خلال تحويل النوع؛ وإذا لم تكن عملية الخادم وعمليه العميل نفس العمليه، فإنه يتم تحويل Stub إلى IBookManager من خلال فئة الوسيط Proxy. لماذا يتم هذا، نعلم أن إذا لم تكن عملية الخادم وعمليه العميل نفس العمليه، فإنه لا يمكن مشاركة ذاكرة بينهما، ولا يمكن التواصل من خلال الطريقة العادية، ولكن إذا كنا نعمل على تحقيق طريقة تواصل بين العمليات، فإن تكلفة ذلك كبيرة بالنسبة للمطورين العاديين، لذا قام المبرمج المساعد بإنشاء أداة م封装ة لتواصل بين العمليات، وهي Proxy هذه، حيث قامت الفئة هذه بتعقيم ميكانيزم التواصل بين العمليات الأساسية فقط وعرض واجهات الطريقة، حيث يكفي للعميل فقط استدعاء هذه الطريقتين لتحقيق التواصل بين العمليات (وهو في الواقع استدعاء الطريقة عن بعد) دون الحاجة إلى معرفة التفاصيل.

مع هذه الطريقة، يمكننا على العملاء استخدامها لتحويل متغير من نوع IBinder إلى واجهة التي قمنا بتعريفها IBookManager، ونحن سنوضح استخداماتها في الأمثلة التي سنراها لاحقًا.

onTransact(int code, Parcel data, Parcel reply, int flags)

@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
      switch (code) {
        case INTERFACE_TRANSACTION: {
          reply.writeString(DESCRIPTOR);
          return true;
        }
        case TRANSACTION_addBook: {
          data.enforceInterface(DESCRIPTOR);
          net.bingyan.library.Book _arg0;
          if ((0 != data.readInt())) {
            _arg0 = net.bingyan.library.Book.CREATOR.createFromParcel(data);
          } else {
            _arg0 = null;
          }
          this.addBook(_arg0);
          reply.writeNoException();
          return true; /* http://www.manongjc.com/article/1499.html */
        }
        case TRANSACTION_getBookList: {
          data.enforceInterface(DESCRIPTOR);
          java.util.List<net.bingyan.library.Book> _result = this.getBookList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
      }
      return super.onTransact(code, data, reply, flags);
}

هل هذا الطريقة نحن نعرفها أيضًا؟ نرى أيضًا طريقة مشابهة في Proxy وهي transact(int, Parcel, Parcel, int)، والأرجح أن لها نفس المعلمات، بالإضافة إلى أنهم جميعًا طرق في Binder، إذن ما هي علاقتهم؟

كما ذكرنا سابقًا، يقوم transact() بتنفيذ استدعاء مسافي، إذا كان transact() هو بداية استدعاء مسافي، فإن onTransact() هو استجابة استدعاء مسافي. في الواقع، يقوم العملاء ببداية استدعاء عملية مسافية، ثم يتم استجابة هذه الاستدعاء من قبل نظام اندرويد من خلال كود طبقي، ثم يتم استدعاء طريقة onTransact() في الخادم، يتم استخراج معرفات الطريقة من الحزمة، يتم تقديمها إلى الطريقة التي تم تنفيذها في الخادم، ويتم 包装 القيمة العائدة وإرجاعها إلى العملاء.

على الرغم من أن onTransact() يتم في نواة Binder لخدمة الخادم، مما يعني أننا يجب أن نستخدم Handler إذا كنا نريد تحديث واجهة المستخدم في طريقة onTransact().

يعني المعنى الأول لهذه الطريقتين هو رمز طريقة واجهة AIDL، حيث تم تعريف كيانين كتسمية لهذه الطريقتين في Stub:

static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
   static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

إذا كان code == TRANSACTION_addBook، فإن هذا يعني أن العملاء يستدعون addBook()؛ وإذا كان code == TRANSACTION_getBookList، فإن العملاء يستدعون getBookList()، ثم يتم معالجتها من قبل طريقة الخادم المناسبة. يمكن التعبير عن عملية الاتصال بالكامل باستخدام صورة:

آلية اتصال عملية اندرويد AIDL

بعد فهم عملية AIDL بالكامل، فإن الخطوة التالية هي استخدام AIDL في تطبيقات اندرويد.

 استخدام AIDL

أعتقد أن الجميع يجب أن يكونوا على دراية باستخدام Service، حيث يُطلق عليه "خدمة" ويُن�行 في الخلفية، ولكنها تُقام بشكل افتراضي في نواة العملية الافتراضية في نواة العمل الرئيسية. في الواقع، قد تكون استخدام Service في نواة العملية الافتراضية قد يكون استغلالًا غير مناسبًا. العديد من خدمات نظام اندرويد تُقام في نواة عملية منفصلة، تُستخدم من قبل تطبيقات أخرى، مثل خدمة إدارة النوافذ. هذا يوفر ميزة أن يمكن لتطبيقات متعددة مشاركة نفس الخدمة، مما يوفر الموارد ويُسهل إدارة العملاء المختلفين، ولكن يجب الانتباه إلى مشكلة أمان السلسلة.

إذن، في الخطوة التالية، سنقوم بإنشاء نظام إدارة مكتبة بسيط باستخدام AIDL بنية CS.

أولاً، نحدد الخادم:

BookManagerService

public class BookManagerService extends Service {
  private final List<Book> mLibrary = new ArrayList<>();
  private IBookManager mBookManager = new IBookManager.Stub() {
    @Override
    public void addBook(Book book) throws RemoteException {
      synchronized (mLibrary) {
        mLibrary.add(book);
        Log.d("BookManagerService", "الآن مكتبة لدينا تحتوي على " + mLibrary.size() + " كتابًا");
      }
    }
    @Override
    public List<Book> getBookList() throws RemoteException {
      return mLibrary;
    }
  };
  @Override /* http://www.manongjc.com/article/1496.html */
  public IBinder onBind(Intent intent) {
    return mBookManager.asBinder();
  }
}
<service
   android:process=":remote"
   android:name=".BookManagerService"/>

في الخادم، قمنا بتعريف فئة BookManagerService، حيث قمنا بإنشاء عميل الخادم Stub، وتم تنفيذ طريقتين من واجهة AIDL المطلوبة لتحديد استراتيجية إدارة الكتب في الخادم. في دالة onBind()، نحن نرجع عميل IBookManager كـ IBinder. نعرف أن عندما نقوم ب绑定 خدمة، يتم استدعاء دالة onBinder() من قبل النظام للحصول على عميل IBinder في الخادم، ثم يتم تحويله إلى عميل IBinder في العميل، على الرغم من أن عميل IBinder في الخادم وعميل IBinder في العميل هما عميلين IBinder، إلا أنهم في الأسفل هم نفس العميلة. نحن نعين اسم العملية في xml عند تسجيل الخدمة، بحيث يمكن للخدمة تشغيل نفسها في عملية منفردة.

يبدأ الآن في النظر إلى تنفيذ العميل:

Client

public class Client extends AppCompatActivity {
  private TextView textView;
  private IBookManager bookManager;
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.library_book_manager_system_client);
    Intent i = new Intent(Client.this, BookManagerService.class);
    bindService(i, conn, BIND_AUTO_CREATE);
    Button addABook = (Button) findViewById(R.id.button);
    addABook.setOnClickListener(v -> {
      if (bookManager == null) return;
      try {
        bookManager.addBook(new Book(0, "book"));
        textView.setText(getString(R.string.book_management_system_book_count, String.valueOf(bookManager.getBookList().size())));
      } catch (RemoteException e) {
        e.printStackTrace();
      }
    });
    textView = (TextView) findViewById(R.id.textView);
  }
  private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
      Log.d("Client -->", service.toString());
      bookManager = IBookManager.Stub.asInterface(service);
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
      Log.d("Client", name.toString());
    }
  };
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical" android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:weightSum="1"
  android:gravity="center">
  <Button
    android:text="http://www.manongjc.com/article/1495.html"
    android:layout_width="111dp"
    android:layout_height="wrap_content"
    android:id="@+id/button" />
  <TextView
    android:layout_marginTop="10dp"
    android:text="@string/book_management_system_book_count"
    android:layout_width="231dp"
    android:gravity="center"
    android:layout_height="wrap_content"
    android:id="@+id/textView" />
</LinearLayout>

العميل هو Activity، يتم في onCreate() ربط الخدمة، يحتوي bindService() على معامل ServiceConnection:conn، لأن ربط الخدمة عملية غير متزامنة، فإن دور هذا المعامل هو إعادة توجيه الاتصال بعد ربط الخدمة بنجاح، ويحتوي على طريقتين إعادة توجيه: طريقة إعادة توجيه عند ربط الخدمة بنجاح، وأخرى عند قطع الاتصال بخدمة الخادم. ما نحن مهتمين به الآن هو طريقة onServiceConnected()، حيث نقوم فقط بتحويل العنصر IBinder الذي يأتي من الخادم إلى واجهة AIDL، نحدد حقل IBookManager:bookManager لتحافظ على الإشارة إليه. بهذا الشكل، يمكننا استخدام bookManager للقيام بطلبات دالة عن بُعد. نسجل حدثًا للزر في العميل: كل ضغط زر يضيف كتابًا جديدًا إلى الخادم ويظهر عدد الكتب الموجودة في المكتبة.

دعونا الآن نرى تأثير تشغيل البرنامج:

عند كل ضغط زر، نضيف كتابًا جديدًا إلى الخادم بنجاح، مما يعني أننا نجحنا في التواصل عبر AIDL بين العمليات.

شكرًا على القراءة، آمل أن يساعدكم هذا، شكرًا لدعمكم لهذا الموقع!

بيان: محتوى هذا المقال تم جمعه من الإنترنت، ويتمتع المالك الأصلي بحقوق الطبع والنشر، والمحتوى تم إضافته من قبل مستخدمي الإنترنت بشكل تلقائي، ويتمتع هذا الموقع بعدم امتلاك حقوق الملكية، ولا يتم تعديل المحتوى بشكل يدوي، ولا يتحمل هذا الموقع أي مسؤولية قانونية. إذا اكتشفتم محتوى يشتبه في انتهاك حقوق النسخ، فلا تترددوا في إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (عند إرسال البريد الإلكتروني، يرجى استبدال '#' ب '@') لإبلاغنا، وقدموا الأدلة ذات الصلة، وسنقوم بإزالة المحتوى المزعوم بشكل فوري إذا تم التحقق من صحة الشكوى.

سيحبك