סקירה כללית של שירותים קשורים

שירות קשור הוא השרת בממשק לקוח-שרת. הוא מאפשר לרכיבים למשל פעילויות שמקושרות לשירות, שליחת בקשות, קבלת תשובות תקשורת בין תהליכים (IPC). שירות קשור בדרך כלל חי רק בזמן שהוא משרת שירות אחר של האפליקציה ולא פועל ברקע ללא הגבלת זמן.

במסמך הזה מוסבר איך ליצור שירות קשור, כולל הסבר על הקישור לשירות מרכיבי אפליקציה אחרים. למידע נוסף על שירותים ב: כללי, כמו איך לשלוח התראות משירות ולהגדיר את השירות לפעול. בחזית, קוראים את סקירה כללית של השירותים.

העקרונות הבסיסיים

שירות קשור הוא הטמעה של המחלקה Service שמאפשרת אפליקציות אחרות מקושרות אליו ומקיימות איתו אינטראקציה. כדי לספק קישור עבור שירות, מטמיעים את שיטת הקריאה החוזרת onBind(). הזה מחזירה אובייקט IBinder שמגדיר את ממשק התכנות, של לקוחות יכולים לקיים אינטראקציה עם השירות.

קישור לשירות שהופעל

כמו שצוין בקטע סקירה כללית של השירותים, אפשר ליצור שירות גם מתחיל וגם מוגבל. כלומר, אפשר להתחיל באמצעות שירות startService(), שמאפשרת השירות יפעל ללא הגבלת זמן. אפשר גם לאפשר ללקוח לקשר לשירות באמצעות מתבצעת התקשרות אל bindService().

אם תאפשרו לשירות להתחיל ולא לחייב, אז כשהשירות יתחיל, המערכת לא משמידת את השירות כשכל הלקוחות מבטלים את הקישור. במקום זאת, להפסיק במפורש את השירות על ידי קריאה ל-stopSelf() או ל-stopService().

למרות שבדרך כלל מטמיעים את onBind() או onStartCommand(), לפעמים נחוץ להטמיע את שניהם. לדוגמה, ייתכן שכדאי להפעיל את השירות שלו בנגן מוזיקה ללא הגבלת זמן, וגם מספקות קישור. כך, פעילות יכולה להפעיל את השירות כדי להפעיל והמוזיקה תמשיך להתנגן גם אם המשתמש עוזב את האפליקציה. לאחר מכן, כשהמשתמש חוזרת לאפליקציה, הפעילות יכולה להיות מקושרת לשירות כדי לקבל שוב שליטה להפעלה.

כדי לקבל מידע נוסף על מחזור החיים של השירות כשמוסיפים קישור לשירות שהופעל, עיינו בקטע ניהול מחזור החיים של שירות קשור.

הלקוח מתחבר לשירות באמצעות התקשרות bindService() כשהיא פועלת, היא חייבת לספק יישום של ServiceConnection, עוקב אחר החיבור לשירות. הערך המוחזר של bindService() מציין אם קיים שירות מבוקש ואם הלקוח מורשה גישה אליו.

מתי שמערכת Android יוצרת את החיבור בין הלקוח לשירות, מתקשר אל onServiceConnected() ב-ServiceConnection. ה-method onServiceConnected() כוללת IBinder ארגומנט, שבו הלקוח משתמש כדי לתקשר עם השירות המקושר.

אפשר לחבר מספר לקוחות לשירות בו-זמנית. אבל, המערכת שומרת במטמון את ערוץ התקשורת של השירות IBinder. במילים אחרות, המערכת קוראת ל-onBind() של השירות כדי ליצור את ה-IBinder רק כאשר קישורי לקוח. לאחר מכן המערכת מספקת את אותו IBinder אל את כל הלקוחות הנוספים שקשורים לאותו שירות, בלי לקרוא onBind() שוב.

כשהלקוח האחרון מבטל את הקישור לשירות, המערכת משמידת את השירות, אלא אם השירות התחיל להשתמש ב-startService().

החלק החשוב ביותר בהטמעת השירות הכבול הוא הגדרת הממשק ששיטת הקריאה החוזרת (callback) של onBind() מחזירה. הבאים נפרט כמה דרכים שבאמצעותן תוכלו להגדיר את רמת הגישה של השירות ממשק IBinder.

יצירת שירות קשור

כשיוצרים שירות שמספק קישור, צריך לספק IBinder שמספק את ממשק התכנות שהלקוחות יכולים להשתמש בו לאינטראקציה עם השירות. יש מפורטות שלוש דרכים שבהן אפשר להגדיר את הממשק:

להגדיל את המחלקה של Binder
אם השירות שלך פרטי לאפליקציה שלך ופועל באותו תהליך בתור הלקוח, בדרך כלל, יוצרים את הממשק על ידי הרחבה של Binder כיתה והחזרת מופע שלו onBind(). הלקוח מקבל את Binder וגם יכול להשתמש בו כדי לגשת ישירות לשיטות ציבוריות שזמינות דרך Binder או Service.

זו השיטה המועדפת אם השירות הוא רק שירות שפועל ברקע בשבילכם תרגום מכונה. התרחיש לדוגמה היחיד כאשר זו לא הדרך המועדפת ליצירת הממשק שלכם הוא אם אפליקציות אחרות או תהליכים נפרדים משתמשים בשירות שלכם.

שימוש ב-Messenger
אם אתם צריכים שהממשק שלכם יפעל בתהליכים שונים, אתם יכולים ליצור ממשק של השירות עם Messenger. באופן הזה, השירות מגדירה Handler שמגיב לסוגים שונים של אובייקטים מסוג Message.

הHandler הזה הוא הבסיס ל-Messenger שיכול לשתף לאחר מכן IBinder עם הלקוח, מאפשרת ללקוח לשלוח לשירות פקודות באמצעות אובייקטים של Message. בנוסף, הלקוח יכול להגדיר Messenger של שלו, כדי שהשירות יוכל לשלוח הודעות בחזרה.

זו הדרך הפשוטה ביותר לבצע תקשורת בין תהליכים (IPC), כי ה-Messenger מעביר את כל הבקשות לשרשור אחד כך שלא יהיה צורך לעצב את השירות שלכם, כך שהוא יהיה בטוח לשרשורים.

שימוש ב-AIDL
שפת ההגדרה של ממשק Android (AIDL) פורקת אובייקטים ל פרימיטיביים שמערכת ההפעלה יכולה להבין ולשלב אותם בין תהליכים שונים, IPC השיטה הקודמת, באמצעות Messenger, מבוססת למעשה על AIDL בתור את המבנה הבסיסי שלו.

כפי שצוין בקטע הקודם, Messenger יוצר תור של כל הבקשות של הלקוח בשרשור יחיד, כך שהשירות מקבל בקשות בנפרד. אם, עם זאת, רוצים שהשירות יטפל במספר בקשות בו-זמנית, אז אפשר להשתמש ב-AIDL ישירות. במקרה כזה, השירות חייב להיות בטוח ל-threads עם אפשרות לבצע ריבוי שרשורים.

כדי להשתמש ישירות ב-AIDL, ליצור קובץ .aidl שמגדיר את ממשק התכנות. הכלים של Android SDK משתמשים של הקובץ הזה כדי ליצור מחלקה מופשטת שמממשת את הממשק ומטפלת ב-IPC, לאחר מכן הוא יוכל להרחיב את השירות.

הערה: ברוב האפליקציות, AIDL היא לא האפשרות הטובה ביותר תיצור שירות קשור, כי הוא עשוי לדרוש יכולות של ריבוי שרשורים עלול להוביל להטמעה מורכבת יותר. לכן, המסמך הזה לא מסביר כדי להשתמש בו בשירות שלך. אם אתם בטוחים שצריך כדי להשתמש ב-AIDL באופן ישיר, ראו AIDL מהמסמך.

הרחבה של סיווג Binder

אם רק האפליקציה המקומית משתמשת בשירות והיא לא צריכה עובדים על פני תהליכים שונים, תוכלו להטמיע מחלקה משלכם ב-Binder שמספקת ללקוח לגשת לשיטות ציבוריות בשירות.

הערה: האפשרות הזו פועלת רק אם הלקוח והשירות זהים את ההטמעה והעיבוד הכי נפוץ. לדוגמה, זה עובד טוב למוזיקה שצריך לקשר בין פעילות לשירות שלו שמשמיע מוזיקה רקע.

כך מגדירים את קטע הקוד:

  1. בשירות, יוצרים מופע של Binder שעושה אחד מהפרטים הבאים:
    • מכיל שיטות ציבוריות שהלקוח יכול לקרוא להן.
    • מחזירה את מכונת Service הנוכחית, עם methods ציבוריות הלקוח יכול להתקשר.
    • מחזירה מופע של מחלקה אחרת שמתארחת בשירות באמצעות methods ציבוריות הלקוח יכול להתקשר.
  2. החזרת המופע הזה של Binder משיטת הקריאה החוזרת (callback) של onBind().
  3. בלקוח, מקבלים את ה-Binder משיטת הקריאה החוזרת (callback) של onServiceConnected() לבצע קריאות לשירות המקושר באמצעות השיטות שצוינו.

הערה: השירות והלקוח חייבים להיות באותו אופן. כך שהלקוח יוכל להפעיל Cast של האובייקט שהוחזר ולקרוא באופן תקין לממשקי ה-API שלו. השירות והלקוח צריך להיות באותו תהליך, כי השיטה הזו לא מבצעת התמרנות בין תהליכים שונים.

הנה שירות לדוגמה, שנותן ללקוחות גישה לשיטות בשירות באמצעות הטמעה של Binder:

Kotlin

class LocalService : Service() {
    // Binder given to clients.
    private val binder = LocalBinder()

    // Random number generator.
    private val mGenerator = Random()

    /** Method for clients.  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)

    /**
     * Class used for the client Binder. Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods.
        fun getService(): LocalService = this@LocalService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }
}

Java

public class LocalService extends Service {
    // Binder given to clients.
    private final IBinder binder = new LocalBinder();
    // Random number generator.
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods.
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    /** Method for clients. */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

ה-LocalBinder מספק את ה-method getService() ללקוחות כדי לאחזר את המופע הנוכחי של LocalService. כך הלקוחות יכולים לקרוא ל-methods ציבוריות לאחר השיפור. לדוגמה, לקוחות יכולים להתקשר ל-getRandomNumber() מהשירות.

זו פעילות שקשורה אל LocalService ומתקשרת אל getRandomNumber() כשמשתמש לוחץ על לחצן:

Kotlin

class BindingActivity : Activity() {
    private lateinit var mService: LocalService
    private var mBound: Boolean = false

    /** Defines callbacks for service binding, passed to bindService().  */
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            val binder = service as LocalService.LocalBinder
            mService = binder.getService()
            mBound = true
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mBound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to LocalService.
        Intent(this, LocalService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        unbindService(connection)
        mBound = false
    }

    /** Called when a button is clicked (the button in the layout file attaches to
     * this method with the android:onClick attribute).  */
    fun onButtonClick(v: View) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            val num: Int = mService.randomNumber
            Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
        }
    }
}

Java

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService.
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
        mBound = false;
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute). */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService(). */
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

הדוגמה שלמעלה ממחישה איך הלקוח מקשר לשירות באמצעות הטמעה של ServiceConnection והקריאה החוזרת (callback) של onServiceConnected(). הבא מספק מידע נוסף על תהליך הקישור לשירות.

הערה: בדוגמה שלמעלה, השיטה onStop() מבטלת את הקישור של הלקוח מהשירות. לבטל את הקישור של הלקוחות לשירותים בזמנים המתאימים, כפי שמפורט ב בקטע הערות נוספות.

לקבלת קוד לדוגמה נוסף, אפשר לעיין ב הכיתה LocalService.java וגם LocalServiceActivities.java בכיתה ב-ApiDemos.

שימוש ב-Messenger

אם אתם צריכים שהשירות שלכם יוכל לתקשר עם תהליכים מרחוק, תוכלו להשתמש Messenger כדי לספק את הממשק לשירות. השיטה הזו מאפשרת כדי לבצע תקשורת בין תהליכים (IPC) בלי להשתמש ב-AIDL.

השימוש ב-Messenger לממשק שלך פשוט יותר מאשר שימוש ב-AIDL כי יש תורים של Messenger את כל הקריאות לשירות. ממשק AIDL טהור שולח בקשות סימולטניות שירות, שחייב לאחר מכן לטפל בריבוי שרשורים.

ברוב האפליקציות, השירות לא צריך לבצע ריבוי שרשורים, כך ששימוש ב-Messenger מאפשר לשירות לטפל בקריאה אחת בכל פעם. אם זה חשוב שהשירות מבוסס על כמה שרשורים, צריך להשתמש ב-AIDL כדי להגדיר את הממשק.

הנה סיכום של אופן השימוש ב-Messenger:

  1. השירות מטמיע Handler שמקבל קריאה חוזרת לגבי כל אחד מהלקוח.
  2. השירות משתמש ב-Handler כדי ליצור Messenger אובייקט (שהוא הפניה אל Handler).
  3. השדה Messenger יוצר IBinder שהשירות חוזרת ללקוחות מ-onBind().
  4. הלקוחות משתמשים ב-IBinder כדי ליצור את Messenger (שמפנה ל-Handler של השירות), שהלקוח משתמש בו כדי לשלוח Message אובייקטים לשירות.
  5. השירות מקבל כל Message ב-Handler שלו, באופן ספציפי, ב-method handleMessage().

כך אין שיטות שבהן הלקוח יכול לקרוא לשירות. במקום זאת, הלקוח מעביר הודעות (Message אובייקטים) שהשירות מקבל/ת Handler.

זוהי דוגמה לשירות פשוט שמשתמש בממשק Messenger:

Kotlin

/** Command to the service to display a message.  */
private const val MSG_SAY_HELLO = 1

class MessengerService : Service() {

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private lateinit var mMessenger: Messenger

    /**
     * Handler of incoming messages from clients.
     */
    internal class IncomingHandler(
            context: Context,
            private val applicationContext: Context = context.applicationContext
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO ->
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                else -> super.handleMessage(msg)
            }
        }
    }

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    override fun onBind(intent: Intent): IBinder? {
        Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show()
        mMessenger = Messenger(IncomingHandler(this))
        return mMessenger.binder
    }
}

Java

public class MessengerService extends Service {
    /**
     * Command to the service to display a message.
     */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

ה-method handleMessage() ברכיב Handler הוא המקום שבו השירות מקבל את Message הנכנס והוא מחליט מה לעשות בהתאם למשתמש what.

כל מה שהלקוח צריך לעשות הוא ליצור Messenger על סמך ה-IBinder שהוחזר על ידי השירות ולשלוח הודעה באמצעות send(). לדוגמה, הנה פעילות שקשורה שירות ומוסרת את ההודעה MSG_SAY_HELLO לשירות:

Kotlin

class ActivityMessenger : Activity() {
    /** Messenger for communicating with the service.  */
    private var mService: Messenger? = null

    /** Flag indicating whether we have called bind on the service.  */
    private var bound: Boolean = false

    /**
     * Class for interacting with the main interface of the service.
     */
    private val mConnection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null
            bound = false
        }
    }

    fun sayHello(v: View) {
        if (!bound) return
        // Create and send a message to the service, using a supported 'what' value.
        val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(msg)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to the service.
        Intent(this, MessengerService::class.java).also { intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection)
            bound = false
        }
    }
}

Java

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean bound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            bound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null;
            bound = false;
        }
    };

    public void sayHello(View v) {
        if (!bound) return;
        // Create and send a message to the service, using a supported 'what' value.
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service.
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection);
            bound = false;
        }
    }
}

בדוגמה הזו לא רואים איך השירות יכול להגיב ללקוח. אם רוצים כדי להגיב, צריך גם ליצור Messenger בלקוח. כשהלקוח מקבל את הקריאה החוזרת של onServiceConnected(), הוא שולח Message לשירות שכולל Messenger של הלקוח בפרמטר replyTo של ה-method send().

ניתן לראות דוגמה למתן הודעות דו-כיווניות ב MessengerService.java (שירות) וגם MessengerServiceActivities.java (לקוח) דוגמאות.

קישור לשירות

רכיבי אפליקציה (לקוחות) יכולים לקשר לשירות באמצעות קריאה bindService() מכשיר Android לאחר מכן המערכת מפעילה את השיטה onBind() של השירות, שמחזירה IBinder עבור אינטראקציה את השירות.

הקישור הוא אסינכרוני, ו-bindService() חוזר באופן מיידי בלי להחזיר את IBinder אל עם הלקוח. כדי לקבל את IBinder, הלקוח צריך ליצור של ServiceConnection ומעבירים אותו אל bindService(). השדה ServiceConnection כולל שיטת קריאה חוזרת כדי להעביר את IBinder.

הערה: רק ספקי פעילויות, שירותים וספקי תוכן יכולים להיות מקושרים לשירות – אי אפשר לקשר לשירות ממקלט שידורים.

כדי לקשר לשירות של לקוח, צריך לפעול לפי השלבים הבאים:

  1. הטמעה של ServiceConnection.

    ההטמעה חייבת לבטל שתי שיטות של קריאה חוזרת:

    onServiceConnected()
    המערכת קוראת לזה כדי להעביר את ה-IBinder שמוחזר על ה-method onBind() של השירות.
    onServiceDisconnected()
    מערכת Android תפעיל את ההודעה הזו אם החיבור לשירות לא צפוי אבד, למשל כשהשירות קורס או מושבת. המיקום הזה לא שנקראה ביטולי קישור של לקוחות.
  2. קוראים לפונקציה bindService() ומעבירים את ההטמעה של ServiceConnection.

    הערה: אם ה-method מחזירה את הערך False, ללקוח אין חיבור חוקי לשירות. עם זאת, unbindService() אצל הלקוח. אחרת, הלקוח שלך צריך למנוע את השירות המכשיר ייכבה כשהמכשיר ללא פעילות.

  3. כשהמערכת מתקשרת לשיטת הקריאה החוזרת של onServiceConnected(), אפשר להתחיל לבצע שיחות לשירות באמצעות בהתאם ל-methods שהוגדרו בממשק.
  4. כדי להתנתק מהשירות, צריך להתקשר אל unbindService().

    אם הלקוח עדיין קשור לשירות כשהאפליקציה משמיצה את הלקוח, תגרום לביטול הקישור של הלקוח. עדיף לבטל את הקישור של הלקוח ברגע שהפעולה מתבצעת. האינטראקציה עם השירות. הפעולה הזו מאפשרת לשירות לפעול ללא פעילות. אפשר לקבל מידע נוסף מידע על הזמנים המתאימים לקישור ולביטול הקישור, יש לעיין בקטע הערות נוספות.

הדוגמה הבאה מחברת את הלקוח לשירות שנוצר קודם לכן על ידי הרחבה של המחלקה של Binder, כך שכל מה שצריך לעשות הוא להפעיל Cast IBinder למחלקה LocalBinder ומבקשים את המכונה LocalService:

Kotlin

var mService: LocalService

val mConnection = object : ServiceConnection {
    // Called when the connection with the service is established.
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        val binder = service as LocalService.LocalBinder
        mService = binder.getService()
        mBound = true
    }

    // Called when the connection with the service disconnects unexpectedly.
    override fun onServiceDisconnected(className: ComponentName) {
        Log.e(TAG, "onServiceDisconnected")
        mBound = false
    }
}

Java

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established.
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly.
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mBound = false;
    }
};

באמצעות השדה הזה של ServiceConnection, הלקוח יכול לקשר לשירות על ידי העברת אותו ל-bindService(), כמו בדוגמה הבאה:

Kotlin

Intent(this, LocalService::class.java).also { intent ->
    bindService(intent, connection, Context.BIND_AUTO_CREATE)
}

Java

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • הפרמטר הראשון של bindService() הוא Intent שמגדיר באופן מפורש את השירות לקישור.

    זהירות: אם אתם משתמשים ב-Intent כדי לקשר אל Service, עליך לוודא שהאפליקציה מאובטחת באמצעות ערך מפורש בכוונה טובה. שימוש מתוך כוונה מרומזת להפעלת שירות הוא כי לא ניתן לדעת בוודאות איזה שירות מגיב לכוונה, והמשתמש לא יכול לראות איזה שירות מתחיל. החל מ-Android 5.0 (רמת API 21), המערכת מקפיצה הודעת שגיאה (throw) לחריגה אם מתקשרים ל-bindService() מתוך כוונה מרומזת.

  • הפרמטר השני הוא האובייקט ServiceConnection.
  • הפרמטר השלישי הוא דגל שמציין אפשרויות לקישור. בדרך כלל BIND_AUTO_CREATE, כדי ליצור את השירות אם הוא עדיין לא פועל בחיים. ערכים אפשריים נוספים הם BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND או 0 ללא.

הערות נוספות

הנה כמה הערות חשובות לגבי קישור לשירות:

  • תמיד מלכוד DeadObjectException חריגים, שנזרקים כשהחיבור מתנתק. זהו החריג היחיד שמתרחש על ידי שיטות מרחוק.
  • אובייקטים נספרים בהפניות בין תהליכים.
  • בדרך כלל מתבצעת התאמה של הקישור והביטול הקישור במהלך רגעי שיא תואמים ורגעים שלמים במחזור החיים של הלקוח, כפי שמתואר הדוגמאות הבאות:
    • אם יש לך צורך באינטראקציה עם השירות רק כשהפעילות גלויה, צריך לקשר את החשבון במהלך onStart() ולבטל את הקישור במהלך onStop().
    • אם אתם רוצים שהפעילות שלכם תקבל תשובות גם בזמן שהיא נעצרת ברקע, צריך לקשר במהלך onCreate() ולבטל את הקישור במהלך onDestroy(). שימו לב שזה מרמז על כך הפעילות צריכה להשתמש בשירות כל הזמן שהוא פועל, גם ברקע, כך השירות נמצא בתהליך אחר, ואז אתם מגדילים את המשקל של התהליך, והסבירות שהוא נהרג על ידי המערכת.

    הערה:בדרך כלל לא מתבצע קישור ומבטל קישור במהלך הקריאות החוזרות (callback) של onResume() ו-onPause() בפעילות שלך, מפני שהקריאות החוזרות האלה מתרחשות בכל את המעבר במחזור החיים. כדאי לצמצם ככל האפשר את העיבוד שמתבצע במעברים האלה.

    כמו כן, אם כמה פעילויות באפליקציה מקשרות לאותו שירות, מעבר בין שתיים מהפעילויות האלה, ייתכן שהשירות יושמד וייצרו מחדש בתור ביטולי קישור פעילות (בזמן השהיה) לפני הקישור הבא (בהמשך). מעבר בין הפעילויות הפעילויות מתואמות למחזורי החיים שלהן, כפי שהן מתוארות במאמר מחזור החיים של פעילות.

קוד לדוגמה נוסף שמראה איך לקשר לשירות, זמין בכתובת RemoteService.java בכיתה ב-ApiDemos.

ניהול מחזור החיים של שירות קשור

כששירות אינו מוגבל מכל הלקוחות, מערכת Android משמדת אותו (אלא אם הוא התחיל להשתמש startService()). לכן, אתם לא צריכים לנהל את מחזור החיים של השירות אם שירות קשור. מערכת Android מנהלת אותו בשבילך על סמך והוא קשור ללקוחות כלשהם.

אבל אם בוחרים להטמיע את שיטת הקריאה החוזרת onStartCommand(), צריך להפסיק באופן מפורש את השירות, מהסיבה השירות נחשב כמופעל. במקרה כזה, השירות יפעל עד מפסיק את עצמו באמצעות stopSelf() או רכיב אחר קורא ל-stopService(), בין אם הוא קשור או הלקוחות שלנו.

בנוסף, אם השירות מופעל ומקבל את הקישור, המערכת תבצע קריאה אמצעי התשלום onUnbind(), אפשר להחזיר אותו true אם רוצים לקבל שיחה אל onRebind() בפעם הבאה שהלקוח יוצר קשר עם השירות. הפונקציה onRebind() מחזירה null, אבל הלקוח עדיין מקבל את IBinder התקשרות חזרה onServiceConnected(). האיור הבא ממחיש את הלוגיקה של מחזור חיים כזה.

איור 1. מחזור החיים של שירות שמתחיל ומאפשרת קישור.

מידע נוסף על מחזור החיים של שירות שהתחלתם זמין במאמר סקירה כללית של השירותים.