שירות קשור הוא השרת בממשק לקוח-שרת. הוא מאפשר לרכיבים למשל פעילויות שמקושרות לשירות, שליחת בקשות, קבלת תשובות תקשורת בין תהליכים (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
שמספקת ללקוח
לגשת לשיטות ציבוריות בשירות.
הערה: האפשרות הזו פועלת רק אם הלקוח והשירות זהים את ההטמעה והעיבוד הכי נפוץ. לדוגמה, זה עובד טוב למוזיקה שצריך לקשר בין פעילות לשירות שלו שמשמיע מוזיקה רקע.
כך מגדירים את קטע הקוד:
- בשירות, יוצרים מופע של
Binder
שעושה אחד מהפרטים הבאים:- מכיל שיטות ציבוריות שהלקוח יכול לקרוא להן.
- מחזירה את מכונת
Service
הנוכחית, עם methods ציבוריות הלקוח יכול להתקשר. - מחזירה מופע של מחלקה אחרת שמתארחת בשירות באמצעות methods ציבוריות הלקוח יכול להתקשר.
- החזרת המופע הזה של
Binder
משיטת הקריאה החוזרת (callback) שלonBind()
. - בלקוח, מקבלים את ה-
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
:
- השירות מטמיע
Handler
שמקבל קריאה חוזרת לגבי כל אחד מהלקוח. - השירות משתמש ב-
Handler
כדי ליצורMessenger
אובייקט (שהוא הפניה אלHandler
). - השדה
Messenger
יוצרIBinder
שהשירות חוזרת ללקוחות מ-onBind()
. - הלקוחות משתמשים ב-
IBinder
כדי ליצור אתMessenger
(שמפנה ל-Handler
של השירות), שהלקוח משתמש בו כדי לשלוחMessage
אובייקטים לשירות. - השירות מקבל כל
Message
ב-Handler
שלו, באופן ספציפי, ב-methodhandleMessage()
.
כך אין שיטות שבהן הלקוח יכול לקרוא לשירות. במקום זאת,
הלקוח מעביר הודעות (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
.
הערה: רק ספקי פעילויות, שירותים וספקי תוכן יכולים להיות מקושרים לשירות – אי אפשר לקשר לשירות ממקלט שידורים.
כדי לקשר לשירות של לקוח, צריך לפעול לפי השלבים הבאים:
- הטמעה של
ServiceConnection
.ההטמעה חייבת לבטל שתי שיטות של קריאה חוזרת:
onServiceConnected()
- המערכת קוראת לזה כדי להעביר את ה-
IBinder
שמוחזר על ה-methodonBind()
של השירות. onServiceDisconnected()
- מערכת Android תפעיל את ההודעה הזו אם החיבור לשירות לא צפוי אבד, למשל כשהשירות קורס או מושבת. המיקום הזה לא שנקראה ביטולי קישור של לקוחות.
- קוראים לפונקציה
bindService()
ומעבירים את ההטמעה שלServiceConnection
.הערה: אם ה-method מחזירה את הערך False, ללקוח אין חיבור חוקי לשירות. עם זאת,
unbindService()
אצל הלקוח. אחרת, הלקוח שלך צריך למנוע את השירות המכשיר ייכבה כשהמכשיר ללא פעילות. - כשהמערכת מתקשרת לשיטת הקריאה החוזרת של
onServiceConnected()
, אפשר להתחיל לבצע שיחות לשירות באמצעות בהתאם ל-methods שהוגדרו בממשק. - כדי להתנתק מהשירות, צריך להתקשר אל
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()
.
האיור הבא ממחיש את הלוגיקה של מחזור חיים כזה.
מידע נוסף על מחזור החיים של שירות שהתחלתם זמין במאמר סקירה כללית של השירותים.