বাউন্ড পরিষেবা হল ক্লায়েন্ট-সার্ভার ইন্টারফেসের সার্ভার। এটি অ্যাক্টিভিটির মতো কম্পোনেন্টকে পরিষেবার সাথে বাইন্ড করতে, অনুরোধ পাঠাতে, উত্তর পেতে এবং ইন্টারপ্রসেস কমিউনিকেশন (IPC) সম্পাদন করতে দেয়। একটি বাউন্ড পরিষেবা সাধারণত শুধুমাত্র অন্য অ্যাপ্লিকেশন উপাদান পরিবেশন করার সময় বেঁচে থাকে এবং ব্যাকগ্রাউন্ডে অনির্দিষ্টকালের জন্য চলে না।
এই ডকুমেন্ট থেকে আপনি জানতে পারবেন কীভাবে বাউন্ড সার্ভিস তৈরি করতে হয়, যার মধ্যে অন্যান্য অ্যাপ্লিকেশন কম্পোনেন্ট থেকে সার্ভিসে কীভাবে বাউন্ড করতে হয় তাও অন্তর্ভুক্ত। সাধারণভাবে পরিষেবা সম্পর্কে অতিরিক্ত তথ্যের জন্য, যেমন কোনও পরিষেবা থেকে কীভাবে বিজ্ঞপ্তি ডেলিভার করতে হয় এবং ফোরগ্রাউন্ডে রান করার জন্য পরিষেবা সেট করতে হয়, পরিষেবার ওভারভিউ দেখুন।
প্রাথমিক বিষয়
বাউন্ড পরিষেবা হল Service ক্লাসের একটি ইমপ্লিমেন্টেশন যা অন্যান্য অ্যাপ্লিকেশনকে
এটির সাথে বাইন্ড করতে এবং এটির সাথে ইন্টার্যাক্ট করতে দেয়। কোনও পরিষেবার জন্য বাইন্ডিং প্রদান করতে, আপনাকে onBind() কলব্যাক পদ্ধতি প্রয়োগ করতে হবে। এই
পদ্ধতিটি একটি IBinder অবজেক্ট রিটার্ন করে যা প্রোগ্রামিং ইন্টারফেসকে সংজ্ঞায়িত করে যা
ক্লায়েন্টরা পরিষেবার সাথে ইন্টারঅ্যাক্ট করতে ব্যবহার করতে পারে।
একটি শুরু করা পরিষেবাতে আবদ্ধ করুন
পরিষেবার ওভারভিউ-এ যেমন আলোচনা করা হয়েছে,
আপনি এমন একটি পরিষেবা তৈরি করতে পারেন যা শুরু এবং বাউন্ড, দুটিই করা হয়েছে। অর্থাৎ, আপনি startService() কল করে একটি পরিষেবা শুরু করতে পারেন, যা পরিষেবাটিকে অনির্দিষ্টকালের জন্য চলতে দেয়। এছাড়াও, আপনি bindService() কল করে কোনও ক্লায়েন্টকে পরিষেবার সাথে বাইন্ড করতে দিতে পারেন।
আপনি যদি আপনার পরিষেবা শুরু এবং বাউন্ড হতে দেন, তাহলে পরিষেবা শুরু হলে,
সব ক্লায়েন্ট আনবাইন্ড করার সময় সিস্টেম পরিষেবাটি নষ্ট করে না।
পরিবর্তে, আপনাকে অবশ্যই
stopSelf() বা stopService()-এ কল করে পরিষেবাটি বন্ধ করতে হবে।
যদিও আপনি সাধারণত onBind()
অথবা onStartCommand()-এর মধ্যে যেকোনও একটি প্রয়োগ করেন, তবে কখনও কখনও
দুটিই প্রয়োগ করার
প্রয়োজন হয়। যেমন, কোনও মিউজিক প্লেয়ারের ক্ষেত্রে তার পরিষেবা অনির্দিষ্টকালের জন্য চালানো এবং
বাইন্ডিং প্রদান করা উপযোগী হতে পারে। এইভাবে, কোনও অ্যাক্টিভিটি কিছু মিউজিক চালানোর জন্য পরিষেবা শুরু করতে পারে এবং ব্যবহারকারী অ্যাপ্লিকেশন ছেড়ে চলে গেলেও মিউজিক চলতে থাকে। তারপর, ব্যবহারকারী
অ্যাপ্লিকেশনে ফিরে এলে, প্লেব্যাকের কন্ট্রোল ফিরে পেতে অ্যাক্টিভিটি পরিষেবার সাথে
বাইন্ড করতে পারে।
চালু করা পরিষেবায় বাইন্ডিং যোগ করার সময় পরিষেবার লাইফসাইকেল সম্পর্কে আরও তথ্য পেতে, বাউন্ড পরিষেবার লাইফসাইকেল ম্যানেজ করুন বিভাগটি দেখুন।
bindService()-এ কল করার মাধ্যমে
কোনও ক্লায়েন্ট কোনও পরিষেবার সাথে বাইন্ড করে। এটি করার সময়, এটিকে অবশ্যই
ServiceConnection-এর একটি বাস্তবায়ন প্রদান করতে হবে, যা
পরিষেবার সাথে কানেকশন মনিটর করে। bindService()-এর রিটার্ন ভ্যালু
থেকে বোঝা যায় যে অনুরোধ করা পরিষেবাটি আছে কিনা এবং
ক্লায়েন্টের কাছে সেটির অ্যাক্সেস আছে কিনা।
Android সিস্টেম
ক্লায়েন্ট ও পরিষেবার মধ্যে কানেকশন তৈরি করলে, এটি
ServiceConnection-এ
onServiceConnected() কল করে। onServiceConnected()
পদ্ধতিতে একটি IBinder
আর্গুমেন্ট অন্তর্ভুক্ত থাকে, যা ক্লায়েন্ট বাউন্ড পরিষেবার সাথে যোগাযোগ করতে ব্যবহার করে।
আপনি একই সাথে একাধিক ক্লায়েন্টকে একটি পরিষেবার সাথে কানেক্ট করতে পারেন। তবে, সিস্টেম IBinder পরিষেবা কমিউনিকেশন চ্যানেল ক্যাশে করে।
অন্যভাবে বলতে গেলে, প্রথম ক্লায়েন্ট বাইন্ড করার পরেই সিস্টেম IBinder জেনারেট করার জন্য পরিষেবার onBind()
মেথড কল করে। তারপরে, সিস্টেম সেই একই IBinder,
সেই একই পরিষেবার সাথে যুক্ত সব অতিরিক্ত ক্লায়েন্টকে ডেলিভার করে, এক্ষেত্রে onBind()-কে
আবার কল করা হয় না।
শেষ ক্লায়েন্ট পরিষেবা থেকে আনবাইন্ড করলে, সিস্টেম পরিষেবাটি নষ্ট করে দেয়, যদি না
পরিষেবাটি startService() ব্যবহার করে শুরু করা হয়।
আপনার বাউন্ড সার্ভিস ইমপ্লিমেন্টেশনের সবচেয়ে গুরুত্বপূর্ণ অংশ হল ইন্টারফেসকে সংজ্ঞায়িত করা
যা আপনার onBind() কলব্যাক পদ্ধতি ফিরিয়ে দেয়। নিম্নলিখিত
বিভাগে আপনার পরিষেবার IBinder ইন্টারফেস
সংজ্ঞায়িত করার বিভিন্ন উপায় আলোচনা করা হয়েছে।
একটি বাউন্ড পরিষেবা তৈরি করুন
বাইন্ডিং প্রদান করে এমন কোনও পরিষেবা তৈরি করার সময়, আপনাকে অবশ্যই একটি IBinder
প্রদান করতে হবে যা প্রোগ্রামিং ইন্টারফেস প্রদান করে এবং ক্লায়েন্টরা পরিষেবার সাথে ইন্টার্যাক্ট করার জন্য এটি ব্যবহার করতে পারে। আপনি
তিনটি উপায়ে ইন্টারফেস নির্ধারণ করতে পারবেন:
- বাইন্ডার ক্লাস এক্সটেন্ড করা
- আপনার পরিষেবা যদি আপনার নিজের অ্যাপ্লিকেশনের জন্য ব্যক্তিগত হয় এবং ক্লায়েন্টের মতো একই প্রসেসে
রান করে, যা সাধারণ, তাহলে
Binderক্লাস বাড়িয়ে এবংonBind()থেকে এটির একটি ইনস্ট্যান্স ফিরিয়ে দিয়ে আপনার ইন্টারফেস তৈরি করুন। ক্লায়েন্টBinderপায় এবংBinderইমপ্লিমেন্টেশন বাService-এ উপলভ্য পাবলিক মেথড সরাসরি অ্যাক্সেস করতে এটি ব্যবহার করতে পারে।আপনার পরিষেবাটি যখন আপনার নিজের অ্যাপ্লিকেশনের জন্য শুধুমাত্র একটি ব্যাকগ্রাউন্ড কর্মী হয় তখন এটিই হল পছন্দের কৌশল। আপনার ইন্টারফেস তৈরি করার জন্য এটি একমাত্র ব্যবহারিক ক্ষেত্র নয়, যদি আপনার পরিষেবা অন্যান্য অ্যাপ্লিকেশন বা আলাদা প্রসেস জুড়ে ব্যবহার করা হয়।
- মেসেঞ্জার ব্যবহার করা
- আপনার ইন্টারফেস যদি বিভিন্ন প্রসেস জুড়ে কাজ করার প্রয়োজন হয়, তাহলে আপনি
Messengerসহ পরিষেবার জন্য একটি ইন্টারফেস তৈরি করতে পারেন। এইভাবে, পরিষেবাটি একটিHandlerসংজ্ঞায়িত করে যা বিভিন্ন ধরনেরMessageঅবজেক্টে সাড়া দেয়।এই
HandlerহলMessenger-এর ভিত্তি যা পরে ক্লায়েন্টের সাথেIBinderশেয়ার করতে পারে, এর ফলে ক্লায়েন্টMessageঅবজেক্ট ব্যবহার করে পরিষেবায় কমান্ড পাঠাতে পারে। এছাড়াও, ক্লায়েন্ট নিজেরMessengerসংজ্ঞায়িত করতে পারে, যাতে পরিষেবাটি মেসেজ ফেরত পাঠাতে পারে।ইন্টারপ্রসেস কমিউনিকেশন (IPC) করার এটি সবচেয়ে সহজ উপায়, কারণ
Messengerসব অনুরোধকে একটি থ্রেডে সারিবদ্ধ করে যাতে আপনাকে থ্রেড-নিরাপদ করার জন্য আপনার পরিষেবা ডিজাইন করতে না হয়। - AIDL ব্যবহার করা
- Android ইন্টারফেস ডেফিনিশন ল্যাঙ্গুয়েজ (AIDL) অবজেক্টকে এমন প্রিমিটিভে
বিভক্ত করে যা অপারেটিং সিস্টেম বুঝতে পারে এবং IPC পারফর্ম করার জন্য
প্রসেস জুড়ে সেগুলিকে মার্শাল করে। আগের টেকনিক,
Messengerব্যবহার করে, আসলে AIDL-এর উপর ভিত্তি করে এর অন্তর্নিহিত কাঠামো।আগের বিভাগে যেমন উল্লেখ করা হয়েছে,
Messengerএকটি থ্রেডে সব ক্লায়েন্ট অনুরোধের একটি সারি তৈরি করে, যাতে পরিষেবাটি একবারে একটি অনুরোধ পায়। তবে, আপনার পরিষেবা যদি একসাথে একাধিক অনুরোধ ম্যানেজ করতে চায়, তাহলে আপনি সরাসরি AIDL ব্যবহার করতে পারেন। এই ক্ষেত্রে, আপনার পরিষেবা অবশ্যই থ্রেড-সেফ এবং মাল্টিথ্রেডিংয়ের জন্য উপযুক্ত হতে হবে।সরাসরি AIDL ব্যবহার করতে, একটি
.aidlফাইল তৈরি করুন যা প্রোগ্রামিং ইন্টারফেসকে সংজ্ঞায়িত করে। Android SDK টুল এই ফাইল ব্যবহার করে একটি অ্যাবস্ট্রাক্ট ক্লাস তৈরি করে যা ইন্টারফেস প্রয়োগ করে এবং IPC ম্যানেজ করে, যা আপনি আপনার পরিষেবার মধ্যে এক্সটেন্ড করতে পারবেন।
মনে রাখবেন: বেশিরভাগ অ্যাপ্লিকেশনের ক্ষেত্রে, বাউন্ড সার্ভিস তৈরি করার জন্য AIDL সেরা বিকল্প নয়, কারণ এর জন্য মাল্টিথ্রেডিং ক্ষমতার প্রয়োজন হতে পারে এবং এর ফলে আরও জটিল ইমপ্লিমেন্টেশন হতে পারে। তাই, এই ডকুমেন্টে আপনার পরিষেবার জন্য এটি কীভাবে ব্যবহার করবেন সেই বিষয়ে আলোচনা করা হয়নি। আপনি যদি নিশ্চিত হন যে আপনাকে সরাসরি AIDL ব্যবহার করতে হবে, তাহলে AIDL ডকুমেন্ট দেখুন।
Binder ক্লাস এক্সটেন্ড করুন
শুধুমাত্র স্থানীয় অ্যাপ্লিকেশন আপনার পরিষেবা ব্যবহার করলে এবং প্রসেস জুড়ে কাজ করার প্রয়োজন না হলে,
আপনি নিজের Binder ক্লাস প্রয়োগ করতে পারেন যা আপনার ক্লায়েন্টকে পরিষেবার পাবলিক পদ্ধতিতে সরাসরি অ্যাক্সেস প্রদান করে।
মনে রাখবেন: ক্লায়েন্ট ও পরিষেবা একই অ্যাপ্লিকেশন ও প্রসেসে থাকলে তবেই এটি কাজ করে, যা সাধারণত হয়ে থাকে। যেমন, এটি এমন একটি মিউজিক অ্যাপ্লিকেশনের জন্য ভাল কাজ করে যা ব্যাকগ্রাউন্ডে মিউজিক চালানোর জন্য নিজস্ব পরিষেবার সাথে একটি অ্যাক্টিভিটি বাইন্ড করতে চায়।
এটি কীভাবে সেট-আপ করবেন তা এখানে দেওয়া হল:
- আপনার পরিষেবায়,
Binder-এর একটি ইনস্ট্যান্স তৈরি করুন যা নিম্নলিখিতগুলির মধ্যে একটি করে:- এতে সর্বজনীন পদ্ধতি আছে যা ক্লায়েন্ট কল করতে পারে।
- বর্তমান
Serviceইনস্ট্যান্স রিটার্ন করে, যার পাবলিক পদ্ধতি ক্লায়েন্ট কল করতে পারে। - পরিষেবার মাধ্যমে হোস্ট করা অন্য ক্লাসের ইনস্ট্যান্স ফিরিয়ে দেয়, যেখানে ক্লায়েন্ট কল করতে পারে এমন পাবলিক পদ্ধতি থাকে।
onBind()কলব্যাক পদ্ধতি থেকেBinder-এর এই ইনস্ট্যান্স ফিরিয়ে দিন।- ক্লায়েন্টে,
onServiceConnected()কলব্যাক পদ্ধতি থেকেBinderপান এবং প্রদান করা পদ্ধতি ব্যবহার করে বাউন্ড পরিষেবাতে কল করুন।
মনে রাখবেন: পরিষেবা ও ক্লায়েন্টকে একই অ্যাপ্লিকেশনে থাকতে হবে যাতে ক্লায়েন্ট ফেরত আসা অবজেক্ট কাস্ট করতে এবং সঠিকভাবে এর 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 } }
জাভা
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, LocalService-এর বর্তমান ইনস্ট্যান্স ফিরিয়ে আনার জন্য ক্লায়েন্টদের getService() পদ্ধতি প্রদান করে। এর ফলে ক্লায়েন্টরা পরিষেবায় পাবলিক মেথড কল করতে
পারেন। যেমন, ক্লায়েন্টরা পরিষেবা থেকে getRandomNumber()-এ কল করতে পারেন।
এখানে একটি অ্যাক্টিভিটি দেওয়া হল যা LocalService-এর সাথে বাইন্ড করে এবং বোতামে ক্লিক করা হলে
getRandomNumber()-কে কল করে:
Kotlin
class BindingActivity : Activity() { private var mService: LocalService? = null 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) { mService = null 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) // Release the reference so the service can be garbage-collected once it's // destroyed. onServiceDisconnected() isn't called when the client unbinds. mService = null 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 ?: return 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); // Release the reference so the service can be garbage-collected once it's // destroyed. onServiceDisconnected() isn't called when the client unbinds. mService = null; 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) { mService = null; mBound = false; } }; }
আগের স্যাম্পেল থেকে বোঝা যায় যে ক্লায়েন্ট কীভাবে ServiceConnection এবং onServiceConnected() কলব্যাকের ইমপ্লিমেন্টেশন ব্যবহার করে পরিষেবার সাথে বাইন্ড করে। পরবর্তী
বিভাগে পরিষেবার সাথে বাইন্ডিংয়ের এই প্রসেস সম্পর্কে আরও তথ্য দেওয়া হয়েছে।
মনে রাখবেন: পূর্ববর্তী উদাহরণে, onStop() পদ্ধতি পরিষেবা থেকে ক্লায়েন্টকে আনবাইন্ড করে
এবং এর রেফারেন্স রিলিজ করে।
অতিরিক্ত নোট বিভাগে যেমন আলোচনা করা হয়েছে, সেই অনুযায়ী উপযুক্ত সময়ে পরিষেবা থেকে ক্লায়েন্টকে আনবাইন্ড করুন।
আরও স্যাম্পেল কোডের জন্য, ApiDemos-এ
LocalService.java ক্লাস এবং
LocalServiceActivities.java ক্লাস দেখুন।
মেসেঞ্জার ব্যবহার করুন
রিমোট প্রসেসের সাথে যোগাযোগ করার জন্য আপনার পরিষেবার প্রয়োজন হলে, আপনার পরিষেবার জন্য ইন্টারফেস প্রদান করতে আপনি
Messenger ব্যবহার করতে পারেন। এই টেকনিকের সাহায্যে
আপনি AIDL ব্যবহার না করেই ইন্টারপ্রসেস কমিউনিকেশন (IPC) করতে পারবেন।
আপনার ইন্টারফেসের জন্য Messenger ব্যবহার করা
AIDL ব্যবহার করার চেয়ে সহজ কারণ Messenger
পরিষেবার সব কলকে সারিবদ্ধ করে। একটি পিওর AIDL ইন্টারফেস একই সাথে পরিষেবাতে অনুরোধ পাঠায়, যা অবশ্যই মাল্টিথ্রেডিং ম্যানেজ করে।
বেশিরভাগ অ্যাপ্লিকেশনের ক্ষেত্রে, পরিষেবাকে মাল্টিথ্রেডিং করতে হয় না, তাই Messenger ব্যবহার করলে পরিষেবাটি একবারে একটি কল ম্যানেজ করতে পারে। আপনার পরিষেবা মাল্টিথ্রেডেড হওয়া গুরুত্বপূর্ণ হলে, আপনার ইন্টারফেস সংজ্ঞায়িত করতে AIDL ব্যবহার করুন।
Messenger কীভাবে ব্যবহার করবেন তার সারসংক্ষেপ এখানে দেওয়া হল:
- পরিষেবাটি একটি
Handlerপ্রয়োগ করে যা প্রতিটি ক্লায়েন্টের কলের জন্য একটি কলব্যাক পায়। - পরিষেবাটি
Handlerব্যবহার করে একটিMessengerঅবজেক্ট তৈরি করে (যাHandler-এর একটি রেফারেন্স)। MessengerএকটিIBinderতৈরি করে যা পরিষেবাটিonBind()থেকে ক্লায়েন্টদের কাছে ফেরত দেয়।- ক্লায়েন্ট
IBinderব্যবহার করেMessengerইনস্ট্যান্টিয়েট করে (যা পরিষেবারHandlerরেফারেন্স দেয়), যা ক্লায়েন্ট ব্যবহার করেMessageঅবজেক্ট পরিষেবায় পাঠায়। - পরিষেবাটি তার
Handler-এ প্রতিটিMessageগ্রহণ করে—বিশেষ করে,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 } }
জাভা
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(); } }
Handler-এ handleMessage() পদ্ধতি হল সেই জায়গা যেখানে পরিষেবা ইনকামিং Message পায়
এবং what মেম্বারের উপর ভিত্তি করে কী করতে হবে তা সিদ্ধান্ত নেয়।
ক্লায়েন্টকে শুধু পরিষেবার মাধ্যমে ফেরত পাঠানো IBinder-এর ভিত্তিতে Messenger তৈরি করতে হবে এবং 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) mService = null 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); mService = null; bound = false; } } }
এই উদাহরণে দেখানো হয়নি যে পরিষেবাটি কীভাবে ক্লায়েন্টকে উত্তর দিতে পারে।
আপনি যদি চান যে
পরিষেবাটি উত্তর দিক, তাহলে আপনাকে ক্লায়েন্টে একটি Messenger তৈরি করতে হবে।
ক্লায়েন্ট onServiceConnected() কলব্যাক পেলে, এটি পরিষেবায় Message পাঠায় যার মধ্যে
send() পদ্ধতির replyTo প্যারামিটারে
ক্লায়েন্টের Messenger অন্তর্ভুক্ত থাকে।
MessengerService.java (পরিষেবা) এবং
MessengerServiceActivities.java (ক্লায়েন্ট) স্যাম্পেলে আপনি দেখতে পাবেন কীভাবে টু-ওয়ে মেসেজিং প্রদান করতে হয়।
কোনও পরিষেবার সাথে বাইন্ড করা
অ্যাপ্লিকেশন কম্পোনেন্ট (ক্লায়েন্ট)
bindService()-এ কল করে কোনও পরিষেবার সাথে বাইন্ড করতে পারে। Android
সিস্টেম তখন পরিষেবার onBind() পদ্ধতিকে কল করে, যা পরিষেবার সাথে ইন্টার্যাক্ট করার জন্য
একটি IBinder রিটার্ন করে।
বাইন্ডিংটি অ্যাসিঙ্ক্রোনাস এবং bindService() অবিলম্বে IBinder ক্লায়েন্টকে ফেরত না দিয়েই ফেরত দেয়। IBinder পেতে, ক্লায়েন্টকে অবশ্যই ServiceConnection-এর ইনস্ট্যান্স তৈরি করতে হবে
এবং bindService()-এ পাস করতে হবে। ServiceConnection-এ একটি কলব্যাক পদ্ধতি অন্তর্ভুক্ত থাকে যা
সিস্টেম IBinder ডেলিভার করার জন্য কল করে।
মনে রাখবেন: শুধুমাত্র অ্যাক্টিভিটি, পরিষেবা এবং কন্টেন্ট প্রোভাইডার কোনও পরিষেবার সাথে বাইন্ড করতে পারে—আপনি ব্রডকাস্ট রিসিভার থেকে কোনও পরিষেবার সাথে বাইন্ড করতে পারবেন না।
আপনার ক্লায়েন্ট থেকে কোনও পরিষেবার সাথে বাইন্ড করতে, এইসব ধাপ অনুসরণ করুন:
ServiceConnectionপ্রয়োগ করুন।আপনার ইমপ্লিমেন্টেশনকে অবশ্যই দুটি কলব্যাক পদ্ধতি ওভাররাইড করতে হবে:
onServiceConnected()- পরিষেবার
onBind()পদ্ধতি দ্বারা ফেরত দেওয়াIBinderডেলিভার করার জন্য সিস্টেম এটিকে কল করে। onServiceDisconnected()- পরিষেবার সাথে কানেকশন অপ্রত্যাশিতভাবে বিচ্ছিন্ন হয়ে গেলে, যেমন পরিষেবা ক্র্যাশ করলে বা বন্ধ হয়ে গেলে, Android সিস্টেম এটি কল করে। ক্লায়েন্ট আনবাইন্ড করলে এটি কল করা হয় না।
bindService()কল করুন,ServiceConnectionইমপ্লিমেন্টেশন পাস করুন।মনে রাখবেন: পদ্ধতিটি মিথ্যা রিটার্ন করলে, আপনার ক্লায়েন্টের কাছে পরিষেবার সাথে কোনও বৈধ কানেকশন নেই। তবে, আপনার ক্লায়েন্টে
unbindService()কল করুন। অন্যথায়, আপনার ক্লায়েন্ট নিষ্ক্রিয় থাকার সময় পরিষেবা বন্ধ হওয়া থেকে বিরত রাখে।- সিস্টেম আপনার
onServiceConnected()কলব্যাক মেথড কল করলে, আপনি ইন্টারফেসের মাধ্যমে সংজ্ঞায়িত মেথড ব্যবহার করে পরিষেবায় কল করা শুরু করতে পারবেন। - পরিষেবা থেকে ডিসকানেক্ট করতে,
unbindService()নম্বরে কল করুন।আপনার অ্যাপ ক্লায়েন্টকে ধ্বংস করার সময় আপনার ক্লায়েন্ট এখনও কোনও পরিষেবার সাথে আবদ্ধ থাকলে, ধ্বংসের কারণে ক্লায়েন্ট আনবাউন্ড হয়ে যায়। পরিষেবার সাথে ইন্টার্যাক্ট করা হয়ে গেলে যত তাড়াতাড়ি সম্ভব ক্লায়েন্টকে আনবাইন্ড করা ভাল অভ্যাস। এটি করলে, অলস পরিষেবা বন্ধ হয়ে যায়। বাইন্ড ও আনবাইন্ড করার উপযুক্ত সময় সম্পর্কে আরও তথ্যের জন্য, অতিরিক্ত নোট বিভাগ দেখুন।
নিম্নলিখিত উদাহরণটি ক্লায়েন্টকে পূর্বে তৈরি করা পরিষেবার সাথে কানেক্ট করে
বাইন্ডার ক্লাস বাড়ানো, তাই এটিকে যা করতে হবে তা হল রিটার্ন করা কাস্ট করা
IBinder থেকে LocalBinder ক্লাস এবং LocalService ইনস্ট্যান্সের জন্য অনুরোধ করুন:
Kotlin
var mService: LocalService? = null 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") mService = null 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"); mService = null; mBound = false; } };
এই ServiceConnection-এর মাধ্যমে, ক্লায়েন্ট কোনও পরিষেবার সাথে বাইন্ড করতে পারে
bindService()-এ এটি পাস করার মাধ্যমে, যেমন নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
Kotlin
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যা স্পষ্টভাবে বাইন্ড করার জন্য পরিষেবার নাম দেয়।সতর্কতা: আপনি যদি কোনও
Service-এর সাথে বাইন্ড করার জন্য ইনটেন্ট ব্যবহার করেন, তাহলে আপনার অ্যাপটি এক্সপ্লিসিট ইনটেন্ট ব্যবহার করে সুরক্ষিত কিনা তা নিশ্চিত করুন। কোনও পরিষেবা শুরু করার জন্য ইমপ্লিসিট ইনটেন্ট ব্যবহার করা নিরাপত্তার ক্ষেত্রে বিপজ্জনক কারণ আপনি নিশ্চিত হতে পারবেন না যে কোন পরিষেবা ইনটেন্টে সাড়া দেয়, এবং ব্যবহারকারী দেখতে পাবেন না যে কোন পরিষেবা শুরু হয়। Android 5.0 (API লেভেল ২১) থেকে শুরু করে, ইমপ্লিসিট ইনটেন্ট সহbindService()কল করলে, সিস্টেম ব্যতিক্রম দেখায়।- দ্বিতীয় প্যারামিটার হল
ServiceConnectionঅবজেক্ট। - তৃতীয় প্যারামিটার হল একটি ফ্ল্যাগ যা বাইন্ডিংয়ের বিকল্প নির্দেশ করে—সাধারণত
BIND_AUTO_CREATE, যাতে পরিষেবাটি আগে থেকে চালু না থাকলে সেটি তৈরি করা যায়। অন্যান্য সম্ভাব্য ভ্যালু হলBIND_DEBUG_UNBIND,BIND_NOT_FOREGROUND, অথবা কোনওটির জন্য0।
অতিরিক্ত টীকা
কোনও পরিষেবার সাথে বাইন্ডিং করার ব্যাপারে কিছু গুরুত্বপূর্ণ তথ্য এখানে দেওয়া হল:
- কানেকশন বিচ্ছিন্ন হয়ে গেলে, সবসময়
DeadObjectExceptionব্যতিক্রম ট্র্যাপ করুন, যা থ্রো করা হয়। রিমোট মেথডের মাধ্যমে শুধুমাত্র এই ব্যতিক্রমটিই থ্রো করা হয়। - প্রসেস জুড়ে অবজেক্টের রেফারেন্স গণনা করা হয়।
- আপনি সাধারণত ক্লায়েন্টের লাইফসাইকেলের ম্যাচিং ব্রিং-আপ ও টিয়ার-ডাউন মোমেন্টের সময় বাইন্ডিং ও আনবাইন্ডিং পেয়ার করেন, যেমনটি নিম্নলিখিত উদাহরণে বর্ণনা করা হয়েছে:
- আপনার অ্যাক্টিভিটি দেখা যাওয়ার সময় যদি আপনাকে পরিষেবার সাথে ইন্টার্যাক্ট করতে হয়, তাহলে
onStart()-এর সময় বাইন্ড করুন এবংonStop()-এর সময় আনবাইন্ড করুন। - ব্যাকগ্রাউন্ডে বন্ধ থাকা অবস্থাতেও আপনার অ্যাক্টিভিটি যাতে উত্তর পায়, তা চাইলে,
onCreate()-এর সময় বাইন্ড করুন এবংonDestroy()-এর সময় আনবাইন্ড করুন। মনে রাখবেন, এর অর্থ হল আপনার অ্যাক্টিভিটি যতক্ষণ চলছে, ততক্ষণ পরিষেবা ব্যবহার করতে হবে, এমনকি ব্যাকগ্রাউন্ডেও, তাই যখন পরিষেবাটি অন্য কোনও প্রসেসে থাকে, তখন আপনি প্রসেসের ওজন বাড়িয়ে দেন এবং সিস্টেমের মাধ্যমে এটি বন্ধ হওয়ার সম্ভাবনা বেশি থাকে।
মনে রাখবেন: আপনার অ্যাক্টিভিটির
onResume()এবংonPause()কলব্যাকের সময় আপনি সাধারণত বাইন্ড এবং আনবাইন্ড করেন না, কারণ এই কলব্যাকগুলি প্রতিটি লাইফসাইকেল ট্রানজিশনে ঘটে। এইসব ট্রানজিশনে যে প্রসেসিং হয় তা যতটা সম্ভব কম রাখুন।এছাড়াও, আপনার অ্যাপ্লিকেশনের একাধিক অ্যাক্টিভিটি একই পরিষেবার সাথে যুক্ত থাকলে এবং সেই দুটি অ্যাক্টিভিটির মধ্যে ট্রানজিশন হলে, বর্তমান অ্যাক্টিভিটি আনবাইন্ড (পজ করার সময়) হওয়ার কারণে পরিষেবাটি ধ্বংস হয়ে যেতে পারে এবং পরবর্তী অ্যাক্টিভিটি বাইন্ড (আবার চালু করার সময়) হওয়ার আগে আবার তৈরি হতে পারে। অ্যাক্টিভিটি কীভাবে তাদের লাইফসাইকেল কোঅর্ডিনেট করে তার জন্য এই অ্যাক্টিভিটি ট্রানজিশন অ্যাক্টিভিটি লাইফসাইকেল-এ বর্ণনা করা হয়েছে।
- আপনার অ্যাক্টিভিটি দেখা যাওয়ার সময় যদি আপনাকে পরিষেবার সাথে ইন্টার্যাক্ট করতে হয়, তাহলে
কোনও পরিষেবার সাথে কীভাবে বাইন্ড করতে হয় তা দেখানোর জন্য আরও স্যাম্পেল কোড পেতে, ApiDemos-এ
RemoteService.java ক্লাস দেখুন।
বাউন্ড পরিষেবার লাইফসাইকেল ম্যানেজ করা
কোনও পরিষেবা সব ক্লায়েন্ট থেকে আনবাউন্ড করা হলে, Android সিস্টেম সেটি ধ্বংস করে দেয়
(যদি না সেটি
startService() ব্যবহার করে শুরু করা হয়)।
তাই, আপনার পরিষেবা যদি
সম্পূর্ণরূপে বাউন্ড পরিষেবা হয়, তাহলে আপনাকে সেটির লাইফসাইকেল ম্যানেজ করতে হবে না। Android সিস্টেম এটি আপনার জন্য ম্যানেজ করে, এটি কোনও ক্লায়েন্টের সাথে বাউন্ড কিনা তার উপর ভিত্তি করে।
তবে, আপনি যদি onStartCommand() কলব্যাক পদ্ধতি প্রয়োগ করার সিদ্ধান্ত নেন, তাহলে আপনাকে অবশ্যই স্পষ্টভাবে পরিষেবাটি বন্ধ করতে হবে, কারণ
পরিষেবাটি এখন শুরু হয়েছে বলে বিবেচিত হয়। এই ক্ষেত্রে, পরিষেবাটি stopSelf()-এর সাথে নিজে বন্ধ না হওয়া পর্যন্ত বা অন্য কোনও কম্পোনেন্ট stopService()-কে কল না করা পর্যন্ত চলতে থাকে, এটি কোনও ক্লায়েন্টের সাথে আবদ্ধ কিনা তা নির্বিশেষে।
এছাড়াও, আপনার পরিষেবা শুরু হলে এবং বাইন্ডিং গ্রহণ করলে, সিস্টেম আপনার onUnbind() মেথড কল করার সময়,
আপনি ঐচ্ছিকভাবে true রিটার্ন করতে পারেন,
যদি আপনি চান যে পরের বার কোনও ক্লায়েন্ট পরিষেবার সাথে বাইন্ড করলে onRebind()-এ কল আসুক। onRebind() শূন্য রিটার্ন করে, কিন্তু ক্লায়েন্ট এখনও IBinder তার
onServiceConnected() কলব্যাকে পায়।
এই ধরনের লাইফসাইকেলের লজিক নিচের ছবিতে দেখানো হয়েছে।
চালু করা পরিষেবার লাইফসাইকেল সম্পর্কে আরও জানতে, পরিষেবার ওভারভিউ দেখুন।