نمای کلی خدمات محدود

یک سرویس محدود، سروری است که در رابط مشتری-سرور قرار دارد. این اجازه می دهد تا اجزایی مانند فعالیت ها به سرویس متصل شوند، درخواست ها را ارسال کنند، پاسخ ها را دریافت کنند و ارتباطات بین فرآیندی (IPC) را انجام دهند. یک سرویس محدود معمولاً فقط زمانی زندگی می کند که جزء برنامه دیگری را ارائه می دهد و به طور نامحدود در پس زمینه اجرا نمی شود.

این سند نحوه ایجاد یک سرویس محدود، از جمله نحوه اتصال به سرویس از سایر اجزای برنامه را توضیح می دهد. برای اطلاعات بیشتر در مورد خدمات به طور کلی، مانند نحوه ارائه اعلان‌ها از یک سرویس و تنظیم سرویس برای اجرا در پیش‌زمینه، به نمای کلی سرویس‌ها مراجعه کنید.

اصول اولیه

یک سرویس محدود پیاده سازی کلاس Service است که به برنامه های کاربردی دیگر اجازه می دهد به آن متصل شده و با آن تعامل داشته باشند. برای ارائه binding برای یک سرویس، شما متد onBind() callback را پیاده سازی می کنید. این روش یک شی IBinder را برمی گرداند که رابط برنامه نویسی را تعریف می کند که کلاینت ها می توانند از آن برای تعامل با سرویس استفاده کنند.

به یک سرویس شروع شده متصل شوید

همانطور که در نمای کلی سرویس ها بحث شد، می توانید سرویسی ایجاد کنید که هم شروع شده و هم محدود شده باشد. یعنی می‌توانید با فراخوانی startService() یک سرویس را راه‌اندازی کنید که به سرویس اجازه می‌دهد به طور نامحدود اجرا شود. همچنین می توانید با فراخوانی bindService() به مشتری اجازه دهید به سرویس متصل شود.

اگر اجازه دهید سرویس شما راه‌اندازی و محدود شود، وقتی سرویس شروع می‌شود، وقتی همه کلاینت‌ها باز می‌شوند، سیستم سرویس را از بین نمی‌برد . درعوض، باید صراحتاً سرویس را با فراخوانی stopSelf() یا stopService() متوقف کنید.

اگرچه معمولاً onBind() یا onStartCommand() را پیاده سازی می کنید، گاهی اوقات لازم است هر دو را پیاده سازی کنید. به عنوان مثال، یک پخش کننده موسیقی ممکن است مفید باشد که سرویس خود را به طور نامحدود اجرا کند و همچنین اتصال را ارائه دهد. به این ترتیب، یک فعالیت می‌تواند سرویس را برای پخش موسیقی شروع کند و حتی اگر کاربر برنامه را ترک کند، موسیقی به پخش ادامه می‌دهد. سپس، هنگامی که کاربر به برنامه بازگشت، فعالیت می تواند به سرویس متصل شود تا کنترل پخش را دوباره به دست آورد.

برای اطلاعات بیشتر در مورد چرخه عمر سرویس هنگام افزودن اتصال به سرویس شروع شده، به بخش مدیریت چرخه عمر سرویس محدود مراجعه کنید.

یک کلاینت با فراخوانی bindService() به یک سرویس متصل می شود. هنگامی که این کار را انجام می دهد، باید یک پیاده سازی از ServiceConnection را ارائه دهد که اتصال با سرویس را نظارت می کند. مقدار برگشتی bindService() نشان می دهد که آیا سرویس درخواستی وجود دارد یا خیر و آیا مشتری مجاز به دسترسی به آن است.

هنگامی که سیستم Android ارتباط بین سرویس گیرنده و سرویس ایجاد می کند، onServiceConnected() در ServiceConnection فراخوانی می کند. متد onServiceConnected() شامل یک آرگومان IBinder است که مشتری از آن برای برقراری ارتباط با سرویس محدود استفاده می کند.

می توانید چندین مشتری را به طور همزمان به یک سرویس متصل کنید. با این حال، سیستم کانال ارتباطی سرویس IBinder را در حافظه پنهان نگه می دارد. به عبارت دیگر، سیستم onBind() سرویس را فراخوانی می‌کند تا IBinder تنها زمانی که اولین مشتری متصل می‌شود، تولید کند. سپس سیستم همان IBinder به تمام کلاینت های اضافی که به همان سرویس متصل می شوند، بدون فراخوانی مجدد onBind() تحویل می دهد.

هنگامی که آخرین سرویس گیرنده از سرویس جدا می شود، سیستم سرویس را از بین می برد، مگر اینکه سرویس با استفاده از startService() راه اندازی شده باشد.

مهمترین بخش اجرای سرویس باند شما، تعریف رابطی است که متد callback onBind() شما برمی گرداند. بخش زیر چندین روش را مورد بحث قرار می دهد که می توانید رابط IBinder سرویس خود را تعریف کنید.

یک سرویس محدود ایجاد کنید

هنگام ایجاد سرویسی که اتصال را ارائه می دهد، باید یک IBinder ارائه دهید که رابط برنامه نویسی را ارائه می دهد که مشتریان می توانند برای تعامل با سرویس از آن استفاده کنند. سه راه برای تعریف رابط وجود دارد:

کلاس Binder را گسترش دهید
اگر سرویس شما برای برنامه شخصی شما خصوصی است و در همان فرآیند مشتری اجرا می شود که معمول است، رابط خود را با گسترش کلاس Binder و برگرداندن نمونه ای از آن از onBind() ایجاد کنید. مشتری Binder دریافت می کند و می تواند از آن برای دسترسی مستقیم به روش های عمومی موجود در اجرای Binder یا Service استفاده کند.

این روش ترجیحی است زمانی که سرویس شما صرفاً یک کارگر پس زمینه برای برنامه شخصی شما باشد. تنها مورد استفاده زمانی که این روش ترجیحی برای ایجاد رابط شما نیست، این است که سرویس شما توسط برنامه های کاربردی دیگر یا در فرآیندهای جداگانه استفاده شود.

از مسنجر استفاده کنید
اگر نیاز دارید که رابط شما در فرآیندهای مختلف کار کند، می توانید یک رابط برای سرویس با یک Messenger ایجاد کنید. به این ترتیب، سرویس یک Handler را تعریف می کند که به انواع مختلف اشیاء Message پاسخ می دهد.

این Handler مبنایی برای یک Messenger است که سپس می‌تواند یک IBinder با مشتری به اشتراک بگذارد و به مشتری اجازه می‌دهد با استفاده از اشیاء Message دستورات را به سرویس ارسال کند. علاوه بر این، مشتری می‌تواند یک Messenger برای خود تعریف کند، بنابراین سرویس می‌تواند پیام‌ها را بازگرداند.

این ساده‌ترین راه برای انجام ارتباطات بین پردازشی (IPC) است، زیرا Messenger تمام درخواست‌ها را در یک رشته قرار می‌دهد تا مجبور نباشید سرویس خود را به گونه‌ای طراحی کنید که ایمن باشد.

از AIDL استفاده کنید
Android Interface Definition Language (AIDL) اشیاء را به موارد اولیه که سیستم عامل می تواند درک کند تجزیه می کند و آنها را در سراسر فرآیندها برای اجرای IPC به کار می گیرد. تکنیک قبلی، با استفاده از یک Messenger ، در واقع بر اساس AIDL به عنوان ساختار زیربنایی آن است.

همانطور که در بخش قبل ذکر شد، Messenger یک صف از تمام درخواست های مشتری در یک رشته ایجاد می کند، بنابراین سرویس درخواست ها را یک به یک دریافت می کند. با این حال، اگر می خواهید سرویس شما چندین درخواست را به طور همزمان انجام دهد، می توانید مستقیماً از AIDL استفاده کنید. در این حالت، سرویس شما باید ایمن باشد و قابلیت چند رشته ای را داشته باشد.

برای استفاده مستقیم از AIDL، یک فایل .aidl ایجاد کنید که رابط برنامه نویسی را تعریف می کند. ابزار Android SDK از این فایل برای تولید یک کلاس انتزاعی استفاده می‌کند که رابط را پیاده‌سازی می‌کند و IPC را مدیریت می‌کند، سپس می‌توانید آن را در سرویس خود گسترش دهید.

توجه: برای اکثر برنامه‌ها، AIDL بهترین انتخاب برای ایجاد یک سرویس محدود نیست، زیرا ممکن است به قابلیت‌های چند رشته‌ای نیاز داشته باشد و می‌تواند منجر به اجرای پیچیده‌تر شود. بنابراین، این سند در مورد نحوه استفاده از آن برای خدمات شما صحبت نمی کند. اگر مطمئن هستید که باید مستقیماً از AIDL استفاده کنید، به سند AIDL مراجعه کنید.

کلاس Binder را گسترش دهید

اگر فقط برنامه محلی از سرویس شما استفاده می کند و نیازی به کار در بین فرآیندها ندارد، می توانید کلاس Binder خود را پیاده سازی کنید که دسترسی مستقیم مشتری شما به روش های عمومی در سرویس را فراهم می کند.

توجه: این فقط در صورتی کار می کند که مشتری و سرویس در برنامه و فرآیند یکسانی باشند، که رایج ترین است. به عنوان مثال، این برای یک برنامه موسیقی که باید یک فعالیت را به سرویس خود که در حال پخش موسیقی در پس‌زمینه است متصل کند، به خوبی کار می‌کند.

در اینجا نحوه تنظیم آن آمده است:

  1. در سرویس خود، یک نمونه از Binder ایجاد کنید که یکی از موارد زیر را انجام دهد:
    • شامل روش های عمومی است که مشتری می تواند با آنها تماس بگیرد.
    • نمونه Service فعلی را برمی‌گرداند، که دارای روش‌های عمومی است که مشتری می‌تواند تماس بگیرد.
    • نمونه‌ای از کلاس دیگری را که توسط سرویس میزبانی می‌شود با روش‌های عمومی که کلاینت می‌تواند فراخوانی کند، برمی‌گرداند.
  2. این نمونه از Binder را از متد onBind() callback برگردانید.
  3. در کلاینت، Binder از متد callback onServiceConnected() دریافت کنید و با استفاده از روش های ارائه شده با سرویس محدود تماس بگیرید.

توجه: سرویس و سرویس گیرنده باید در یک برنامه باشند تا مشتری بتواند شیء برگشتی را ارسال کند و APIهای آن را به درستی فراخوانی کند. سرویس و سرویس گیرنده نیز باید در یک فرآیند باشند، زیرا این تکنیک هیچ گونه مارشال‌سازی در بین فرآیندها را انجام نمی‌دهد.

به عنوان مثال، در اینجا یک سرویس است که دسترسی به روش‌های موجود در سرویس را از طریق پیاده‌سازی Binder به مشتریان ارائه می‌دهد:

کاتلین

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

جاوا

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 متد getService() برای مشتریان فراهم می کند تا نمونه فعلی LocalService را بازیابی کنند. این به مشتریان اجازه می دهد تا روش های عمومی را در سرویس فراخوانی کنند. به عنوان مثال، مشتریان می توانند getRandomNumber() از سرویس فراخوانی کنند.

در اینجا یک اکتیویتی است که به LocalService متصل می شود و با کلیک روی دکمه getRandomNumber() فرا می خواند:

کاتلین

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

جاوا

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 و پاسخ تماس onServiceConnected() به سرویس متصل می شود. بخش بعدی اطلاعات بیشتری در مورد این فرآیند اتصال به سرویس ارائه می دهد.

توجه: در مثال قبل، متد onStop() کلاینت را از سرویس جدا می کند. همانطور که در بخش یادداشت های اضافی بحث شد، مشتریان را در زمان های مناسب از خدمات جدا کنید.

برای نمونه کد بیشتر، به کلاس LocalService.java و کلاس LocalServiceActivities.java در ApiDemos مراجعه کنید.

از مسنجر استفاده کنید

اگر به سرویس خود برای ارتباط با فرآیندهای راه دور نیاز دارید، می توانید از یک Messenger برای ارائه رابط برای سرویس خود استفاده کنید. این تکنیک به شما امکان می دهد ارتباطات بین فرآیندی (IPC) را بدون نیاز به استفاده از AIDL انجام دهید.

استفاده از Messenger برای رابط کاربری ساده‌تر از استفاده از AIDL است زیرا Messenger تمام تماس‌های سرویس را در صف قرار می‌دهد. یک رابط AIDL خالص درخواست‌های هم‌زمان را به سرویس ارسال می‌کند، که سپس باید multithreading را مدیریت کند.

برای اکثر برنامه‌ها، سرویس نیازی به انجام چند رشته‌ای ندارد، بنابراین استفاده از Messenger به سرویس اجازه می‌دهد هر بار یک تماس را مدیریت کند. اگر مهم است که سرویس شما چند رشته ای باشد، از AIDL برای تعریف رابط خود استفاده کنید.

در اینجا خلاصه ای از نحوه استفاده از Messenger آورده شده است:

  1. این سرویس یک Handler پیاده سازی می کند که به ازای هر تماس از یک مشتری یک تماس پاسخ دریافت می کند.
  2. این سرویس از Handler برای ایجاد یک شی Messenger (که اشاره ای به Handler است) استفاده می کند.
  3. Messenger یک IBinder ایجاد می کند که سرویس از onBind() به مشتریان برمی گرداند.
  4. کلاینت ها از IBinder برای نمونه سازی Messenger (که به Handler سرویس اشاره می کند) استفاده می کنند، که مشتری از آن برای ارسال اشیاء Message به سرویس استفاده می کند.
  5. این سرویس هر Message در Handler خود دریافت می کند — به طور خاص، در متد handleMessage() .

به این ترتیب هیچ روشی برای تماس مشتری با سرویس وجود ندارد. در عوض، سرویس گیرنده پیام هایی (اشیاء Message ) را که سرویس در Handler خود دریافت می کند، ارائه می دهد.

در اینجا یک نمونه سرویس ساده است که از رابط Messenger استفاده می کند:

کاتلین

/** 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
    }
}

جاوا

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

متد handleMessage() در Handler جایی است که سرویس Message دریافتی را دریافت می کند و بر اساس what عضوی تصمیم می گیرد چه کاری انجام دهد.

تنها کاری که یک کلاینت باید انجام دهد این است که یک Messenger بر اساس IBinder بازگشتی توسط سرویس ایجاد کند و با استفاده از send() پیامی ارسال کند. به عنوان مثال، در اینجا یک فعالیت است که به سرویس متصل می شود و پیام MSG_SAY_HELLO را به سرویس ارائه می دهد:

کاتلین

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

جاوا

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 متد send() قرار می دهد.

می توانید نمونه ای از نحوه ارائه پیام دو طرفه را در نمونه های MessengerService.java (سرویس) و MessengerServiceActivities.java (مشتری) مشاهده کنید.

به یک سرویس متصل شوید

اجزای برنامه (کلینت ها) می توانند با فراخوانی bindService() به یک سرویس متصل شوند. سپس سیستم اندروید متد onBind() سرویس را فراخوانی می کند که یک IBinder برای تعامل با سرویس برمی گرداند.

binding ناهمزمان است و bindService() بلافاصله بدون برگرداندن IBinder به مشتری بازمی گردد. برای دریافت IBinder ، مشتری باید یک نمونه از ServiceConnection ایجاد کرده و آن را به bindService() ارسال کند. ServiceConnection شامل یک روش برگشت تماس است که سیستم برای تحویل IBinder فراخوانی می کند.

توجه: فقط فعالیت‌ها، خدمات و ارائه‌دهندگان محتوا می‌توانند به یک سرویس متصل شوند—شما نمی‌توانید از یک گیرنده پخش به سرویس متصل شوید.

برای اتصال به سرویس مشتری خود، این مراحل را دنبال کنید:

  1. ServiceConnection پیاده سازی کنید.

    پیاده سازی شما باید دو روش بازگشت به تماس را لغو کند:

    onServiceConnected()
    سیستم این را برای تحویل IBinder بازگردانده شده توسط متد onBind() سرویس فراخوانی می کند.
    onServiceDisconnected()
    سیستم اندروید زمانی که اتصال به سرویس به طور غیرمنتظره ای قطع می شود، مانند زمانی که سرویس از کار می افتد یا از بین می رود، این را فرا می خواند. وقتی کلاینت باز می شود این نام خوانده نمی شود.
  2. bindService() را فراخوانی کنید و اجرای ServiceConnection را ارسال کنید.

    توجه: اگر روش false را برگرداند، کلاینت شما اتصال معتبری به سرویس ندارد. با این حال، unbindService() در کلاینت خود فراخوانی کنید. در غیر این صورت، سرویس گیرنده شما از خاموش شدن سرویس در حالت بیکار جلوگیری می کند.

  3. هنگامی که سیستم onServiceConnected() شما را فراخوانی می‌کند، می‌توانید با استفاده از روش‌های تعریف‌شده توسط رابط، شروع به برقراری تماس با سرویس کنید.
  4. برای قطع ارتباط با سرویس، unbindService() را فراخوانی کنید.

    اگر زمانی که برنامه شما مشتری را از بین می‌برد، کلاینت شما همچنان به یک سرویس متصل است، تخریب باعث می‌شود که کلاینت باز شود. عمل بهتری است که به محض انجام تعامل با سرویس، مشتری را باز کنید. انجام این کار باعث می شود سرویس بیکار خاموش شود. برای اطلاعات بیشتر در مورد زمان‌های مناسب برای اتصال و باز کردن، به بخش یادداشت‌های اضافی مراجعه کنید.

مثال زیر، سرویس گیرنده را با گسترش کلاس Binder به سرویس ایجاد شده قبلی متصل می کند، بنابراین تنها کاری که باید انجام دهد این است که IBinder برگشتی را به کلاس LocalBinder فرستاده و نمونه LocalService درخواست کند:

کاتلین

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

جاوا

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() به یک سرویس متصل شود، همانطور که در مثال زیر نشان داده شده است:

کاتلین

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

جاوا

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • اولین پارامتر bindService() یک Intent است که به صراحت سرویسی را که باید باند نامگذاری کند.

    احتیاط: اگر از intent برای اتصال به یک Service استفاده می‌کنید، با استفاده از یک هدف صریح مطمئن شوید که برنامه شما ایمن است. استفاده از یک قصد ضمنی برای شروع یک سرویس یک خطر امنیتی است زیرا نمی توانید مطمئن باشید که چه سرویسی به این هدف پاسخ می دهد و کاربر نمی تواند ببیند کدام سرویس شروع می شود. با شروع Android 5.0 (سطح API 21)، اگر شما bindService() با یک هدف ضمنی فراخوانی کنید، سیستم یک استثنا ایجاد می کند.

  • پارامتر دوم شی ServiceConnection است.
  • سومین پارامتر پرچمی است که گزینه‌های مربوط به اتصال را نشان می‌دهد - معمولاً BIND_AUTO_CREATE ، برای ایجاد سرویس در صورتی که از قبل فعال نیست. سایر مقادیر ممکن عبارتند از BIND_DEBUG_UNBIND ، BIND_NOT_FOREGROUND ، یا 0 برای هیچ کدام.

یادداشت های اضافی

در اینجا چند نکته مهم در مورد اتصال به یک سرویس آورده شده است:

  • همیشه استثناهای DeadObjectException را به دام بیاندازید، که وقتی اتصال قطع می شود پرتاب می شوند. این تنها استثنایی است که توسط روش های راه دور ایجاد می شود.
  • اشیاء مرجع شمارش شده در فرآیندها هستند.
  • همانطور که در مثال‌های زیر توضیح داده شده است، معمولاً در طول زمان‌های تطبیق بالا و پایین‌آمدن چرخه زندگی مشتری، اتصال و عدم اتصال را جفت می‌کنید:
    • اگر نیاز به تعامل با سرویس دارید فقط زمانی که فعالیت شما قابل مشاهده است، در طول onStart() bind و در طول onStop() unbind کنید.
    • اگر می‌خواهید فعالیت شما حتی زمانی که در پس‌زمینه متوقف شده است، پاسخ‌ها را دریافت کند، در طول onCreate() bind و در طول onDestroy() unbind کنید. مراقب باشید که این بدان معناست که فعالیت شما نیاز به استفاده از سرویس در تمام مدت اجرا دارد، حتی در پس‌زمینه، بنابراین وقتی سرویس در فرآیند دیگری است، وزن فرآیند را افزایش می‌دهید و احتمال کشته شدن آن توسط سیستم

    توجه: شما معمولاً در طول تماس‌های onResume() و onPause() فعالیت خود را باند و باز نمی‌کنید ، زیرا این تماس‌ها در هر انتقال چرخه حیات رخ می‌دهند. پردازشی را که در این انتقال ها اتفاق می افتد به حداقل برسانید.

    همچنین، اگر چندین فعالیت در برنامه شما به یک سرویس متصل شود و بین دو مورد از آن فعالیت‌ها انتقالی وجود داشته باشد، ممکن است سرویس از بین برود و دوباره ایجاد شود، زیرا فعالیت فعلی (در طول مکث) قبل از اتصال بعدی (در طول رزومه) باز می‌شود. این انتقال فعالیت برای اینکه چگونه فعالیت ها چرخه عمر خود را هماهنگ می کنند در چرخه حیات فعالیت توضیح داده شده است.

برای نمونه کد بیشتر که نحوه اتصال به یک سرویس را نشان می دهد، به کلاس RemoteService.java در ApiDemos مراجعه کنید.

چرخه عمر یک سرویس محدود را مدیریت کنید

وقتی یک سرویس از همه کلاینت‌ها جدا می‌شود، سیستم Android آن را از بین می‌برد (مگر اینکه با استفاده از startService() شروع شده باشد. بنابراین، لازم نیست چرخه عمر سرویس خود را اگر صرفاً یک سرویس محدود است مدیریت کنید. سیستم اندروید آن را بر اساس اینکه آیا به هر مشتری متصل است یا خیر، آن را برای شما مدیریت می کند.

با این حال، اگر متد onStartCommand() را پیاده سازی کنید، باید صراحتاً سرویس را متوقف کنید، زیرا اکنون سرویس شروع شده در نظر گرفته می شود. در این مورد، سرویس تا زمانی اجرا می‌شود که سرویس با stopSelf() متوقف شود یا مؤلفه دیگری stopService() را فراخوانی کند، بدون توجه به اینکه آیا به هر کلاینت متصل است یا خیر.

علاوه بر این، اگر سرویس شما راه اندازی شده است و binding را می پذیرد، زمانی که سیستم متد onUnbind() شما را فراخوانی می کند، اگر می خواهید دفعه بعد که کلاینت به سرویس متصل می شود، تماسی با onRebind() دریافت کنید، می توانید به صورت اختیاری true برگردانید. onRebind() void برمی گرداند، اما مشتری همچنان IBinder در پاسخ به تماس onServiceConnected() خود دریافت می کند. شکل زیر منطق این نوع چرخه حیات را نشان می دهد.

شکل 1. چرخه عمر سرویسی که شروع شده و همچنین امکان اتصال را فراهم می کند.

برای اطلاعات بیشتر در مورد چرخه عمر سرویس شروع شده، به نمای کلی خدمات مراجعه کنید.