আহ, ব্লুটুথ। যে প্রযুক্তিটি আমরা সবাই পছন্দ করে ঘৃণা করি। এটি সেই একজন বন্ধুর মতো যিনি সবসময় সংযোগ স্থাপনের চেষ্টা করেন, কিন্তু... পারেন না।
বছর যাবত অ্যান্ড্রয়েড ডেভেলপাররা ব্লুটুথের সাথে একটি নাটकीয়, প্রায় দারুণ, প্রেম সম্পর্কে বাঁধা পড়ে ছিলেন। আমরা এর विचित्रতাগুলোর সাথে লড়েছি, এটি কেবল কাজ করার জন্য অনুনয় করেছি, এবং এর מסתোরিক সংযোগ ড্রপগুলোতে নীরব কান্না বিলেছে।
কিন্তু যদি আমি আপনাকে বলি যে পরিস্থিতি পরিবর্তন হতে যাচ্ছে? যদি আমি বলি যে অ্যান্ড্রয়েড ১৬-এ ব্লুটুথের দেবতারা অবশেষে আমাদের ওপর হেসে উঠেছেন? এটি কোনো স্বপ্ন নয়, বন্ধুরা। এটি AOSP ১৬ ব্লুটুথ স্ক্যানার, এবং এটি আমাদের ক্লান্ত ডেভেলপার আত্মাদের জন্য নতুন আশা নিয়ে এসেছে।
এই হ্যান্ডবুকে, আমরা একটি যাত্রা শুরু করব। AOSP ১৬-এর নতুন ব্লুটুথ ফিচারগুলির হৃদয়ে একটি যাত্রা। আমরা হাসব, কান্না করব (আশা করি এই বার আনন্দের), এবং এই নতুন ক্ষমতাগুলো কীভাবে ভালো কাজে লাগাতে হয় সেটা শিখব। আমরা প্যাসিভ স্ক্যানিংয়ের জাদু, বন্ড লস কারণগুলোর নাটকিয়তা, এবং সার্ভিস UUID গুলো সাধারণ ঝামেলা ছাড়া পাওয়ার সুবিধা অন্বেষণ করব।
এই মহাকাব্যের শেষে, আপনি পারবেন:
একটি ব্লুটুথ স্ক্যানার তৈরি করা যা এতটাই দক্ষ যে, এটি প্রায় জ্ঞানেন্ত্রিক।
একজন অভিজ্ঞ জাসوسের মতো সংযোগ সমস্যা ডিবাগ করা।
আপনার নতুন ব্লুটুথ দক্ষতায় বন্ধু ও সহকর্মীদের প্রভাবিত করা।
পূর্বশর্ত:
গভীরে যাওয়ার আগে, অ্যান্ড্রয়েড ডেভেলপমেন্ট এবং কোটলিনের বেসিক ধারণা থাকা ভালো। যদি আপনি কখনো দুটি ডিভাইসকে একে অপরের সাথে কথা বলতে পারাতে চেয়ে না পারলে এবং কম্পিউটারটি জানালায় বেরিয়ে ফেলতে ইচ্ছা করলে, আপনি চাপলൂসি করতে পারবেন।
তো আপনার প্রিয় পানটি ধরুন, কোডিং ক্যাপ पहনুন, এবং ব্লুটুথ জাগরণের জন্য প্রস্তুত হোন!
সুচিপত্র
অ্যান্ড্রয়েডে ব্লুটুথের সংক্ষিপ্ত ইতিহাস
AOSP ১৬-এ কী নতুন: তিন মুস্কেটিয়ার
গভীর অন্বেষণ ১: প্যাসিভ স্ক্যানিং
BluetoothLeScanner বোঝা
হ্যান্ডস-অন: আপনার প্রথম প্যাসিভ স্ক্যানার তৈরি করা
গভীর অন্বেষণ ২: ব্লুটুথ বন্ড লস কারণ
গভীর অন্বেষণ ৩: বিজ্ঞাপন থেকে সার্ভিস UUID
উন্নত বিষয়: আপনার স্ক্যানিং গেম লেভেল আপ করা
বাস্তব বিশ্বের ব্যবহার ক্ষেত্রে: যেখানে ব্লুটুথ সড়ক ছুঁয়ে
API ভার্সন চেকিং: কীভাবে আপনার অ্যাপ ক্র্যাশ এড়াতে হয়
টেস্টিং এবং ডিবাগিং: মজার অংশ (যে কেউ কখনো বলেনি)
পারফরম্যান্স এবং সেরা অনুশীলন: কীভাবে একজন ভালো ব্লুটুথ নাগরিক হতে হয়
উপসংহার: ভবিষ্যত প্যাসিভ (এবং এটি ঠিক আছে)
ব্লুটুথের সংক্ষিপ্ত ইতিহাস (অথবা: কীভাবে আমরা চিন্তা বন্ধ করে রেডিও তরঙ্গকে ভালোবাসতে শিখলাম)
অন্ধকার যুগ: ক্লাসিক ব্লুটুথ
শুরুতে ছিল ক্লাসিক ব্লুটুথ। এটি একটি কলাহলময়, উচ্ছ্রুখল পার্টি অতিথির ডিজিটেল সমতুল্য। এটি প্রচুর ডেটা বহন করতে পারত (যেমন আপনার প্রিয় গানগুলো স্পিকারর থেকে), কিন্তু এটি ব্যাটারির দারুণ এক ভোক্তা। এটি অডিও স্ট্রিমিংয়ের জন্য চমৎকার, কিন্তু ছোট,_comma_ অপ্রচুর ডেটা ট্রান্সফারের জন্য? এটি হাউসপ্ল্যান্টে পানি দেওয়ার জন্য ফায়ার হোস ব্যবহার করার মতো। অতি, এবং بصراحة, একটু গyclic।
এই যুগের ডেভেলপাররা তাদের দিনগুলো BluetoothAdapter, BluetoothDevice, এবং ভীতিকর BluetoothSocket এর সাথে লড়াইয়ে কাটাত। এটি বড় অনিশ্চিততার যুগ ছিল, যেখানে একটি সাধারণ সংযোগ স্থাপনে সেকেন্ডের পর সেকেন্ড লেগে যেত, অথবা... স Kolkata, বলতে পারুন আপনি এক কাপ কফি বানাতে পারেন। এবং ব্যাটারি ড্রেন? আপনার ব্যবহারকারীরা তাদের ফোনের পাওয়ার লেভেলকে লেড ব্যালুনের চেয়ে দ্রুত পতন করতে দেখত।
পুনর্জাগরণ: ব্লুটুথ লো এনার্জি (BLE) এর আগমন
তারপর, অ্যান্ড্রয়েড ৪.৩-এ, একটি নতুন নায়ক উদিত হয়: ব্লুটুথ লো এনার্জি, বা BLE। এটি আপনার বাবার ব্লুটুথ নয়। BLE ছিল স্লিক, দক্ষ, এবং מסתোরিক। এটি ডেটার ছোট ব্লাস্টের জন্য ডিজাইন করা হয়েছিল, পাওয়ার কাটা ছাড়া সিপ করতো চায়ের মতো।
BLE ছিল ব্লকে কুল কিড। এটি আমাদের একটি সম্পূর্ণ নতুন সম্ভাবনার জগত পরিচয় করিয়েছে: হার্ট-রেট মনিটর, স্মার্ট ওয়াচ, এবং লক্ষ লক্ষ IoT ডিভাইস যা একক কয়েন-সেল ব্যাটারিতে মাস পর মাস চলে। এটি একটি গেম-চেঞ্জার ছিল।
але কিন্তু বড় ক্ষমতার সাথে এসেছিল... বড় জটিলতা। আমাদের GATT, GAP, সার্ভিস, এবং ক্যারেক্টারিস্টিকের একটি সম্পূর্ণ নতুন ভাষা শিখতে হতো। এটি সাধারণ স্ক্রিপ্ট লেখা থেকে পূর্ণ-বযবহার অপেরা রচনায় যাওয়ার মতো ছিল। সম্ভাবনা ব্যাপক ছিল, কিন্তু লার্নিং কার্ভ খুব ঢালু।
সমস্যা বাচ্চা: স্ক্যানিং
তারপর ছিল স্ক্যানিং। এই নতুন, পাওয়ার-সিপিং ডিভাইসগুলো খোঁজার কাজ। BLE-এর শুরুর দিনগুলোতে, স্ক্যানিং এখনও একটু 와ইল্ড 웨স্ট-এর মতো ছিল। এটি একটি সক্রিয়, শোরগুলো প্রক্রিয়া। আপনার ফোন শূন্যে চিৎকার করত, "কারো আছেন?", এবং তারপর জবাবের জন্য শোনত। এটি কাজ করত, কিন্তু এটি এখনও একটি উল্লেখযোগ্য পাওয়ার ড্রেন, বিশেষ করে যদি আপনার অ্যাপটি দীর্ঘ সময় স্ক্যান করতে চায়।
এটি ক্লাসিক ডেভেলপার দ্বন্দ্ব: আপনাকে ডিভাইস খুঁজে বের করতে হয়, কিন্তু আপনি আপনার ব্যবহারকারীর ফোন দुपুরের আগে মরানোর কারণ হতে চান না। বছরের পর বছর আমরা এই রস्सি বাঁকায় হাঁটেছি, আবিষ্কারের প্রয়োজন এবং ব্যাটারি লাইফের নীরব প্রার্থনার মধ্যে ভারসাম্য রক্ষা করে।
এই হল वह বিশ্ব যেখানে AOSP ১৬ জন্ম নিয়েছে। একটি বিশ্ব যা স্ক্যান করার একটি ভালো পথের জন্য চিৎকার করছিল। একটি বিশ্ব যা একটি নায়কের জন্য প্রস্তুত ছিল। এবং সেই নায়ক, বন্ধুরা, হল প্যাসিভ স্ক্যানিং। কিন্তু এর কথা পরে...
AOSP ১৬-এ কী নতুন? (স্পয়লার: এটি আসলে কুল)
ঠিক আছে, আসুন মূল কথায় আসি। AOSP ১৬-এ অ্যান্ড্রয়েড টিম আমাদের কী চমৎকার নতুন প্লে দিয়েছে? মনে হয়, বেশ কিছু! কিন্তু উপহারগুলো খোলার আগে, আসুন নতুন ডেলিভারি শিডিউল সম্পর্কে কথা বলি, কারণ এটাও এখন একটু ভিন্ন।
দুই রিলিজের গল্প
একটি আশ্চর্য প্লট টווিস্টে, অ্যান্ড্রয়েড ২০২৫-এ আমাদের দুটি বড় API রিলিজ দিয়েছে। প্রথমে, আমরা মূল ইভেন্ট পেলাম, অ্যান্ড্রয়েড ১৬ (কোডনেম "বাকলাভা," কারণ কে ভালো পেষ্টри পছন্দ করে না?), যা Q2-এ এসেছিল। এটি আপনার ঐতিহ্যবাহী, বড়-ব্যাং রিলিজ যা আপনি জানেন এবং পছন্দ করেন (বা ভয় করেন) সকল বিহেভিয়র চেঞ্জের সাথে।
কিন্তু তারপর, Q4-এ, আমরা একটি বিস্ময়কর দ্বিতীয় অঙ্ক পেলাম: একটি মাইনর রিলিজ, যা আমাদের নতুন ব্লুটুথ প্লেগুলোর গ্র্যান্ড এন্ট্রি হয়েছিল। এই রিলিজটি নতুন ফিচার এবং API-র সাথে আসে, কোনো ভয়ঙ্কর, অ্যাপ-ব্রেকিং চেঞ্জ ছাড়া। এটি বিল चुकाয়ার পর ফ্রি ডেজার্ট পাওয়ার মতো।
ব্লুটুথের তিন মুস্কেটিয়ার
তো, এই Q4 রিলিজটি ব্লুটুথ পার্টিতে কী নিয়ে এল? খুশি হলাম আপনি জিজ্ঞেস করেছেন। এতে তিনটি নতুন নায়ক এল, যারা আমাদের ব্লুটুথ ভেদন থেকে বাঁচাতে প্রস্তুত। আমি তাদের বলি... তিন মুস্কেটিয়ার।
| ফিচার | সংক্ষিপ্ত | কেন আপনার খেয়াল রাখা উচিত |
|---|---|---|
| প্যাসিভ স্ক্যানিং | চিৎকার না করে ব্লুটুথ ডিভাইস শোনার ক্ষমতা। | আপনার অ্যাপ এখন একটি নীরব, ব্যাটারি-বচত নিনজা হতে পারে। |
| বন্ড লস কারণ | অবশেষে কিছু বন্ধুত্বপূর্ণ কারণ যে আপনার ব্লুটুথ সংযোগ কেন ভাঙে। | আপনি অনুমানের খেলা বন্ধ করে আসলে সংযোগ সমস্যা ডিবাগ করতে পারেন। |
| বিজ্ঞাপন থেকে সার্ভিস UUID | একটি ডিভাইসের ভিতাল সatges সরাসরি তার বিজ্ঞাপন থেকে নিন। | ব্লুটুথ ডিভাইসের জন্য স্পিড ডেটিং। দ্রুত, আরও দক্ষ সংযোগ। |
এই গুলো শুধু ছোট মোট টুইক নয়, বন্ধুরা। এগুলো কোয়ালিটি-অফ-লাইফ উন্নতি যা আমরা কীভাবে ব্লুটুথ-সক্ষম অ্যাপ বিল্ড এবং ডিবাগ করি সেটাকে মূলভূতভাবে পরিবর্তন করবে। মনে হয় অ্যান্ড্রয়েড টিম আসলে আমাদের সমবেত চিৎকার শুনেছে। (আমিও চকিত, আমি জানি।)
পরবর্তী কয়েকটি সেকশনে, আমরা এই নতুন ফিচারগুলির প্রতিটির সাথে ঘনিষ্ঠভাবে পরিচিত হব। আমরা কোডে নিমজ্জন করব, ব্যবহার ক্ষেত্রে অন্বেষণ করব, এবং তাদের শক্তি কীভাবে wykorzyst করতে হয় সেটা শিখব। তো, আমাদের প্রথম মুস্কেটিয়ারের সাথে পরিচয়ের জন্য প্রস্তুত হোন: বলবান, নীরব টাইপ প্যাসিভ স্ক্যানিং।
গভীর অন্বেষণ ১: প্যাসিভ স্ক্যানিং
কল্পনা করুন আপনি একটি লাইব্রেরিতে আছেন। আপনি একজন বন্ধু খুঁজছেন, কিন্তু জানেন না তিনি কোথানে। আপনার দুটি অপশন আছে:
সক্রিয় স্ক্যানিং: আপনি লাইব্রেরির মাঝপথে দাঁড়িয়ে চিৎকার করছেন, "হে, স্টিভ! তুমি কি এখানে আছ?" এটি কার্যকর, কিন্তু এটি কোলাহলময়, ব্যাঘাতক, এবং লাইব্রেরিয়ান (যিনি এই রূপকে আপনার ব্যবহারকারীর ব্যাটারি) আপনাকে বের করে দিবেন।
প্যাসিভ স্ক্যানিং: আপনি লাইব্রেরিতে নীরব হাঁটছেন, আপনার বন্ধুর স্বাদিষ্ট, হাঁফানোর হাসি শোনছেন। আপনি এক শব্দও বলছেন না। শুধু শুনছেন। এটি গোপন, দক্ষ, এবং আপনার সামাজিক (বা বাস্তব) ব্যাটারি ড্রেন করবে না।
বছর ধরে অ্যান্ড্রয়েডের ব্লুটুথ স্ক্যানিং লাইব্রেরিতে চিৎকার করছে সেই লোকটির মতো। কিন্তু AOSP ১৬-এ, আমরা অবশেষে নীরব শোনকার হতে পারছি। এটি প্যাসিভ স্ক্যানিংয়ের জাদু।
সক্রিয় বনাম প্যাসিভ: প্রযুক্তিগত সংগ্রাম
BLE-এর জগতে, ডিভাইসগুলো ছোট ছোট ইনফরমেশন প্যাকেট পাঠায় যাকে "বিজ্ঞাপন" বলা হয়। এটি তাদের উপস্থিতি এবং ক্ষমতা জানানোর পথ।
সক্রিয় স্ক্যানিং: যখন আপনার ফোন একটি সক্রিয় স্ক্যান করে, এটি একটি বিজ্ঞাপন শুনে এবং তারপর একটি SCAN_REQ (স্ক্যান রিকোয়েস্ট) পাঠায় পিছনে। এটি মূলত বলছে, "আমাকে আরও বলো!" পেরিফেরাল ডিভাইসটি তারপর একটি SCAN_RSP (স্ক্যান রেসপন্স) দিয়ে জবাব দেয়, যাতে অতিরিক্ত তথ্য থাকে।
প্যাসিভ স্ক্যানিং: প্যাসিভ স্ক্যানিং-এ, আপনার ফোন বিজ্ঞাপনটি শুনে... এবং এতে শেষ। এটি কিছুই পাঠায় না। এটি শুধু প্রাথমিক বিজ্ঞাপনটি নোট করে এবং চলে যায়। এটি একটি একপক্ষী কথা বলা।
কেন প্যাসিভ? নীরবতার শক্তি
তাহলে এটি এত বড় ডিল কেন? দুটি শব্দ: পাওয়ার কনসাম্পশন। আপনার ফোন রেডিও যখন কিছু ট্রান্সমিট করে (যেমন একটি SCAN_REQ), তখন এনার্জি ব্যবহার করে। যদি আপনার অ্যাপটি সারাদিন ডিভাইস স্ক্যান করে, সেই ছোট ছোট ট্রান্সমিশনগুলো যোগ হয়ে যায়, এবং আপনার ব্যবহারকারীর ব্যাটারি দাম চুকায়।
প্যাসিভ স্ক্যানিং-এ স্যুইচ করে, আপনি রেডিওকে শুধু শোনা বলছেন। কোনো কথা বলা নেই, শুধু শোনা। এটি স্ক্যানের জন্য প্রয়োজনীয় পাওয়ার 劇的に কমানোয়, এটি লম্বা সময় নিকটবর্তী ডিভাইস মনিটর করার দরকারী অ্যাপের জন্য সেরা সমাধান।
কোড: কীভাবে একটি ব্লুটুথ নিনজা হওয়া যায়
তাহলে আমরা এই নতুন স্টীলথ মোডটি কীভাবে ইমপ্লিমেন্ট করব? এটি আশ্চর্যजनকভাবে সহজ। সবকিছু নির্ভর করে আপনি স্ক্যান শুরু করার সময় কোন ScanSettings ব্যবহার করছেন।
পূর্বে, আপনি এভাবে করতে পারতেন:
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
এখন, AOSP ১৬-এ, আমাদের একটি নতুন অপশন আছে। প্যাসিভ স্ক্যানিং সক্ষম করতে, আপনি শুধু স্ক্যান টাইপ সেট করুন:
// এটি জাদুই লাইন!
.setScanMode(ScanSettings.SCAN_TYPE_PASSIVE)
রুকুন, এটা সঠিক নয়। ডকুমেন্টেশন বলে SCAN_TYPE_PASSIVE একটি স্ক্যান টাইপ, স্ক্যান মোড নয়। এবং আপনি সঠিক! আমি একটু বেশি উত্তেজিত হয়েছিলাম। এটি করার সঠিক পথ হলো স্ক্যান মোডকে প্যাসিভ সেট করা। আবার চেষ্টা করি।
val settings = ScanSettings.Builder()
// এটি প্যাসিভের চেয়ে কাছাকাছি
.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC)
.build()
রুকুন, এটাও একদম সঠিক নয়। মনে হয় আমার তারগুলো গুলিয়ে ফেলেছি। অফিসিয়াল ডকুমেন্টেশন দেখি... আহা, পেলাম! ScanSettings.Builder-এ অ্যান্ড্রয়েড ১৬ QPR2-এ একটি নতুন মেথড আছে। এটি setScanMode নয়, এটি একটি সম্পূর্ণ নতুন সেটিং।
এবার একদম সঠিকভাবে করি। প্যাসিভ স্ক্যানিং সক্ষম করার সঠিক পথ:
// অ্যান্ড্রয়েড ১৬ QPR2 এবং পরবর্তীতে উপলব্ধ
val settings = ScanSettings.Builder()
// এটি হয়েছে আসল জাদুই লাইন, আমি প্রতিজ্ঞা করছি!
.setScanType(ScanSettings.SCAN_TYPE_PASSIVE)
.build()
এবং এটা হলো ফলাফল। সেই এক লাইনের সাথে, আপনি আপনার অ্যাপকে একটি কোলাহলময়, ব্যাটারি-খাবো টুরিস্ট থেকে একটি নীরব, দক্ষ ব্লুটুথ নিনজায় রূপান্তর করেছেন। আপনার ব্যবহারকারীদের ব্যাটারি আপনাকে ধন্যবাদ জানাবে।
অবশ্যই, এখানে একটি ট্রেড-অফ আছে। যেহেতু আপনি SCAN_REQ পাঠাচ্ছেন না, আপনি SCAN_RSP থেকে অতিরিক্ত ডেটা পাবেন না। কিন্তু অনেক ব্যবহার ক্ষেত্রে, প্রাথমিক বিজ্ঞাপনই যথেষ্ট। এবং পাওয়ার সেভিং এর তুলনায় এটি সম্পূর্ণ অর্থহীন।
এখন আমরা নীরব স্ক্যানিংয়ের কালা জাদু শিখেছি, চলুন পরবর্তী পাজলটি সমাধান করি: BluetoothLeScanner নিজে বোঝা।
BluetoothLeScanner বোঝা (আমাদের শোটির তারকা)
আমরা ব্লুটুথ স্ক্যানিংয়ের কালা জাদুতে পারদর্শী হতে পারি তার আগে, আমাদের আমাদের প্রাথমিক অস্ত্র বুঝতে হবে: BluetoothLeScanner। এটিকে গোষ্টবাস্টার্স-এর PKE মিটার ধরে নিন। এটি আমাদের অদৃশ্য শক্তি (আমাদের কেসে, BLE বিজ্ঞাপন) সনাক্ত করার টুল। কিন্তু এই ভূত-শিকার গ্যাজেট আসলে কীভাবে কাজ করে?
আর্কিটেকচার: পর্দার পিছনে এক নজর
উচ্চস্তরে, প্রক্রিয়াটি খুব সরল। আপনার অ্যাপ, নিজের ছোট বিশ্বে আরাম থেকে, সিদ্ধান্ত নেয় যে এটা কিছু BLE ডিভাইস খুঁজে বের করতে চায়। এটি BluetoothLeScanner-এর একটি ইনস্ট্যান্স ধরে এবং বলে, "হে, কিছু খুঁজে বের কর।"
হুডের নিচে, অনেক কিছু ঘটছে। BluetoothLeScanner অ্যান্ড্রয়েড ব্লুটুথ স্ট্যাক (কোডনেম "ফ্লোরাইড," যেটি শুনতে হয় আপনার ডেন্টিস্ট খুব গর্বিত হবেন) এর সাথে কথা বলে। স্ট্যাকটি তারপর ডিভাইসের ব্লুটুথ কন্ট্রোলারের সাথে যোগাযোগ করে, প্রকৃত হার্ডওয়্যার যা রেডিও তরঙ্গ পাঠায় এবং গ্রহণ করে। এটি একটি ক্লাসিক কেস "দেখতে সহজ কিন্তু আসলে জটিল।"
অক্ষরের সープ: GATT, GAP, এবং বন্ধুরা
যখন আপনি BLE-এর জগতে যাবেন, আপনি দ্রুত একগাদা অক্ষর সংক্ষেপের সাথে মুখোমুখি হবেন। প্যানিক করবেন না! এগুলো দেখতে যত ভয়ঙ্কর, আসলে তত নয়। সবচেয়ে গুরুত্বপূর্ণ দুটি বুঝতে হলে GAP এবং GATT।
GAP (জেনেরিক এক্সেস প্রোফাইল): এটি সব কিয়া ডিভাইস একে অপরের সাথে কীভাবে আবিষ্কার এবং সংযোগ স্থাপন করে। GAP কে একটি নাইটক্লাবের বাউন্সার ধরে নিন। এটি ডিসাইড করে কে কাদের সাথে কথা বলবে। এটি বিজ্ঞাপন (ডিভাইস চিৎকার করছে "আমি এখানে!") এবং স্ক্যানিং (আপনার অ্যাপ সেই চিৎকার শুনছে) ম্যানেজ করে। আমাদের BluetoothLeScanner GAP-বিশ্বের একটি মূল প্লেয়ার।
GATT (জেনেরিক অ্যাট্রিবিউট প্রোফাইল): একবার দুটি ডিভাইস সংযুক্ত হলে, GATT দায়িত্ব নেয়। এটি সংজ্ঞায়িত করে কীভাবে তারা ডেটা এক্সচেঞ্জ করবে। GAP কে নাইটক্লাবের বাইরের লাইন ধরে নিন, আর GATT কে ভিতরের আসল কথা বলা। এটি সার্ভিস, ক্যারেক্টারিস্টিক, এবং ডেসক্রিপ্টর সম্পর্কে। একটি ডিভাইসের "হার্ট রেট সার্ভিস" থাকতে পারে, যাতে "হার্ট রেট মেজারমেন্ট ক্যারেক্টারিস্টিক" থাকে। আপনার অ্যাপ এই ক্যারেক্টারিস্টিক থেকে পড়ে বা লিখে ডেটা পায়।
স্ক্যানিংয়ের উদ্দেশ্যে, আমরা অধিকাংশ সময় GAP-এর বিশ্বে থাকি। আমরা ক্লাবের বাইরে দাঁড়িয়ে আকর্ষণীয় বিজ্ঞাপন শুনছি।
স্ক্যানিং লাইফসাইকেল: তিন অঙ্কের একটি নাট্য
একটি ব্লুটুথ স্ক্যানের জীবন একটি সরল, তবে anggল, নাট্য।
অঙ্ক ১: প্রস্তুতি। আপনার অ্যাপ সিদ্ধান্ত নেয় স্ক্যান করার। এটি BluetoothLeScanner পায়, ScanFilters (নির্দিষ্ট ডিভাইস খুঁজে বের করার জন্য) এবং ScanSettings (কীভাবে স্ক্যান করব, আমাদের নতুন প্যাসিভ মোডের মতো) তৈরি করে, এবং একটি ScanCallback ডিফাইন করে।
অঙ্ক ২: স্ক্যান। আপনার অ্যাপ startScan() কল করে। ব্লুটুথ রেডিও সজীব হয়ে ওঠে, আপনার ফিল্টারের সাথে মেলে বিজ্ঞাপন শোনতে। যখন একটা পায়, এটি আপনার অ্যাপে আপনার ScanCallback-এ onScanResult() মেথড দিয়ে রিপোর্ট করে।
অঙ্ক ৩: সমাপ্তি। যখন আপনার অ্যাপে এর প্রয়োজন নেই (বা আরও গুরুত্বপূর্ণভাবে, আপনি যা খুঁজছিলেন তা পেয়ে গেলে), এটি stopScan() কল করে। রেডিও পাওয়ার ডাউন হয়, এবং সবকিছু আবার শান্ত হয়ে যায়। আপনি কাজ শেষ হলে সবসময় আপনার স্ক্যান বন্ধ করতে হবে। একটি বেগুনা স্ক্যান হল "আমার ব্যাটারি এক ঘণ্টায় মরে যায়" শিকয়ের নম্বর এক কারণ।
এটোদাও হলো BluetoothLeScanner একটি নাটশেলে। এটি BLE আবিষ্কারের আমাদের গেটওয়ে। এটি শক্তিশালী, জটিল, কিন্তু যেমন আমরা শিখছি, এটি প্রতিটি নতুন অ্যান্ড্রয়েড রিলিজে আরও বুদ্ধিমান এবং দক্ষ হচ্ছে। এখন আমরা আমাদের টুল জানি, চলুন হাত গোড়া করি এবং আমাদের প্রথম প্যাসিভ স্ক্যানার বানাই!
হ্যান্ডস-অন: আপনার প্রথম প্যাসিভ স্ক্যানার বানানো
থিওরি ভালো, কিন্তু সত্যি বলতে, আমরা ডেভেলপার। আমরা করেই শিখি (বা স্ট্যাক ওভারফ্লো থেকে কপি-পেস্ট করেই)। আस्तিন চড়ানোয়, অ্যান্ড্রয়েড স্টুডিও চালু করা, এবং কিছু বানানোর সময়। আমরা একটি সাধারণ অ্যাপ বানাবো যা আমাদের নতুন প্যাসিভ স্ক্যানিং শক্তি ব্যবহার করে নিকটবর্তী BLE ডিভাইস খুঁজে বের করবে।
ধাপ ১: পারমিশন যাচাই
কোনো কোটলিন লাইন লিখার আগে, আমাদের অ্যান্ড্রয়েড পারমিশন দেবতাদের প্রশ্ন করতে হবে। এটি একটি পবিত্র এবং প্রায়শই বাধাদান প্রক্রিয়া। ব্লুটুথ স্ক্যানের জন্য, নিয়মগুলো বছরের পর বছর একটু পরিবর্তন হয়েছে।
প্রথমে, আপনার AndroidManifest.xml খুলুন এবং নিচের موارد যোগ করুন:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- অ্যান্ড্রয়েড ১২ (API ৩১) এবং পরবর্তী জন্য -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- পুরনো ভার্সনের জন্য, লোকেশন পারমিশন দরকার ছিল -->
<!-- পুরনো ডিভাইস সাপোর্ট করলে এখনও এটি দরকার হতে পারে -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
উপরে ঘোষিত পারমিশনগুলো দেখে, আপনি অ্যান্ড্রয়েডের ব্লুটুথ পারমিশন মডেলের বিবর্তন বাস্তব-সময়ে দেখতে পাচ্ছেন।
প্রথম দুটি পারমিশন, BLUETOOTH এবং BLUETOOTH_ADMIN, পুরনো পাহারা। এগুলো অ্যান্ড্রয়েডের sơদিন থেকে আছে এবং বেসিক ব্লুটুথ ফাংশনালিটি এবং ডিভাইস আবিষ্কারের ক্ষমতা দেয়। তারপর আছে BLUETOOTH_SCAN, যা অ্যান্ড্রয়েড ১২ (API ৩১)-এ আনা হয়েছিল এবং গুগল কীভাবে গোপনียতার বিষয়ে চিন্তা করে সেটার একটি বড় পরিবর্তন উপস্থাপন করেছিল।
হ্যাঁ, আপনি সঠিক দেখছেন। ভালো পুরনো দিনগুলোতে (অ্যান্ড্রয়েড ১২-এর আগে), গুগল সিদ্ধান্ত নিয়েছিল যে একটি ব্লুটুথ ডিভাইস খুঁজে বের করা মূলত আপনার ব্যবহারকারীর সঠিক লোকেশন জানার সমান। এটি একটু মানে পড়ত: ভাবুন, যদি আপনি দেখতে পান কোন ব্লুটুথ বিকন নিকটে আছে, আপনি আপনার পজিশন ট্রায়্যাঙ্গুলেট করতে পারেন। কিন্তু একটি সিম্পল ব্লুটুথ স্ক্যানার অ্যাপ লোকেশন চাইলে ব্যবহারকারীরা 당연ভাবেই সন্দেহ করত।
ধন্যবাদ, অ্যান্ড্রয়েড ১২-এ তারা BLUETUOTH_SCAN পারমিশন আনা যেটি আরও যৌক্তিক। এই পারমিশন finalment অ্যাপগুলোকে লোকেশন এক্সেস চাড়া ব্লুটুথ ডিভাইস স্ক্যান করতে দেয়, যা ব্যবহারকারীর দৃষ্টিকোণ থেকে অনেক বেশি মানে পড়ে। আপনাকে এখনও রানটাইমে এই পারমিশন রিকোয়েস্ট করতে হবে, কিন্তু কমপক্ষে আপনাকে আপনার ব্যবহারকারীদের ব্যাখ্যা করতে হবে না যে আপনার সিম্পল গ্যাজেট-ফাইন্ডার অ্যাপ কেন তাদের বাসা জানতে চায়।
তবে, লোকেশন এক্সেসের জন্য সেই শেষ দুটি পারমিশনটি লক্ষ্য করুন। এগুলো পুরনো সিস্টেমের অবশিষ্টাংশ। যদি আপনি এমন একটি অ্যাপ বানান যা পুরনো ডিভাইস (অ্যান্ড্রয়েড ১১ বা তার নিচে) সাপোর্ট করতে হবে, তাহলে আপনাকে ব্যাকওয়ার্ড কম্প্যাটিবিলিটির জন্য আপনার ম্যানিফেস্টে এই লোকেশন পারমিশনগুলো রাখতে হবে। আধুনিক ডিভাইসে, BLUETOOTH_SCAN পারমিশন এককেই কাজ করবে।
ধাপ ২: কোড জাগ उठে
ঠিক আছে, চলুন মজার অংশে যাই। আপনার Activity বা Fragment-এ প্যাসিভ স্ক্যানার ইমপ্লিমেন্ট করার ব্রেকডাউন দিচ্ছি।
স্ক্যানার পাওয়া
প্রথমে, আমাদের BluetoothLeScanner-এর একটি ইনস্ট্যান্স দরকার।
private val bluetoothAdapter: BluetoothAdapter? by lazy {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothManager.adapter
}
private val bleScanner: BluetoothLeScanner? by lazy {
bluetoothAdapter?.bluetoothLeScanner
}
উপরোক্ত কোডে কী হচ্ছে তা ভাঙিয়ে দেখি। আমরা কোটলিনের lazy ডেলিগেশন ব্যবহার করছি, যা একটি ফ্যান্সি পথে বলছে "এই অবজেক্টটি তখন তৈরি করো যখন আমার সত্যিকারের প্রয়োজন হবে।" এটি ভালো প্র্যাকটিস কারণ ব্লুটুথ অ্যাডাপ্টার পাওয়া সিস্টেম কল জুড়ে, এবং এটি করার কোনো मतलब নাই যদি আমরা কখনো এটি ব্যবহার না করি।
প্রথমে, আমরা সিস্টেম সার্ভিস থেকে BluetoothManager ধরি। BluetoothManager কে আপনার ডিভাইসে ব্লুটুথ সংক্রান্ত সব কিছুর গেটকিপার ধরে নিন। এই ম্যানেজার থেকে, আমরা BluetoothAdapter পাই, যা আপনার ডিভাইসের ফিজিক্যাল ব্লুটুথ হার্ডওয়্যারকে রিপ্রেজেন্ট করে। লক্ষ্য করুন আমরা এটি নালেবল (BluetoothAdapter?) ডিক্লেয়ার করেছি কারণ, বিশ্বাস করুন বা না করুন, প্রতিটি অ্যান্ড্রয়েড ডিভাইসে ব্লুটুথ থাকে না। কিছু ট্যাবলেট বা অসামান্য ডিভাইসে হার্ডওয়্যার নাও থাকতে পারে, তাই আমাদের সেই সম্ভাবনার জন্য প্রস্তুত থাকতে হবে।
একবার অ্যাডাপ্টার পেলে, আমরা এটার কাছ থেকে BluetoothLeScanner চাই। এটি वह আসল অবজেক্ট যা আমরা আমাদের স্ক্যান করতে ব্যবহার করব। আবার, আমরা সেফ কল অপারেটর (?.) ব্যবহার করছি কারণ যদি অ্যাডাপ্টার নাল হয় (কোনো ব্লুটুথ হার্ডওয়্যার নেই), আমরা সেটা থেকে কোনো স্ক্যানার পাবো না। এই প্রতিরক্ষামূলক প্রোগ্রামিং প্যারানয়া লাগতে পারে, কিন্তু এটি যে অ্যাপগুলো গোপনে ক্র্যাশ করে এবং যে অ্যাপগুলো গ্রেসফুলি এজ কেস হ্যান্ডেল করে তাদের মধ্যে পার্থক্য সৃষ্টি করে।
কলব্যাক ডিফাইন করা
এখানেই জাদু হয়। ScanCallback হল একটি অবজেক্ট যা স্ক্যান রেজাল্ট শোনবে। আমাদের দুটি মেথড ওভাররাইড করতে হবে: onScanResult এবং onScanFailed।
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
// আমরা একটি ডিভাইস পেলাম!
// 'result' অবজেক্টে ডিভাইস, RSSI, এবং বিজ্ঞাপন ডেটা থাকে।
Log.d("BleScanner", "Found device: ${result.device.address}, RSSI: ${result.rssi}")
}
override fun onScanFailed(errorCode: Int) {
// এটি ব্রহ্মাণ্ডের পথে আপনাকে বিরাম নেওয়া বলছে।
// অথবা কিছু ভয়ঙ্কর ভুল হয়েছে।
Log.e("BleScanner", "Scan failed with error code: $errorCode")
}
}
উপরোক্ত ScanCallback আপনার অ্যাপের ব্লুটুথ জগতে কান। যখন স্ক্যানার একটি ডিভাইস খুঁজে পায়, এটি শুধু ইনফরমেশন কোথাও স্টোর করে রাখে না, এটি এই কলব্যাক অবজেক্টের মাধ্যমে আপনার অ্যাপে সক্রিয়ভাবে কল বে্যাক করে। এটি ক্লাসিক ইভেন্ট-ড্রিভেন প্রোগ্রামিং, এবং এটি হলো অ্যান্ড্রয়েড আপনার অ্যাপকে মেইন থ্রেড ব্লক না করেই রেসপন্সিভ রাখে।
onScanResult মেথডটি প্রতি বারের মত কল হয় যখন স্ক্যানার একটি ডিভাইস আবিষ্কার করে যা আপনার ফিল্টারের সাথে মেলে (বা যেকোনো ডিভাইস যদি আপনি ফিল্টার ব্যবহার না করেন)। result প্যারামিটারটি একটি ইনফরমেশন ভাণ্ডার। এতে থাকে BluetoothDevice অবজেক্ট (যাতে ডিভাইসের MAC অ্যাড্রেস এবং নাম থাকে), RSSI ভ্যালু (Received Signal Strength Indicator – মূলত ডিভাইস কতটুকু নিকট, উচ্চ সংখ্যা মানে নিকট), এবং ডিভাইস যা ব্রডকাস্ট করছে সেই কাঁচা বিজ্ঞাপন ডেটা।
আমাদের সাধারণ উদাহরণে, আমরা শুধু MAC অ্যাড্রেস এবং RSSI লগ করছি, কিন্তু একটি বাস্তব অ্যাপে, আপনি সম্ভবত আপনার UI আপডেট করতে চাবেন, ডিভাইসটি একটি লিস্টে যোগ করতে চাবেন, বা একটি সংযোগ ট্রিগার করতে চাবেন।
callbackType প্যারামিটার আপনাকে বলে কেন এই কলব্যাক ট্রিগার হয়েছে। এটি হতে পারে CALLBACK_TYPE_ALL_MATCHES (ডিফল্ট, মানে "যে সব ডিভাইস পেলাম"), CALLBACK_TYPE_FIRST_MATCH (এই ডিভাইসটি প্রথম بار দেখলাম), বা CALLBACK_TYPE_MATCH_LOST (এই ডিভাইসটি একটা সময় ধরে দেখিনি, তাই সম্ভবত চলে গেছে)। আমরা উন্নত সেকশনে এগুলোর গভীরে যাব।
তারপর আছে onScanFailed, वह মেথড যা আমরা সবাই আশা করি কখনো কল না হোক কিন্তু যা হ্যান্ডেল করতে অবশ্যক। এটি তখনই আহ্বান করা হয় যখন স্ক্যানের সাথে কিছু ভয়ঙ্কর ভুল হয়। হতে পারে ব্লুটুথ অ্যাডাপ্টার স্ক্যানের মাঝপথে বন্ধ হয়ে গেছে, হতে পারে আপনার অ্যাপের সঠিক পারমিশন নেই, অথবা ব্লুটুথ কন্ট্রোলারই খারাপ দিন কাটাচ্ছে। errorCode আপনাকে হিন্ট দেবে কী ভুল হয়েছে, এবং আপনাকে সবসময় এটি লগ করা चाहिये এবং গ্রেসফুলি হ্যান্ডেল করা উচিত – সম্ভবত ব্যবহারকারীকে একটি মেসেজ দেখিয়ে বা একটু ডিলে إثرে স্ক্যান রিস্টার্ট করার চেষ্টা করে।
স্ক্যান কনফিগার করা
এখন, আমরা আমাদের ScanSettings তৈরি করব। এখানেই আমরা অ্যান্ড্রয়েডকে বলব যে আমরা একজন প্যাসিভ, ব্যাটারি-বচত নিনজা হতে চাই।
val scanSettings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER) // ব্যাটারির সাথে ভালো করি
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT) // প্রতিটি অ্যাড একবার রিপোর্ট কর
.setReportDelay(0L) // তৎক্ষণাৎ রিপোর্ট কর
// এবং এখানেই আমাদের শোটির তারকা!
.setScanType(ScanSettings.SCAN_TYPE_PASSIVE)
.build()
উপরোক্ত ScanSettings অবজেক্ট যা আমরা বানাচ্ছি, এটি ব্লুটুথ স্ক্যানারের জন্য একটি বিস্তারিত ইনস্ট্রাকশন ম্যানুয়ালের মতো। প্রতিটি মেথড কল স্ক্যানটি কীভাবে বिहেভ করবে সেটি ফাইন-টিউন করে, এবং এই সেটিংগুলো সঠিক করা হয় একটি ব্যাটারি-বান্ধব অ্যাপ এবং একটি যেটি ঘন্টার মধ্যে আনইনস্টল হয়ে যায় তার মধ্যে পার্থক্য।
প্রতিটি সেটিংয়ের মাধ্যমে যাই। প্রথমে, setScanMode(SCAN_MODE_LOW_POWER) স্ক্যানারকে বলে কম পাওয়ার স্ক্যানিং মোড ব্যবহার করতে, মানে এটি ইন্টারভালে স্ক্যান করবে নন-স্টপ না করে। এটি অধিকাংশ ইউজ কেসের জন্য পারফেক্ট যেখানে আপনাকে ইনস্ট্যান্ট রেজাল্ট দরকার নয় এবং ব্যাটারি লাইফ সংরক্ষণ করতে চান। স্ক্যানার জাগবে, একটু স্ক্যান করবে, ঘুमेंে, এবং পুনরাবৃত্তি করবে। এটি ব্লুটুথের সমতুল্য পাওয়ার ন্যাপ নেওয়া।
পরবর্তী, setCallbackType(CALLBACK_TYPE_ALL_MATCHES) মানে আমরা চাই যখনই স্ক্যানার একটি ম্যাচিং ডিভাইস পাবে তখনই আমাদের জানাবে। এটি ডিফল্ট বিহেভিয়র এবং যা আপনি অধিকাংশ সময় ব্যবহার করবেন। যেমন আগে বললাম, আপনি CALLBACK_TYPE_FIRST_MATCH বা CALLBACK_TYPE_MATCH_LOSTও ব্যবহার করতে পারেন আরও জটিল প্রেজেন্স ডিটেকশনের জন্য।
setMatchMode(MATCH_MODE_AGGRESSIVE) সেটিং কন্ট্রোল করে হার্ডওয়্যার কত আগ্রহীভাবে আপনার ফিল্টারের বিপরীতে ডিভাইস ম্যাচ করার চেষ্টা করবে। MATCH_MODE_AGGRESSIVE মানে "দ্রুত রিপোর্ট কর, এমনকি ১০০% নিশ্চিত না হলেও," আর MATCH_MODE_STICKY মানে "আপনি খুব নিশ্চিত না হওয়া পর্যন্ত অপেক্ষা কর।" আগ্রহী মোড দ্রুত ফলাফল দেয় কিন্তু কখনো কখনো ফালস পজিটিভ দিতে পারে।
তারপর আছে setNumOfMatches(MATCH_NUM_ONE_ADVERTISEMENT), যা স্ক্যানারকে বলে একটি ডিভাইসের শুধু একটি বিজ্ঞাপন শুনলে সেটি রিপোর্ট কর। অল্টার্নেটিভ হলো MATCH_NUM_FEW_ADVERTISEMENT, যা রিপোর্ট করার আগে একাধিক বিজ্ঞাপনের জন্য অপেক্ষা করে। একটু বিজ্ঞাপন দ্রুত আবিষ্কার দেয়, একাধিকের জন্য অপেক্ষা করা মাত্র পাশ দিয়ে যাওয়া ডিভাইস থেকে ফালস পজিটিভ কমায়।
setReportDelay(0L) সেটিংটি অত্যন্ত গুরুত্বপূর্ণ। 0 ডিলে মানে "ফলাফল তৎক্ষণাৎ দাও।" যদি আপনি এটিকে, মনে করুন, 5000 মিলিসেকেন্ডে সেট করেন, স্ক্যানার ফলাফলগুলো ব্যাচ করে রাখবে এবং প্রতি ৫ সেকেন্ডে একবার দিবে। ব্যাচিং ব্যাকগ্রাউন্ড স্ক্যানের জন্য চমৎকার (যেমন আমরা উন্নত সেকশনে আলোচনা করেছি), কিন্তু ফোরগ্রাউন্ড স্ক্যানের জন্য যেখানে ব্যবহারকারী সক্রিয়ভাবে অপেক্ষা করছে, তৎক্ষণাৎ রিপোর্টিংই যা চাই।
এবং এখন, আমাদের শোটির তারকা: setScanType(SCAN_TYPE_PASSIVE)। এটি অ্যান্ড্রয়েড ১৬ QPR2 থেকে নতুন API যা আমাদের স্ক্যানারকে একটি নীরব শোনকারে রূপান্তর করে। এটি প্রতিটি ডিভাইসকে যা শুনে সেটাকে সক্রিয়ভাবে স্ক্যান রিকোয়েস্ট পাঠানোর বদলে, এটি শুধু হাওয়ায় ভেসে যাওয়া বিজ্ঞাপনগুলো শুনে। এই একক সেটিং আপনার অ্যাপের স্ক্যানিং সময়ের ব্যাটারি খরচ 劇적으로 কমানোয়। এটি সেই ফিচারটা যাকে আমরা অপেক্ষা করতাম, এবং এটি গৌরবময়।
এখন যেহেতু আমরা নীরব স্ক্যানিংয়ের কালা জাদু শিখেছি, চলুন পরবর্তী পাজলটুকু বুঝি: BluetoothLeScanner নিজে।
স্ক্যান শুরু এবং বন্ধ করা
শেষে, আমাদের স্ক্যান শুরু এবং বন্ধ করার ফাংশন দরকার। মনে রাখবেন: সবসময় আপনার স্ক্যান বন্ধ করুন! একটি ভুলে গেলে স্ক্যান হলো একটি ব্যাটারি-খাবো মন্স্টার।
private fun startBleScan() {
// প্রথমে পারমিশন রিকোয়েস্ট করা ভুলবেন না!
if (bleScanner != null) {
// আপনি এখানে ScanFilters যোগ করতে পারেন নির্দিষ্ট ডিভাইস খুঁজে বের করার জন্য
val scanFilters: List<ScanFilter> = listOf()
bleScanner.startScan(scanFilters, scanSettings, scanCallback)
Log.d("BleScanner", "Scan started.")
} else {
Log.e("BleScanner", "Bluetooth is not available.")
}
}
private fun stopBleScan() {
if (bleScanner != null) {
bleScanner.stopScan(scanCallback)
Log.d("BleScanner", "Scan stopped.")
}
}
উপরোক্ত এই দুটি ফাংশন আপনার ব্লুটুথ স্ক্যানারের অন/অফ সুইচ, এবং এটি কতটুকু গুরুত্বপূর্ণ তার তুলনায় এগুলো ভুলে যাওয়া সহজ। প্রতিটি কী করছে তা ভাঙিয়ে দেখি।
startBleScan()-এ, আমরা প্রথমে চেক করি bleScanner নাল নয় কিনা। এটি আমাদের সেফটি নেট: যদি ডিভাইসে ব্লুটুথ হার্ডওয়্যার না থাকে বা ব্লুটুথ ডিজেবল থাকে, স্ক্যানার নাল হবে, এবং আমরা নাল অবজেক্টে মেথড কল করে ক্র্যাশ করতে চাই না। যদি স্ক্যানার থাকে, আমরা startScan() তিনটি প্যারামিটার দিয়ে কল করি: একটি ScanFilter অবজেক্টের লিস্ট, আমাদের সতর্কতার সাথে তৈরি ScanSettings, এবং আগে ডিফাইন করা ScanCallback।
উদাহরণে scanFilters লিস্টটি বর্তমানে খালি, মানে "সব BLE ডিভাইস খুঁজো।" একটি বাস্তব অ্যাপে, আপনি সাধারণত এখানে ফিল্টার যোগ করবেন আপনার সার্চ ন্যารো ডাউন করার জন্য।
উদাহরণস্বরূপ, যদি আপনি এমন একটি অ্যাপ বানান যা শুধু হার্ট রেট মনিটর নিয়ে কাজ করে, আপনি একটি ফিল্টার তৈরি করবেন যা শুধু হાર্ট রেট সার্ভিস UUID বিজ্ঞাপন করার ডিভাইস ম্যাচ করবে। এটি পারফরম্যান্স এবং ব্যাটারি লাইফ উভয়ের জন্যই অত্যন্ত গুরুত্বপূর্ণ: যে র্যান্ডম ব্লুটুথ টুথব্রাশের জন্য আপনার অ্যাপ কেন জাগবে যখন আপনি শুধু ফিটনেস ট্র্যাকারের জন্য আগ্রহী?
startScan() মেথডটি স্ক্যানিং প্রক্রিয়া শুরু করে। এই পয়েন্ট থেকে, ব্লুটুথ রেডিও সক্রিয়ভাবে (বা আমাদের কেসে, প্যাসিভভাবে) বিজ্ঞাপন শুনতে থাকে, এবং আপনার scanCallback রেজাল্ট পেতে শুরু করে। এটি একটি অ্যাসিনক্রোনাস অপারেশন, মানে আপনার কোড এখানে রেজাল্টের জন্য ব্লক করে না বরং এক্সিকিউশন চালিয়ে যায়, এবং রেজাল্ট কলব্যাকের মাধ্যমে উপলব্ধ হলে আসে।
এখন stopBleScan() কথা বলি, যা হতে পারে আপনার লেখা সবচেয়ে গুরুত্বপূর্ণ ফাংশন। আপনি যখন stopScan() আপনার কলব্যাক দিয়ে কল করেন, আপনি ব্লুটুথ রেডিওকে বলছেন, "বাস, হয়ে গেলো, এখন তুমি ঘুমাও।" এটি তৎক্ষণাৎ স্ক্যানিং প্রক্রিয়া বন্ধ করে এবং রিসোর্স রিলিজ করে।
গুরুত্বপূর্ণ বিষয় হলো যদি আপনি এটি কল না করেন, স্ক্যানটি অনন্তকাল চলতে থাকবে, আপনার ব্যবহারকারীর ব্যাটারি একটি অল-ইউ-ক্যান-ইট ব্লাড ব্যাংকে ভ্যাম্পায়ারের মতো শোষণ করবে। এইीलिये আমরা এটিকে এত জোর দিয়ে বলি: একটি ভুলে গেলে stopScan() কল ব্লুটুথ অ্যাপে ব্যাটারি ড্রেন কমপ্লেইনের নম্বর এক কারণ।
লক্ষ্য করুন আমরা stopScan()-এ একই scanCallback অবজেক্ট পাস করছি যা আমরা startScan()-এ ব্যবহার করেছিলাম। এটি হলো অ্যান্ড্রয়েডকে জানানোর পথ যে কোন স্ক্যান বন্ধ করতে হবে – আপনি তাত্ত্বিকভাবে একাধিক স্ক্যান চালাতে পারেন ভিন্ন কলব্যাক দিয়ে (যদিও এটি কোনো ভালো আইডিয়া নয়)। সবসময় নিশ্চিত করুন আপনি সেটি বন্ধ করছেন যা আপনি শুরু করেছিলাম একই কলব্যাক রেফারেন্স ব্যবহার করে।
সবকিছু একসাথে করা
এখানে একটি সম্পূর্ণ উদাহরণ যা আপনি একটি Activity-তে ড্রপ করতে পারেন। শুধু মনে রাখবেন রানটাইম পারমিশন হ্যান্ডেল করতে হবে!
// আপনার Activity ক্লাসে
class MainActivity : AppCompatActivity() {
// ... (উপর থেকে lazy প্রপার্টিগুলো অ্যাডাপ্টার এবং স্ক্যানারের জন্য)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ... আপনার UI সেটআপ ...
// উদাহরণ: বাটন ক্লিকে স্ক্যান শুরু
val startButton = findViewById<Button>(R.id.startButton)
startButton.setOnClickListener {
// আপনি অবশ্যই এটি কল করার আগে পারমিশন রিকোয়েস্ট করতে হবে!
startBleScan()
}
// উদাহরণ: অন্য বাটন ক্লিকে স্ক্যান বন্ধ
val stopButton = findViewById<Button>(R.id.stopButton)
stopButton.setOnClickListener {
stopBleScan()
}
}
// ... (উপর থেকে scanCallback, startBleScan, stopBleScan ফাংশনগুলো)
override fun onPause() {
super.onPause()
// সবসময় স্ক্যানিং বন্ধ করুন যখন অ্যাক্টিভিটি ভিজিবল নয়।
stopBleScan()
}
}
উপরোক্ত সম্পূর্ণ উদাহরণ দেখাচ্ছে কীভাবে সকল টুকরো একটি বাস্তব Activity-তে ফিট হয়। এটি একটি মিনিমাল কিন্তু ফাংশনাল ব্লুটুথ স্ক্যানার যা আপনি আসলে রান করতে পারেন। আমরা যে কয়েকটি গুরুত্বপূর্ণ প্যাটার্ন ব্যবহার করছি সেগুলো হাইলাইট করি।
প্রথমে, লক্ষ্য করুন কীভাবে আমরা স্ক্যান লাইফসাইকেলকে বাটন ক্লিকের মাধ্যমে ব্যবহারকারীর অ্যাকশনের সাথে বাঁধিয়েছি। এটি একটি সাধারণ প্যাটার্ন: ব্যবহারকারী স্পষ্টভাবে স্ক্যান শুরু ও বন্ধ করে, যাতে তারা কখন অ্যাপ ব্লুটুথ ব্যবহার করছে সেটা কন্ট্রোল করতে পারে। এটি ভালো UX এবং ব্যাটারি লাইফের জন্যও ভালো, কারণ স্ক্যান তখনই চলে যখন ব্যবহারকারী চায়।
কিন্তু এখানে সত্যিকারের গুরুত্বপূর্ণ অংশ: onPause() ওভাররাইড। এটি একটি ক্রিটিক্যাল সেফটি নেট। যখন আপনার Activity ব্যাকগ্রাউন্ডে চলে যায় (হয়তো ব্যবহারকারী হোম বাটন চাপল, অথবা অন্য অ্যাপে স্যুইচ করল), onPause() কল হয়, এবং আমরা তৎক্ষণাৎ স্ক্যান বন্ধ করি। এটি অত্যন্ত অপরিহার্য কারণ যদি ব্যবহারকারী আপনার অ্যাপ দেখে না, তারা স্ক্যান রেজাল্টের প্রয়োজন নেই, এবং তাদের ব্যাটারি ড্রেন করার কোনো কারণ নেই। এই প্যাটার্ন নিশ্চিত করে যে এমনকি ব্যবহারকারী "বন্ধ করুন" বাটন চাপলেও না, স্ক্যান ব্যাকগ্রাউন্ডে চিরকাল চলবে না।
আপনি ভাবতে পারেন, "onResume() কি? ব্যবহারকারী ফিরে এলে স্ক্যান রিস্টার্ট করা উচিত নাকি?" এটি একটি ডিজাইন ডিসিশন। কিছু অ্যাপে, আপনি onResume()-এ স্বয়ংক্রিয়ভাবে স্ক্যান রিস্টার্ট করতে চাইতে পারেন। অন্যদের, আপনি ব্যবহারকারীকে আবার "শুরু" চাপাতে চাইতে পারেন। এটি আপনার ইউজ কেসের ওপর নির্ভর করে। একটি ডিভাইস-ফাইন্ডিং অ্যাপে যেখানে ব্যবহারকারী সক্রিয়ভাবে খুঁজছে, অটো-রিজিউম মানে পড়ে। একটি মনিটরিং অ্যাপে যা ব্যাকগ্রাউন্ডে চলে, আপনি আরও স্পষ্ট কন্ট্রোল চাইতে পারেন।
এই উদাহরণে আমরা যা দেখিনি সেটি হলো রানটাইম পারমিশন হ্যান্ডলিং। মনে রাখবেন যেসব পারমিশন আমরা ম্যানিফেস্টে ডিক্লেয়ার করেছিলাম? অ্যান্ড্রয়েড ৬.০ এবং পরবর্তীতে, আপনি শুধু ডিক্লেয়ার করে দিতে পারবেন না, আপনাকে ব্যবহারকারীর কাছ থেকে রানটাইমে সেগুলো রিকোয়েস্ট করতে হবে। startBleScan() কল করার আগে, আপনার চেক করতে হবে আপনার কাছে প্রয়োজনীয় পারমিশন আছে কিনা এবং, যদি না থাকে, ActivityCompat.requestPermissions() ব্যবহার করে সেগুলো রিকোয়েস্ট করতে হবে। যদি আপনি সঠিক পারমিশন ছাড়া স্ক্যান শুরু করার চেষ্টা করেন, এটি সাইলেন্টলি (বা লাউডলি, অ্যান্ড্রয়েড ভার্সনের ওপর নির্ভর করে) ফেল করবে, এবং আপনি মাথা খারাপ করবেন কেন কিছুই কাজ করছে না।
তো এটা হলো ফলাফল! আপনি এখন আপনার প্রথম AOSP ১৬ প্যাসিভ ব্লুটুথ স্ক্যানার বানিয়ে ফেলেছেন। এটি লীন, মিন, এবং অবিশ্বাস্যভাবে পাওয়ার-দক্ষ। স্ক্যানার নীরবভাবে BLE বিজ্ঞাপন শোনে, আপনার কলব্যাকের মাধ্যমে সেগুলো রিপোর্ট করে, এবং প্রয়োজন না হলে গ্রেসফুলি বন্ধ হয়ে যায়।
এখন, চলুন আমাদের পরবর্তী টপিকের দিকে যাই: জিনিসগুলো ভুল হলে কী করব। কথা বলি ব্রেকআপের... ব্লুটুথ বন্ড ব্রেকআপের।
গভীর অন্বেষণ ২: ব্লুটুথ বন্ড লস কারণ
আহ, ব্লুটুথ বন্ড। এটি একটি সুন্দর, পবিত্র জিনিস। এটি ডিজিটাল সমতুল্য বন্ধুত্বের ব্যান্ডান বিনিময়। যখন আপনি আপনার ফোনকে হেডফোনের সাথে বন্ড করেন, আপনি একটি দীর্ঘমেয়াদী, বিশ্বস্ত সম্পর্ক তৈরি করছেন। তারা গোপন কী শেয়ার করে, একে অপরের স্মৃতিতে রাখে, এবং প্রতিবার পেয়ারিং করার ঝামেলা বাঁচাতে স্বয়ংক্রিয়ভাবে সংযোগ স্থাপন করার প্রতিশ্রুতি দেয়। এটি একটি সুন্দর রোমান্স।
যতক্ষণ না এটি নয়।
হঠাৎ এক দিন, তারা একে অপরের বিষয়ে... ভুলে যায়। সংযোগ চলে যায়। বিশ্বাস ভাঙে। এবং আপনার অ্যাপ মাঝপথে আটকে যায়, থেরাপিস্টের চেষ্টা করছে, কোনো ধারণা নেই কী ভুল হয়েছে। আপনাকে গোস্ট করা হয়েছে। এবং এপর্যন্ত, অ্যান্ড্রয়েড কোনো সাহায্য করত না। আপনি পেতেন একটি নোটিফিকেশন যে বন্ড স্টেট এখন BOND_NONE, কিন্তু এটাই সব। কোনো ব্যাখ্যা নেই। কোনো ক্লোজার নেই। শুধু একটি ভাঙা সংযোগের ঠান্ডা, কঠোর নীরবতা।
অবশেষে, কিছু ক্লোজার!
কিন্তু আমাদের অ্যান্ড্রয়েড টিমের বন্ধুরা স্পষ্টতো কিছু কঠিন ব্রেকআপের মধ্য দিয়ে এসেছেন, কারণ AOSP ১৬-এ তারা আমাদের ক্লোজারের উপহার দিয়েছে। পরিচিত করাই BluetoothDevice.EXTRA_BOND_LOSS_REASON। এটি একটি নতুন এক্সট্রা যা ACTION_BOND_STATE_CHANGED ব্রডকাস্টের সাথে আসে, এবং এটি আপনাকে বলে কেন বন্ডটি হারানো হয়েছে। এটি একটি ব্রেকআপ টেক্সট পাওয়ার মতো যা আসলে বলে কী ঘটেছে!
এখন, যখন একটি বন্ড ভাঙে, আপনি একটি স্পেসিফিক রিজন কোড পেতে পারেন। ব্লুটুথের জন্য ক্লাসিক ব্রেকআপ ম باعনেগুলোর মতো চিন্তা করুন:
| রিজন কোড (ইলাস্ট্রেটিভ) | আসল অর্থ |
|---|---|
| BOND_LOSS_REASON_BREDR_AUTH_FAILURE | বন্ড লসের কারণ BREDR অথেন্টিকেশন ফেইল। |
| BOND_LOSS_REASON_BREDR_INCOMING_PAIRING | বন্ড লসের কারণ BREDR পেয়ারিং ফেইল। |
| BOND_LOSS_REASON_LE_ENCRYPT_FAILURE | বন্ড লসের কারণ LE এনক্রিপশন ফেইল। |
| BOND_LOSS_REASON_LE_INCOMING_PAIRING | বন্ড লসের কারণ LE পেয়ারিং ফেইল। |
কোড: জাসوس খেলা
তাহলে, আমরা এই রসবগ্স গোসিপ কীভাবে পাব? আমাদের বন্ড স্টেট চেঞ্জ শুনতে একটি BroadcastReceiver সেট আপ করতে হবে।
// বন্ড স্টেট চেঞ্জ শুনতে একটি BroadcastReceiver তৈরি
private val bondStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == BluetoothDevice.ACTION_BOND_STATE_CHANGED) {
val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
val bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR)
val previousBondState = intent.getIntExtra(BluetoothDevice.EXTRA_PREVIOUS_BOND_STATE, BluetoothDevice.ERROR)
// চেক করুন কি আমরা বন্ডেড থেকে নন-বন্ডেড এ গেলাম
if (bondState == BluetoothDevice.BOND_NONE && previousBondState == BluetoothDevice.BOND_BONDED) {
Log.d("BondBreakup", "আমাদের ${device?.address} ডাম্প করল!")
// এখন, আসুন জানি কেন...
val reason = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_LOSS_REASON, -1)
when (reason) {
// নোট: আসল কনস্ট্যান্ট ভ্যালুগুলো Android SDK-তে BluetoothDevice-এ আছে
BluetoothDevice.BOND_LOSS_REASON_REMOTE_DEVICE_REMOVED -> {
Log.d("BondBreakup", "কারণ: রিমোট ডিভাইস বন্ড মুছে ফেলেছে।")
// আপনি ব্যবহারকারীকে মেসেজ দেখাতে পারেন: "আপনার হেডফোন আপনাকে ভুলে গেছে বলে মনে হচ্ছে। দয়া করে আবার পেয়ারিং করার চেষ্টা করুন।"
}
// ... অন্যান্য কারণ হ্যান্ডেল করুন ...
else -> {
Log.d("BondBreakup", "কারণ: এটি জটিল (অজানা রিজন কোড: $reason)")
}
}
}
}
}
}
// আপনার Activity বা Service-এ, রিসিভার রেজিস্টার করুন
override fun onResume() {
super.onResume()
val filter = IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED)
registerReceiver(bondStateReceiver, filter)
}
override fun onPause() {
super.onPause()
// আনরেজিস্টার করা ভুলবেন না!
unregisterReceiver(bondStateReceiver)
}
উপরোক্ত কোডটি ব্লুটুথ বন্ড ব্রেকআপের জন্য একটি জাসوس সিস্টেম ইমপ্লিমেন্ট করে, এবং এটি প্রথম দৃষ্টিতে যা মনে হয় তার চেয়ে আরও জটিল। চলুন এই ব্রডকাস্ট রিসিভার প্যাটার্নটি কীভাবে কাজ করে এবং এটি কতটুকু শক্তিশালী সেটা বুঝি।
প্রথমে, আমরা একটি BroadcastReceiver তৈরি করছি, যা অ্যান্ড্রয়েডের পথ আপনার অ্যাপকে সিস্টেম-ওয়াইড ইভেন্ট শুনতে দেয়। এটাকে একটি নোটিফিকেশন সার্ভিসে সাবস্ক্রাইব করার মতো ধরে নিন, যখনই অ্যান্ড্রয়েড সিস্টেমে কিছু আকর্ষণীয় ঘটে (যেমন একটি বন্ড স্টেট চেঞ্জ), সিস্টেম সমস্ত রেজিস্টার্ড লিসেনারের কাছে একটি "ইন্টেন্ট" ব্রডকাস্ট করে। আমাদের রিসিভার সেই লিসেনারদের একজন।
onReceive() মেথডে, আমরা প্রথমে চেক করি ইনকামিং ইন্টেন্টের অ্যাকশন ACTION_BOND_STATE_CHANGED কিনা। এটি অত্যন্ত গুরুত্বপূর্ণ কারণ ব্রডকাস্ট রিসিভার потенশিয়ালি অনেক টাইপের ইন্টেন্ট পেতে পারে, এবং আমরা শুধু বন্ড স্টেট চেঞ্জের কথা জানতে চাই। একবার এটি সঠিক ইভেন্ট টাইপ নিশ্চিত হলে, আমরা ইন্টেন্ট থেকে প্রাসঙ্গিক ইনফরমেশন এক্সট্রাক্ট করি getParcelableExtra() এবং getIntExtra() ব্যবহার করে।
device অবজেক্টটি বলে এই ইভেন্টটি কোন ব্লুটুথ ডিভাইস সম্পর্কে। যাক, আপনি একাধিক ডিভাইসের সাথে বন্ডেড থাকতে পারেন (আপনার হেডফোন, আপনার স্মার্টওয়াচ, আপনার গাড়ি), এবং আমাদের জানতে হবে কোনটি আমাদের সাথে ব্রেকআপ করল। bondState বলে বর্তমান স্টেট (আমরা কি বন্ডেড, বন্ডিং, অথবা নন-বন্ডেড?), এবং previousBondState বলে এই চেঞ্জের আগে স্টেটটি কী ছিল।
মূল লজিক হয় আমাদের কন্ডিশনাল চেকে: if (bondState == BluetoothDevice.BOND_NONE && previousBondState == BluetoothDevice.BOND_BONDED)। এটি "বন্ডেড" থেকে "নন-বন্ডেড" এই নির্দিষ্ট ট্রানজিশন চেক করছে, যা ব্রেকআপের ডিজিটাল সমতুল্য। আমরা বন্ডিং প্রসেসে আগ্রহী নই (নন থেকে বন্ডিং থেকে বন্ডেড যাওয়া) – আমরা শুধু তখন আগ্রহী যখন একটি বিদ্যমান বন্ড হারানো হয়।
একবার ব্রেকআপ ডিটেক্ট হলে, আমরা ইন্টেন্ট থেকে নতুন EXTRA_BOND_LOSS_REASON এক্সট্রাক্ট করি। এটি AOSP ১৬ থেকে স্টার ফিচার যা অবশেষে আমাদের ক্লোজার দিচ্ছে। রিজন কোডটি বলে কেন বন্ডটি হারানো হয়েছিল – কি রিমোট ডিভাইসই শেষ করল? কি ব্যবহারকারী ম্যানুয়ালি ডিভাইস ফরগেট করল? কি অথেন্টিকেশন ফেল হয়েছিল? প্রতিটি রিজন কোড একটি ভিন্ন সিনেরিওর সাথে মিলে, এবং আপনি প্রতিটিকে যথাযথভাবে হ্যান্ডেল করতে পারেন।
উদাহরণে, আমরা একটি when এক্সপ্রেশন ব্যবহার করছি ভিন্ন রিজন কোড হ্যান্ডেল করার জন্য। BOND_LOSS_REASON_BREDR_INCOMING_PAIRING-এ, আমরা জানি অন্য ডিভাইসই ব্রেকআপ শুরু করল, তাই আমরা একটি হেল্পফুল মেসেজ দেখাতে পারি যেমন "আপনার হেডফোন আপনাকে ভুলে গেছে বলে মনে হচ্ছে। দয়া করে আবার পেয়ারিং করার চেষ্টা করুন।" অন্যান্য কারণের জন্য, আপনি আরও ব্র্যাঞ্চ যোগ করে স펙ফিক্যালি হ্যান্ডেল করবেন।
এখন, নিচের লাইফসাইকেল ম্যানেজমেন্ট লক্ষ্য করুন। আমরা আমাদের রিসিভার onResume()-এ রেজিস্টার করি এবং onPause()-এ আনরেজিস্টার করি। এটি অত্যন্ত গুরুত্বপূর্ণ: আপনি যদি ব্রডকাস্ট রিসিভার আনরেজিস্টার করতে ভুলেন, এটি আপনার Activity ডিস্ট্রয় হওয়ার পরেও ব্রডকাস্ট গ্রহণ করবে, যা মেমরি লিক এবং ক্র্যাশ করতে পারে। onResume()-এ রেজিস্টার এবং onPause()-এ আনরেজিস্টার করার প্যাটার্ন নিশ্চিত করে যে আমরা শুধু তখন বন্ড চেঞ্জ শুনি যখন আমাদের Activity ভিজিবল এবং সক্রিয়।
এটি ডিবাগিং এবং ইউজার এক্সপেরিয়েন্স দুটির জন্যই একটি বিশাল অগ্রগতি। শুধু ব্যবহারকারীকে বলার বদলে "সংযোগ ব্যর্থ," আপনি এখন তাদের বন্ড হারানোর নির্দিষ্ট কারণ অনুযায়ী একশনযোগ্য পরামর্শ দিতে পারেন। এটি একটি সহায়ক, জানালা সম্পর্ক পরামর্শদাতার মতো হচ্ছে বদলে একটি বিভ্রান্ত দর্শকের মত যা শুধু কাঁধ ঝাঝকে "আমি জানি না কী হয়েছে" বলে।
এখন যেহেতু আমরা ব্রেকআপের মানসিক বোঝা নিলাম, চলুন কিছু একটু লাইটহার্টেড onderwerp-এ যাই: ব্লুটুথ ডিভাইসের জন্য স্পিড ডেটিং।
গভীর অন্বেষণ ৩: বিজ্ঞাপন থেকে সার্ভিস UUID
আসুন আপনার অ্যাপের জন্য একটি কম্প্যাটিবল পার্টনার খুঁজে বের করার কথা বলি। BLE-এর জগতে, সব ডিভাইস একসমান নয়। একটি হার্ট রেট মনিটর একটি স্মার্ট লাইটবাল্ব থেকে খুব ভিন্ন। তাহলে আপনার অ্যাপ কীভাবে বুঝবে সেটি সঠিক ধরনের ডিভাইসের সাথে কথা বলছে? উত্তর হলো সার্ভিস UUID।
সার্ভিস UUID কী?
একটি সার্ভিস UUID (Universally Unique Identifier) হলো একটি ডিভাইসের জব টাইটলের মতো। এটি একটি יুনিক, ১২৮-বিট সংখ্যা যা বলে, "আমি একটি ডিভাইস যেটি হার্ট রেট সার্ভিস দেয়" বা "আমি একটি ডিভাইস যেটি ব্যাটারি সার্ভিস দেয়।" এটি নির্ধারণ করার জন্য একমাত্র সবচেয়ে গুরুত্বপূর্ণ তথ্য যে একটি ডিভাইস কী করতে পারে।
পুরনো পদ্ধতি: অকুশল প্রথম ডেট
পরাম্পরাগতভাবে, একটি ডিভাইসের সার্ভিস খুঁজে বের করা একটি পুরো ঝামেলা ছিল। এটি সার্ভিস UUID জানতে তিন-कोরস ডিনার ডেটে যাওয়ার মতো ছিল। প্রক্রিয়াটি এভাবে ছিল:
স্ক্যান: ডিভাইস খুঁজে বের করুন।
সংযোগ: একটি সংযোগ স্থাপন করুন (একটি ধীর এবং পাওয়ার-হাঙ্গরি প্রক্রিয়া)।
সার্ভিস আবিষ্কার: ডিভাইসকে জিজ্ঞেস করুন, "তো... তুমি কী করো?" এবং সার্ভিসের তালিকা পাওয়ার জন্য অপেক্ষা করুন।
মূল্যায়ন: চেক করুন সার্ভিসের তালিকাতে আপনার যা দরকার সেটা আছে কিনা।
ডিসকানেক্ট (বা সংযুক্ত থাকুন): যদি সেটি সঠিক ডিভাইস না হয়, আপনাকে ব্রেকআপ (ডিসকানেক্ট) করে পরেরটাতে যেতে হয়। সময় এবং শক্তির কতটুকু বর্জনা!
এটি অবিশ্বাস্যভাবে অদক্ষ, বিশেষ করে যদি আপনি দर्जনাদর্জন BLE ডিভাইসের মাঝপথে আছেন এবং শুধু এক নির্দিষ্ট টাইপ খুঁজছেন।
নতুন পদ্ধতি: গৌরবশালী নেম ট্যাগ
কত ভালো হত যদি পার্টিতে সবাই তাদের নেম ট্যাগে জব টাইটল লিখে রাখত? AOSP ১৬-এ BluetoothDevice.EXTRA_UUID_LE ঠিক একই জিনিস দিয়েছে। অনেক BLE ডিভাইস ইতিমধ্যে যথেষ্ট শিষ্টাচারী যে তাদের প্রাথমিক সার্ভিস UUID তাদের বিজ্ঞাপন প্যাকেটে অন্তর্ভুক্ত করে। এটি তাদের সারাদিন চিৎকার করার পথ, "আমি একটি হার্ট রেট মনিটর!"
AOSP ১৬-এর আগে, বিজ্ঞাপন প্যাকেট থেকে এই ইনফরমেশন বের করা একটি গন্থন, ম্যানুয়াল প্রসেস ছিল কাঁচা বাইট অ্যারে পার্স করার। এটি করা যেত, কিন্তু এটি সেই ধরনের কোড যেটি আপনি একবার লিখেন, দোয়া করেন কাজ করুক, এবং دوبার ছুঁও না।
এখন, অ্যান্ড্রয়েড গন্ধো কাজটি আমাদের জন্য করে! সিস্টেম স্বয়ংক্রিয়ভাবে বিজ্ঞাপন ডেটা পার্স করে এবং, যদি কোনো সার্ভিস UUID পায়, এটি সুবিধাজনকভাবে আপনাকে ScanResult-এ দিয়ে দেয়।
কোড: নেম ট্যাগ পড়া
এই নতুন ফিচার আমাদের ScanCallback-কে আরও শক্তিশালী করে। আমরা এখন ডিভাইসের জব টাইটল চেক করতে পারি আবিষ্কারের সাথে সাথে, কখনো সংযোগ না করেই।
private val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
Log.d("BleSpeedDating", "Found device: ${result.device.address}")
// আসুন তাদের নেম ট্যাগ চেক করি!
val serviceUuids = result.scanRecord?.serviceUuids
if (serviceUuids.isNullOrEmpty()) {
Log.d("BleSpeedDating", "এইটা তো রহস্যময়। অ্যাডে কোনো সার্ভিস UUID নেই।")
return
}
// যে UUID খুঁজছি সেটি ডিফাইন করুন (যেমন, স্ট্যান্ডার্ড হার্ট রেট সার্ভিস UUID)
val heartRateServiceUuid = ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB")
if (serviceUuids.contains(heartRateServiceUuid)) {
Log.d("BleSpeedDating", "এটা ম্যাচ! এটি একটি হার্ট রেট মনিটর। চলুন সংযোগ করি!")
// এখন আপনি result.device-এ সংযোগ করতে পারেন, জানিয়ে যে এটি সঠিকটি।
stopBleScan() // আমরা যা খুঁজছিলাম পেলাম
// connectToDevice(result.device)
} else {
Log.d("BleSpeedDating", "নहीं মেল। চলুন পরবর্তীটার দিকে।")
}
}
// ... onScanFailed ...
}
উপরোক্ত কোড বিজ্ঞাপন ডেটা থেকে সরাসরি সার্ভিস UUID পড়ার শক্তি প্রদর্শন করে, এবং এটি ডিভাইস আবিষ্কারের জন্য একটি গেম-চেঞ্জার। চলুন ভাঙিয়ে দেখি কী হচ্ছে এবং কেন এটি এতটুকু গুরুত্বপূর্ণ উন্নতি।
যখন আমরা কলব্যাকে একটি স্ক্যান রেজাল্ট পাই, result অবজেক্টে একটি scanRecord প্রপার্টি থাকে। এই স্ক্যান রেকোর্ডটি মূলত সেই কাঁচা বিজ্ঞাপন প্যাকেট যা BLE ডিভাইস হাওয়ায় ব্রডকাস্ট করেছিল।
AOSP ১৬-এর আগে, আপনি যদি এখান থেকে সার্ভিস UUID বের করতে চান, আপনাকে ম্যানুয়ালি বাইট অ্যারে পার্স করতে হতো, BLE বিজ্ঞাপন ফরম্যাট বুঝতে হতো, ভিন্ন ডেটা টাইপ হ্যান্ডেল করতে হতো, এবং দোয়া করতে হতো আপনি অফ-বাই-ওয়ান এরর না করলেন। এটি সেই ধরনের কোড যেটি একবার কাজ করে এবং তারপর আপনি ভয়ে আবার ছুঁবেন না।
এখন, AOSP ১৬-এ উন্নতির সাথে, অ্যান্ড্রয়েড সকল গন্ধো পার্সিং আমাদের জন্য করে। আমরা শুধু result.scanRecord?.serviceUuids কল করে ParcelUuid অবজেক্টের একটি সুন্দর, পরিষ্কার লিস্ট পেতে পারি। সেফ কল অপারেটর (?.) এখানে গুরুত্বপূর্ণ কারণ সব ডিভাইস তাদের রেজাল্টে স্ক্যান রেকর্ড অন্তর্ভুক্ত করে না, এবং আমাদের সেটা গ্রেসফুলি হ্যান্ডেল করতে হবে।
সার্ভিস UUID রিট্রিভ করার পর, আমরা চেক করি লিস্ট নাল বা খালি কিনা। কিছু ডিভাইস তাদের বিজ্ঞাপনে সার্ভিস UUID অন্তর্ভুক্ত করে না। তারা প্রpriteরি ফরম্যাট ব্যবহার করতে পারে, বা খারাপ কনফিগার করা থাকতে পারে। যদি কোনো UUID না থাকে, আমরা একটি মেসেজ লগ করি এবং ফিরি যাই। এগিয়ে যাওয়ার কোনো মানে নেই যদি আমরা চিনতে না পারি ডিভাইসটি কী করে।
পরবর্তী, আমরা যে UUID খুঁজছি সেটি ডিফাইন করি। এই উদাহরণে, আমরা হার্ট রেট মনিটর খুঁজছি, তাই আমরা স্ট্যান্ডার্ড হার্ট রেট সার্ভিস UUID ব্যবহার করছি: 0000180D-0000-1000-8000-00805F9B34FB। এটি ব্লুটুথ SIG (স্পেশাল ইন্ট্রেস্ট গ্রুপ) দ্বারা ডিফাইন করা একটি UUID, এবং যেকোনো কমপ্লায়েন্ট হার্ট রেট মনিটর এই UUID বিজ্ঞাপন করবে। আপনি ব্লুটুথ স্পেসিফিকেশন-এ স্ট্যান্ডার্ড সার্ভিস UUID-এর সম্পূর্ণ তালিকা পেতে পারেন, বা আপনি যদি নিজের BLE পেরিফেরাল বানান তবে কাস্টম UUID ব্যবহার করতে পারেন।
জাদু ঘটে if (serviceUuids.contains(heartRateServiceUuid)) চেকে। এখানেই আমরা আমাদের স্পিড ডেটিং করছি: আমরা ডিভাইসের "নেম ট্যাগ" চেক করছি যে এটি আমাদের যা খুঁজছি সেটার সাথে মেলে কিনা।
যদি মেলে, আমরা আমাদের ম্যাচ পেলাম! আমরা অবিলম্বে স্ক্যানিং বন্ধ করি (কারণ যা পেলাম সেটি পাওয়ার পর কেন খুঁজব?) এবং ডিভাইসের সাথে সংযোগ করার প্রক্রিয়া শুরু করি। আমরা নিশ্চিতভাবে জানি যে এই ডিভাইসটি একটি হার্ট রেট মনিটর, তাই আমরা র্যান্ডম ডিভাইসের সাথে সংযোগ করে ব্যাটারি এবং সময় বর্জনা করব না শুধু যেনে পাওয়ার জন্য যে সেগুলো আমাদের দরকার নয়।
যদি UUID মেলে না, আমরা শুধু "নहीं মেল" লগ করি এবং পরবর্তীটার দিকে যাই। কলব্যাক আবার কল হবে যখন পরবর্তী ডিভাইস পাওয়া যাবে, এবং আমরা এই প্রক্রিয়াটি পুনরাবৃত্তি করব যতক্ষণ না আমরা আমাদের হার্ট রেট মনিটর পাই বা ব্যবহারকারী স্ক্যান বন্ধ করে।
পুরনো পদ্ধতির চেয়ে এটি একটি বিশাল পারফরম্যান্স উন্নতি। আগে, আপনাকে খুঁজে পাওয়া প্রতিটি ডিভাইসের সাথে সংযোগ করতে হতো, সার্ভিস ডিসকভারি করতে হতো (যা ডিভাইসের সাথে একাধিক রাউন্ড-ট্রিপ কমিউনিকেশন জুড়ে), চেক করতে হতো এতে আপনার প্রয়োজনীয় সার্ভিস আছে কিনা, এবং না থাকলে ডিসকানেক্ট করতে হতো। প্রতিটি সংযোগ প্রচেষ্টা সময় নেয়, ব্যাটারি খায়, এবং অপ্রয়োজনীয় রেডিও ট্রাফিক সৃষ্টি করে।
এখন, আপনি স্ক্যানিং стаেজেই লাইটনিং স্পিডে ডিভাইস ফিল্টার ও আইডেন্টিফাই করতে পারেন। কোনো অকুশল প্রথম ডেট নেই যেখানে আপনি একটি স্মার্ট লাইটবাল্বের সাথে সংযোগ করলেন ভাবিয়ে যে এটি হতে পারে একটি ফিটনেস ট্র্যাকার। শুধু দক্ষ, টার্গেটেড সংযোগ।
এটি বিশেষভাবে উপকারী لتلك অ্যাপগুলোর জন্য যেগুলো নিরর্থক ডিভাইসের সমুদ্রে একটি নির্দিষ্ট ধরনের সেন্সর বা পেরিফেরাল খুঁজে বের করতে চায়। কল্পনা করুন আপনি একটি হাসপাতালে আছেন যেখানে শত শত BLE-সক্ষম মেডিক্যাল ডিভাইস, অথবা একটি স্মার্ট হোমে যেখানে দজেন দজেন সেন্সর এবং অ্যাকচুয়েটর। বিজ্ঞাপন থেকে সঠিক ডিভাইসmomentে চিনতে পারা একটি রেসপন্সিভ, প্রফেশনাল অ্যাপ এবং একটি স্লাগিশ, অনিশ্চিত অ্যাপের মধ্যে পার্থক্য।
আমরা এখন আমাদের তিনটি ব্লুটুথ মুস্কেটিয়ারের সাথে পরিচিত হয়েছি: ব্যাটারি দক্ষতার জন্য প্যাসিভ স্ক্যানিং, ভালো ডিবাগিং এর জন্য বন্ড লস কারণ, এবং দ্রুত ডিভাইস আইডেন্টিফিকেশনের জন্য বিজ্ঞাপন থেকে সার্ভিস UUID। কিন্তু আমাদের যাত্রা এখানেই শেষ নয়। এডভান্সড স্ক্যানিং টেকনিকের গভীর বনে যাওয়ার সময়।
উন্নত বিষয়: ফিল্টারিং, ব্যাচিং, এবং অন্যান্য জাদু
ঠিক আছে, আপনি বেসিক্স মাস্টার করেছেন। আপনি প্যাসিভলি স্ক্যান করতে পারেন, আপনার সংযোগ ব্রেকআপের জন্য ক্লোজার পেতে পারেন, এবং ডিভাইস স্পিড-ডেট করতে পারেন একজন প্রোর মতো। আপনি আর ব্লুটুথ পাদওয়ান নন। জেদি মাস্টার হতে পারার সময়।
চলুন ফিল্টারিং, ব্যাচিং, এবং অন্যান্য অপ্টিমাইজেশন জাদুর উন্নত কালায় ডুব যাই যা আপনার অ্যাপকে একজন সত্যিকার ব্যাটারি-বচত চ্যাম্পিয়ন করবে।
হার্ডওয়ার ফিল্টারিং: আপনার পারসোনাল অ্যাসিস্ট্যান্ট
কল্পনা করুন আপনি একটি সেলিব্রিটি, এবং আপনি একটি পারসোনাল অ্যাসিস্ট্যান্ট নিয়েছেন। আপনি চান না প্রতিটি অটোগ্রাফ চাওয়ালোকে নিয়ে বাধা পান। তাই আপনি আপনার অ্যাসিস্ট্যান্টকে একটি লিস্ট দেন: "শুধু আমাকে জানাও যদি তুমি আমার এজেন্ট বা মা কে দেখো।" আপনার অ্যাসিস্ট্যান্ট দরজায় দাঁড়িয়ে থাকে এবং শুধু তখন আপনাকে বাধা দেয় যখন লিস্টের কেউ আসে।
এটিই হলো হার্ডওয়ার ফিল্টারিংয়ের কাজ। আপনার অ্যাপের কোড (সেলিব্রিটি) রেডিও যা দেখে সেটির জন্য প্রতিবার জাগানোর বদলে, আপনি ফিল্টারিং লজিক ব্লুটুথ কন্ট্রোলারের 손에 ছেড়ে দিতে পারেন (পারসোনাল অ্যাসিস্ট্যান্ট)। এটি একটি ফিচার যা অ্যান্ড্রয়েড ৬.০ থেকে আছে, কিন্তু এখন এর চেয়ে বেশি গুরুত্বপূর্ণ।
এটি কেন চমৎকার? কারণ আপনার অ্যাপের কোড ঘুমিয়ে থাকতে পারে। মেইন প্রসেসর (AP) প্রতিবার জাগতে হবে না যখন একটি র্যান্ডম ব্লুটুথ টুথব্রাশ বিজ্ঞাপন করে। ব্লুটুথ কন্ট্রোলার, যা অনেক বেশি পাওয়ার-দক্ষ, ফিল্টারিং হ্যান্ডেল করে। AP শুধু তখন জাগে যখন কন্ট্রোলার আপনার কRITEERIA-র সাথে মেলে একটি ডিভাইস পায়।
কোড: আপনার VIP লিস্ট বানানো
আপনি ScanFilter ব্যবহার করে এটি ইমপ্লিমেন্ট করেন। আপনি ডিভাইসের নাম, এর MAC অ্যাড্রেস, বা সবচেয়ে উপকারী, এডভার্টাইজ করার সার্ভিস UUID দ্বারা ফিল্টার করতে পারেন।
// আমরা শুধু তখন বাধা চাই যখন একটি হার্ট রেট মনিটর দেখি।
val heartRateServiceUuid = ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB")
val filter = ScanFilter.Builder()
.setServiceUuid(heartRateServiceUuid)
.build()
val scanFilters: List<ScanFilter> = listOf(filter)
// এখন, যখন আপনি আপনার স্ক্যান শুরু করবেন, এই লিস্ট পাস করুন
bleScanner.startScan(scanFilters, scanSettings, scanCallback)
উপরোক্ত কোডটি একটি হার্ডওয়ার-লেভেল ফিল্টার তৈরি করার পদ্ধতি দেখাচ্ছে যা ব্যাটারি লাইফ এবং অ্যাপ পারফরম্যান্স দুটোই 劇적으로 উন্নত করে। চলুন গভীরে যাই কী হচ্ছে এবং কেন এটি এতটুকু শক্তিশালী।
আমরা আমাদের আগ্রহী সার্ভিস UUID ডিফাইন করি শুরুর দিকে – এই কেসে, স্ট্যান্ডার্ড হার্ট রেট সার্ভিস UUID। এটি সেটি UUID যা আমরা আগের উদাহরণে ব্যবহার করেছিলাম, কিন্তু এখন আমরা এটিকে căn bảnভাবে ভিন্নভাবে ব্যবহার করছি। আমাদের অ্যাপের কোডে স্ক্যান রেজাল্ট পাওয়ার পর UUID চেক করার বদলে, আমরা ব্লুটুথ হার্ডওয়্যারকেই বলছি শুধু সে ডিভাইসগুলো রিপোর্ট করো যা এই UUID-র সাথে মেলে।
ScanFilter.Builder() হলো এই ফিল্টার বানाने আমাদের টুল। এটি একটি বিল্ডার প্যাটার্ন, মানে আমরা একাধিক মেথড চেইন করে কনফিগার করতে পারি আমরা কী খুঁজছি সেটা স্পষ্টভাবে। এই উদাহরণে, আমরা setServiceUuid(heartRateServiceUuid) কল করছি, যা ফিল্টারকে বলে শুধু সে ডিভাইসগুলো ম্যাচ করো যা এই নির্দিষ্ট সার্ভিস বিজ্ঞাপন করছে।
কিন্তু বিল্ডারের আরও অনেক অপশন আছে যা আপনি ব্যবহার করতে পারেন:
setDeviceName()– নির্দিষ্ট নামের ডিভাইস ম্যাচ করুন (যেমন "My Heart Monitor")setDeviceAddress()– MAC অ্যাড্রেস দিয়ে নির্দিষ্ট ডিভাইস ম্যাচ করুন (উপকারী যদি আপনি ইতিমধ্যে একটি ডিভাইসের সাথে পেয়ার্ড থাকেন এবং আবার সেটি খুঁজতে চান)setManufacturerData()– ম্যানুফ্যাকচারার-স্পেসিফিক ডেটা অনুযায়ী ডিভাইস ম্যাচ করুনsetServiceData()– বিজ্ঞাপনে অন্তর্ভুক্ত সার্ভিস ডেটা অনুযায়ী ম্যাচ করুন
আপনি একই ফিল্টারে একাধিক ক্রাইটেরিয়া কাম্বাইনও করতে পারেন। উদাহরণস্বরূপ, আপনি একটি ফিল্টার তৈরি করতে পারেন যা নির্দিষ্ট সার্ভিস UUID এবং নির্দিষ্ট ম্যানুফ্যাকচারার ID-র সাথে মেলে। আপনার ফিল্টার যত স্পেসিফিক হবে, ফালস পজিটিভ তত কম হবে।
আমাদের ফিল্টার বানানোর পর, আমরা এটিকে ধরে একটি লিস্ট বানাই। লিস্ট কেন? কারণ আপনার একাধিক ফিল্টার হতে পারে, এবং একটি ডিভাইস ম্যাচ করবে যদি এটি লিস্টের যেকোনো একটি ফিল্টারের সাথে সেটিসফাই করে। উদাহরণস্বরূপ, আপনি হার্ট রেট মনিটরের জন্য একটি ফিল্টার এবং ব্লাড প্রেশার মনিটরের জন্য অন্য একটি ফিল্টার তৈরি করতে পারেন, এবং আপনার স্ক্যান যেকোনো একটি ম্যাচ করলে রিপোর্ট করবে। এটি একটি OR অপারেশন: ডিভাইসকে সব ফিল্টারের সাথে ম্যাচ করতে হয় না, শুধু একটি কফি।
শেষে, আমরা এই ফিল্টার লিস্টটি startScan()-এ আমাদের স্ক্যান সেটিংস এবং কলব্যাকের সাথে পাস করি। এখানেই জাদু হয়। আপনি যখন ফিল্টার দেন, অ্যান্ড্রয়েড শুধু আপনার অ্যাপের কোডে রেজাল্ট ফিল্টার করে না। এটি এই ফিল্টারগুলো ব্লুটুথ কন্ট্রোলার হার্ডওয়্যারে পুশ করে নিচে। এর মানে ফিল্টারিং নিম্নতম লেভেলে হয়, আপনার অ্যাপ নোটিফাই হওয়ার আগে।
এখানে কেন এটি এতটুকু শক্তিশালী: ফিল্টার ছাড়া, প্রতিবার ব্লুটুথ রেডিও যেকোনো ডিভাইসের বিজ্ঞাপন শুনলে (আপনার প্রতিবেশির স্মার্ট টোস্টার, কাউকে ফিটনেস ট্র্যাকার পাশ দিয়ে যাওয়া, তিন ঘর দূরে ব্লুটুথ স্পিকার), এটি আপনার অ্যাপের প্রসেস জাগাতে হয়, স্ক্যান রেজাল্ট ডেলিভার করতে হয়, এবং আপনার কোড সিদ্ধান্ত নিতে হয় সেটি এই ডিভাইস নিয়ে চিন্তিত কিনা। প্রতিটি এই হোয়েক-আপ ব্যাটারি এবং প্রসেসিং টাইম খরচ করে।
হার্ডওয়ার ফিল্টারসহ, ব্লুটুথ কন্ট্রোলার সাইলেন্টলি সমস্ত ডিভাইসকে অনুপস্থিত করে যা আপনার ক্রাইটেরিয়ার মেট করে না। আপনার অ্যাপ ঘুমিয়ে থাকে। মেইন প্রসেসর ঘুমিয়ে থাকে। শুধু যখন একটি হার্ট রেট মনিটর ডিটেক্ট হয়, হার্ডওয়্যার আপনার অ্যাপকে জগায় এবং রেজাল্ট ডেলিভার করে। এটি একটি ক্লাবের বাউন্সারের মতো যিনি শুধু VIP লিস্টের লোকেদের ভিতর ঢুকান। বাকিগণ দরজায় থামিয়ে দেয়া হয়, এবং আপনি কখনো জানবেন না তারা আসেছিল।
ScanFilter ব্যবহার করে, আপনি হার্ডওয়্যারকে বলছেন, "আমাকে না জাগাও যদি না তুমি হার্ট রেট মনিটর দেখো।" এটি ব্যাকগ্রাউন্ড স্ক্যানের জন্য চূড়ান্ত পাওয়ার-সেভিং মুভ। প্যাসিভ স্ক্যানিং এবং ব্যাচ রিপোর্টিং এর সাথে কম্বাইন করলে, আপনি একটি ব্লুটুথ স্ক্যানিং সিস্টেম বানাতে পারেন যা ঘন্টা পর ঘন্টা বা দিন পর দিন মিনিমাল ব্যাটারি প্রভাবেই চলে। এটিই পেশাদার-গ্রেড অ্যাপগুলো লং-টার্ম ডিভাইস মনিটরিং হ্যান্ডেল করে ব্যাটারি লাইফ নষ্ট না করে।
ব্যাচ স্ক্যানিং: দৈনিক রিপোর্ট
আমাদের সেলিব্রিটি সাদৃশ্যতায় ফিরে যাই। কখনো কখনো, আপনার মা এসেই তখনই আপনাকে বিঘ্নিত হতে চান না। আপনি দিনের শেষে একটি রিপোর্ট চান: "আজ তোমার মা দুবার এসেছিল, এবং তোমার এজেন্ট একবার কল করল।" এটিই হলো ব্যাচ স্ক্যানিং।
স্ক্যান রেজাল্ট আপনার অ্যাপে রিয়েল-টাইমে ডেলিভার করার বদলে, ব্লুটুথ কন্ট্রোলার সেগুলো কালেক্ট করে এবং একটি বড় ব্যাচে ডেলিভার করে। এটি আরেকটি অসাধারণ পাওয়ার-সেভিং ফিচার। আপনার অ্যাপ লম্বা সময় ঘুমাতে পারে, তারপর জাগে, একসাথে একগাদা রেজাল্ট প্রসেস করে, এবং আবার ঘুমিয়ে যায়।
আপনি আপনার ScanSettings-এ setReportDelay() মেথড দিয়ে এটি সক্ষম করেন।
val scanSettings = ScanSettings.Builder()
// ... অন্যান্য সেটিংস ...
// প্রতি ৫ সেকেন্ডে (৫০০০ মিলিসেকেন্ড) রেজাল্ট ডেলিভার কর
.setReportDelay(5000)
.build()
যখন আপনি একটি রিপোর্ট ডিলে ব্যবহার করেন, আপনার onScanResult কলব্যাক onBatchScanResults দিয়ে রিপ্লেস হয়, যা আপনাকে একটি List<ScanResult> দেয়।
private val scanCallback = object : ScanCallback() {
override fun onBatchScanResults(results: List<ScanResult>) {
Log.d("BatchScanner", "এখানে আপনার দৈনিক রিপোর্ট! ${results.size} টি ডিভাইস পেলাম।")
for (result in results) {
// প্রতিটি রেজাল্ট প্রসেস করুন
}
}
// ... onScanFailed ...
}
উপরোক্ত ব্যাচ স্ক্যানিং মেকানিজম অ্যান্ড্রয়েড ব্লুটুথে সবচেয়ে কম ইউটিলাইজড পাওয়ার-সেভিং ফিচারগুলোর একটি, এবং এটি কীভাবে কাজ করে বুঝলে আপনার অ্যাপের ব্যাটারি প্রোফাইল রূপান্তরিত হতে পারে। চলুন একদম গভীরে যাই हुडের নিচে কী হচ্ছে এবং কখন এটি ব্যবহার করা উচিত।
যখন আপনি কোডে উপরে ৫০০০ মিলিসেকেন্ড (৫ সেকেন্ড) রিপোর্ট ডিলে সেট করেন, আপনি স্ক্যানিং পাইপলাইনটি căn bảnভাবে পরিবর্তন করছেন। ব্লুটুথ কন্ট্রোলার আপনার অ্যাপকে প্রতিবার জগিয়ে দেয় না যখন এটি একটি ডিভাইস দেখে, বরং এটি একটি আস্থাবান অ্যাসিস্ট্যান্টের মতো নোট নেয়। সেই ৫ সেকেন্ডের জন্য, কন্ট্রোলার সাইলেন্টলি প্রতিটি স্ক্যান রেজাল্ট যা সেটি সম্মুখীন হয় সেগুলো কালেক্ট করে, সেগুলো নিজস্ব ইন্টারন্যাল বাফারে স্টোর করে। এই সময় আপনার অ্যাপ সম্পূর্ণ ঘুমিয়ে থাকে – কোনো CPU সাইকেল বর্জনা হয় না, কন্টেক্সট স্যুইচ বা প্রসেস হোয়েক-আপের জন্য কোনো ব্যাটারি ড্রেন নেই।
৫ সেকেন্ডের ডিলে এক্সপায়ার হওয়ার পর, কন্ট্রোলার সমস্ত জমা রেজাল্ট একসাথে আপনার onBatchScanResults() কলব্যাকে ডেলিভার করে। এখান থেকেই পাওয়ার সেভিং আসে: ৫০টি ডিভাইস ডিটেক্ট হলে আপনার অ্যাপ ৫০ বার জাগানোর বদলে, এটি একবার জাগে এবং একসাথে ৫০টি রেজাল্ট পায়। আপনার অ্যাপ তারপর দক্ষতার সাথে এই ব্যাচটি প্রসেস করতে পারে – হয় UI লিস্ট আপডেট করে, ডেটা লগ করে, বা নির্দিষ্ট ডিভাইস চেক করে – এবং তারপর পরবর্তী ব্যাচ വരার জন্য আবার ঘুমিয়ে যায়।
onBatchScanResults()-এ results প্যারামিটারটি একটি List<ScanResult>, এবং লিস্টের প্রতিটি ScanResult ব্যাচিং পিরিয়ডের timpul শোনা একটি একক বিজ্ঞাপনকে রিপ্রেজেন্ট করে। লক্ষ্য করুন যে যদি একই ডিভাইস ডিলে পিরিয়ডে একাধিকবার বিজ্ঞাপন করে, আপনি ব্যাচে সেই ডিভাইসের জন্য একাধিক রেজাল্ট পেতে পারেন। লিস্টটি অটোমেটিক্যালি ডিডুপ্লিকেটেড নয় – আপনার কাজ যদি দরকার হয়।
উদাহরণে, আমরা শুধু পাওয়া ডিভাইসের সংখ্যা লগ করছি এবং তারপর প্রতিটি রেজাল্টের মাধ্যমে ইটেরেট করছি। একটি বাস্তব অ্যাপ্লিকেশনটে, আপনি আরও জটিল প্রসেসিং করতে চাইতে পারেন। উদাহরণস্বরূপ, আপনি MAC অ্যাড্রেস দিয়ে কি-ডিভাইসের ম্যাপ বানাতে পারেন প্রতিটি ডিভাইস কতবার বিজ্ঞাপন করলো ট্র্যাক করার জন্য, গড় RSSI ভ্যালু ক্যালকুলেট করতে পারেন দূরত্ব অনুমান করার জন্য, বা ব্যাচটিকে ফিল্টার করে শুধু সে ডিভাইসগুলো প্রসেস করতে পারেন যা নির্দিষ্ট ক্রাইটেরিয়া মেট করে।
সতর্কতা: ব্যাচ স্ক্যানিং একটি শক্তিশালী টুল, কিন্তু এটি সব পরিস্থিতির জন্য নয়। যদি আপনাকে একটি ডিভাইসের উপস্থিতিতে তৎক্ষণাৎ প্রতিক্রিয়া জানাতে হয় (উদাহরণস্বরূপ, আপনি একটি "ফাইন্ড মাই কীস" অ্যাপ বানাচ্ছেন যেখানে ব্যবহারকারী সক্রিয়ভাবে খুঁজছে), একটি রিপোর্ট ডিলে আপনার বন্ধু নয়। ব্যবহারকারী ৫ সেকেন্ড অপেক্ষা করতে চাবে না রেজাল্ট देखने – তারা ইনস্ট্যান্ট ফিডব্যাক চায়। এই কেসগুলোতে, তৎক্ষণাৎ রিপোর্টিংয়ের জন্য setReportDelay(0) সেট করুন।
কিন্তু লং-টার্ম মনিটরিং বা ডেটা কালেকশন সিনেরিওর জন্য, ব্যাচ স্ক্যানিং ব্যাটারির সেরা বন্ধু। এই ইউজ কেসগুলো বিবেচনা করুন:
ব্যাকগ্রাউন্ড প্রেজেন্স মনিটরিং: আপনার অ্যাপ প্রতি মিনিট চেক করে ব্যবহারকারীর স্মার্টওয়াচ রেঞ্জে আছে কিনা, কিন্তু সেকেন্ড-বাই-সেকেন্ড আপডেট দরকার নেই।
পরিবেশগত সেন্সিং: আপনি একটি বিল্ডিংয়ের সমস্ত তাপমাত্রা সেন্সর থেকে ডেটা কালেক্ট করছেন এবং আপনার ড্যাশবোর্ড প্রতি ৩০ সেকেন্ডে আপডেট করলেই চলে।
বিকন এনালিটিক্স: আপনি একটি রিটেল লোকেশনে মানুষ কতজন পাশ দিয়ে যাচ্ছে সেটা তাদের ফোনের BLE বিজ্ঞাপন দিয়ে ট্র্যাক করছেন, এবং আপনি প্রতি ১০ সেকেন্ডে ডেটা অ্যাগ্রিগেট করছেন।
রিপোর্ট ডিলের সুইট স্পট আপনার ইউজ কেসের ওপর নির্ভর করে। খুব ছোট (যেমন ১ সেকেন্ড), এবং আপনি বেশি বেনিফিট পাচ্ছেন না, আপনি এখনও ঘন ঘন জাগাচ্ছেন। খুব বড় (যেমন ৬০ সেকেন্ড), এবং আপনার অ্যাপ আনরেসপন্সিভ লাগতে পারে বা টাইম-সেনসিটিভ ইভেন্ট মিস করতে পারে। অধিকাংশ ব্যাকগ্রাউন্ড মনিটরিং টাস্কের জন্য, ৫ থেকে ৩০ সেকেন্ডের ডিলে ভালো কাজ করে।
একটা কথা আরও মনে রাখবেন: ব্যাচ স্ক্যানিংয়ের লিমিট আছে। ব্লুটুথ কন্ট্রোলারের স্ক্যান রেজাল্ট স্টোর করার জন্য একটি ফিনিট বাফার থাকে। আপনি যদি খুব লম্বা ডিলে সেট করেন এবং আপনি সैंकড়ों BLE ডিভাইসের মাঝপথে আছেন, বাফার ডিলে এক্সপায়ার হওয়ার আগে ফুল হতে পারে। যখন এটা হয়, পুরনো রেজাল্টগুলো ড্রপ হয়ে যায়। অ্যান্ড্রয়েড আপনাকে এর জন্য কোনো সতর্কতা দেয় না, তাই আপনি যদি ডেটা মিস করতে দেখেন, আপনার রিপোর্ট ডিলে কমাতে বা আরও আগ্রহী ফিল্টার ব্যবহার করতে পারেন রেজাল্ট সংখ্যা কমানোর জন্য।
OnFound/OnLost: উপস্থিতির নাট্য
অ্যান্ড্রয়েড ৮.০ থেকে, স্ক্যানিং আরও নাটকিয় হয়েছে। আপনি এখন হার্ডওয়্যারকে বলতে পারেন শুধু ডিভাইস পাওয়ার সময়ই না, বরং কখন হারালো সেটাও জানাতে। এটি ScanSettings-এ CALLBACK_TYPE_FIRST_MATCH এবং CALLBACK_TYPE_MATCH_LOST ফ্ল্যাগ দিয়ে করা যায়।
val scanSettings = ScanSettings.Builder()
.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH or ScanSettings.CALLBACK_TYPE_MATCH_LOST)
.build()
এখন, আপনার ScanCallback-এ, onScanResult-এ callbackType প্যারামিটার বলবে কী ঘটল।
override fun onScanResult(callbackType: Int, result: ScanResult) {
when (callbackType) {
ScanSettings.CALLBACK_TYPE_FIRST_MATCH -> {
Log.d("PresenceDetector", "পেলাম! ${result.device.address} ভিতরে ঢুকল।")
}
ScanSettings.CALLBACK_TYPE_MATCH_LOST -> {
Log.d("PresenceDetector", "চলে গেল! ${result.device.address} বাইরে পড়ল।")
}
}
}
উপরোক্ত প্রেজেন্স ডিটেকশন মেকানিজম ব্লুটুথ স্ক্যানিং সম্পর্কে আমাদের চিন্তায় একটি মূলভূত পরিবর্তন উপস্থাপন করে। স্ক্যানিংকে "এখন কী দেখছি" এর একটি সतতা রূপে চিন্তা করার বদলে, আমরা এখন ইভেন্টের সাথে কাজ করছি: "এই ডিভাইসটি আয়ত্ত হয়েছে" এবং "এই ডিভাইসটি হারানো হয়েছে।" চলুন গভীরে যাই কীভাবে এটি কাজ করে এবং কেন এটি এতটুকু শক্তিশালী।
আপনি যখন বিটওয়াইজ OR অপারেটর (কোটলিনে or, জাভাতে |) ব্যবহার করে কলব্যাক টাইপ সেট করেন, আপনি ব্লুটুথ হার্ডওয়্যারকে ডিভাইসের প্রেজেন্স স্টেট সময়ের সাথে ট্র্যাক করতে বলছেন। কোড CALLBACK_TYPE_FIRST_MATCH or CALLBACK_TYPE_MATCH_LOST দুটি ফ্ল্যাগ কম্বাইন করে, মানে আপনি চান ডিভাইস প্রথম আপডেট সময় এবং হারানোর সময় দুটোই জানতে। আপনি এই ফ্ল্যাগগুলো আলাদা আলাদাও ব্যবহার করতে পারেন যদি আপনি শুধু একটি টাইপের ইভেন্টে আগ্রহী হন, কিন্তু দুটোই একসাথে ব্যবহার করলে আপনাকে সম্পূর্ণ প্রেজেন্স আওয়ারনেস मिलে।
চলুন বুঝি "ফার্স্ট ম্যাচ" এবং "ম্যাচ লস্ট" আসলে মানে কী। যখন ব্লুটুথ কন্ট্রোলার আপনার ফিল্টারের সাথে মেলে একটি ডিভাইসের বিজ্ঞাপন প্রথমবার শুনে, এটি একটি CALLBACK_TYPE_FIRST_MATCH ইভেন্ট ট্রিগার করে। এটি CALLBACK_TYPE_ALL_MATCHES (ডিফল্ট) থেকে ভিন্ন, যা ডিভাইস যতবার বিজ্ঞাপন করে ততবার ট্রিগার হতো। একটি ডিভাইস প্রতি সেকেন্ডে একাধিকবার বিজ্ঞাপন করতে পারে, তাই পার্থক্যটি উল্লেখযোগ্য। FIRST_MATCH-এ, আপনি এক নোটিফিকেশন পাওয়া যাবেন যখন ডিভাইস আপনার স্ক্যানিং রেঞ্জে ঢুকবে, না कि এটি বিজ্ঞাপন চালানোর সাথে সাথে নোটিফিকেশন এর বाढ़।
CALLBACK_TYPE_MATCH_LOST ইভেন্টটি আরও আকর্ষণীয়। ব্লুটুথ কন্ট্রোলার ট্র্যাক করে রেখে যে সেটি প্রতিটি ডিভাইস থেকে কখন শুনেছিলো। যদি একটি ডিভাইস বিজ্ঞাপন করা বন্ধ করদে (কারণ এটি রেঞ্জের বাইরে চলে গেল, বন্ধ হয়ে গেল, বা এর ব্যাটারি মরে গেল), কন্ট্রোলার অনুপস্থিতি লক্ষ্য করে এবং একটি MATCH_LOST ইভেন্ট ট্রিগার করে। এটি অটোমেটিক্যালি হয়: আপনাকে আপনার অ্যাপে ম্যানুয়ালি টাইমস্ট্যাম্প ট্র্যাক করতে বা টাইমআউট লজিক ইমপ্লিমেন্ট করতে হবে না। হার্ডওয়্যার আপনার জন্য করে দিচ্ছে।
কিন্তু হার্ডওয়্যার কীভাবে জানে একটি ডিভাইস "হারানো হয়েছে"? এটি একটি ইন্টারন্যাল টাইমআউট ব্যবহার করে। যদি কন্ট্রোলার কিছু নির্দিষ্ট সময় (সাধারণত কয়েক সেকেন্ড, যদিও সঠিক ডিউরেশন ইমপ্লিমেন্টেশন-ডিপেন্ডেন্ট এবং অ্যাপগুলোর জন্য এক্সপোজড নয়) ধরে একটি ডিভাইস থেকে কিছু না শুনে, এটি ডিভাইসকে হারানো ধরে নেয়। এর মানে ডিভাইস আসলে রেঞ্জ ছাড়ার সময় এবং আপনি MATCH_LOST কলব্যাক পাওয়ার সময়ের মধ্যে একটি ছোট ডিলে থাকে, কিন্তু এই ডিলে সাধারণত প্রেজেন্স ডিটেকশন ইউজ কেসের জন্য গ্রহণযোগ্য।
উপরোক্ত কোড উদাহরণে, আমরা একটি when এক্সপ্রেশন ব্যবহার করছি ভিন্ন কলব্যাক টাইপ হ্যান্ডেল করার জন্য। যখন আমরা একটি FIRST_MATCH পাই, আমরা জানি ডিভাইসটি আমাদের স্ক্যানিং রেঞ্জে এ’daাখিল করেছে, তাই আমরা "পেলাম!" লগ করি। এটি আক্রমণ ট্রিগার করার জন্য পারফেক্ট যেমন আপনার ফোন নিকট আসলে দরজা আনলক করা, বা ডেটা সিঙ্ক শুরু করা যখন আপনার ফিটনেস ট্র্যাকার ডিটেক্ট হয়।
যখন আমরা একটি MATCH_LOST পাই, আমরা জানি ডিভাইসটি আমাদের স্ক্যানিং রেঞ্জ ছাড়িয়ে গেছে বা বিজ্ঞাপন বন্ধ করল, তাই আমরা "চলে গেল!" লগ করি। এটি ক্লিন-আপ অ্যাকশন ট্রিগার করার জন্য আইডিয়াল যেমন আপনার ফোন চলে গেলে দরজা লক করা, বা ট্র্যাকার ডিসকানেক্ট হলে ডেটা সিঙ্ক বন্ধ করা।
এটি প্রেজেন্স ডিটেকশন সিনেরিওর জন্য অবিশ্বাস্যভাবে উপকারী। আপনার স্মার্ট লক রেঞ্জে আছে? আপনার ফিটনেস ট্র্যাকার এখনো সংযুক্ত? ব্যবহারকারীর ফোন নিকটে আছে? এখন আপনি জানতে পারেন, হার্ডওয়্যার-লেভেল নিশ্চিততার সাথে, এবং আপনি প্রেজেন্স চেঞ্জের সাথে রিয়্যাক্ট করতে পারেন বিনা কনস্ট্যান্টলি পোলিং বা আপনার অ্যাপ কোডে জটিল স্টেট মেশিন মেইনটেইন কिये।
এখানে একটি প্র্যাকটিক্যাল উদাহরণ কীভাবে আপনি এটি একটি স্মার্ট হোম অ্যাপে ব্যবহার করতে পারেন:
private val presenceCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
when (callbackType) {
ScanSettings.CALLBACK_TYPE_FIRST_MATCH -> {
// ব্যবহারকারীর ফোন ডিটেক্ট হয়েছে - তারা বাড়িতে আছেন!
Log.d("SmartHome", "বাড়িতে স্বাগতম! দরজা আনলক করছি এবং লাইট অন করছি।")
unlockFrontDoor()
turnOnLights()
adjustThermostat(COMFORTABLE_TEMP)
}
ScanSettings.CALLBACK_TYPE_MATCH_LOST -> {
// ব্যবহারকারীর ফোন চলে গেল - তারা বের হয়ে গেল!
Log.d("SmartHome", "বিদায়! দরজা লক করছি এবং অওয়ে মোডে যাচ্ছি।")
lockFrontDoor()
turnOffLights()
adjustThermostat(ENERGY_SAVING_TEMP)
armSecuritySystem()
}
}
}
override fun onScanFailed(errorCode: Int) {
Log.e("SmartHome", "প্রেজেন্স ডিটেকশন ব্যর্থ: $errorCode")
}
}
একটি গুরুত্বপূর্ণ বিবেচনা: FIRST_MATCH এবং MATCH_LOST হলো CALLBACK_TYPE_ALL_MATCHES এর সাথে মিউচুয়ালি এক্সক্লুসিভ। আপনি যদি সেগুলো ALL_MATCHES এর সাথে কম্বাইন করেন, বিহেভিয়র অনডিফাইনড হয়ে যায় এবং ডিভাইস অনুযায়ী ভিন্ন হয়। বা তো ALL_MATCHES ব্যবহার করুন কন্টিনিউয়াস রিপোর্টিংয়ের জন্য, অথবা FIRST_MATCH/MATCH_LOST প্রেজেন্স ডিটেকশনের জন্য – দুটোই একসাথে ব্যবহার করার চেষ্টা করবেন না।
এছাড়া জানান থাকুন যে প্রেজেন্স ডিটেকশন সবচেয়ে ভালো কাজ করে যখন হার্ডওয়ার ফিল্টারিং-এর সাথে কম্বাইন করা হয়। যদি আপনি ফিল্টার ছাড়া সব ডিভাইস স্ক্যান করেন, কন্ট্রোলারকে রেঞ্জের প্রতিটি একক BLE ডিভাইসের প্রেজেন্স স্টেট ট্র্যাক করতে হবে, যা এর ইন্টারন্যাল ট্র্যাকিং টেবিলকে ওভারওয়েল্ম করতে পারে। প্রেজেন্স ডিটেকশন ব্যবহার করার সময় সবসময় ScanFilter ব্যবহার করুন যাতে আপনি কোন ডিভাইসগুলোর চিন্তা করছেন সেটা ন্যারো ডাউন করতে পারেন।
এই উন্নত টেকনিকগুলো – হার্ডওয়ার ফিল্টারিং, ব্যাচ স্ক্যানিং, এবং প্রেজেন্স ডিটেকশন – কম্বাইন করে, আপনি অবিশ্বাস্যভাবে জটিল এবং পাওয়ার-দক্ষ ব্লুটুথ অ্যাপ্লিকেশন বানাতে পারেন। আপনি আর শুধু একজন ডেভেলপার নন। আপনি একজন ব্লুটুথ উইজার্ড, যিনি তাদের চারপাশের পরিবেশ সম্পর্কে আওয়্যার অ্যাপ, পরিবর্তনের সাথে রেসপন্সিভ, এবং ব্যাটারি লাইফের প্রতি সম্মানীয় বানাতে পারেন।
এখন, চলুন দেখি আমরা এই জাদুকরি শক্তি বাস্তব বিশ্বে কোথায় প্রয়োগ করতে পারি।
বাস্তব বিশ্বের ব্যবহার ক্ষেত্রে: যেখানে ব্লুটুথ সড়ক ছুঁয়ে
ঠিক আছে, আমরা.one কয়েকটি কুল নতুন ট্রিক শিখলাম। আমরা ব্লুটুথ ব্ল্যাক বেল্ট হয়ে গেছি। কিন্তু এই সব শক্তি কোনো ভালো কাজে (অথবা কমপক্ষে একটি কুল অ্যাপে) লাগানো না হলে কী ফায়দা? চলুন কিছু বাস্তব সিনেরিও এক্সপ্লোর করি যেখানে AOSP ১৬-এ নতুন ফিচারগুলো একটি ভালো অ্যাপকে একটি দারুণ অ্যাপে রূপান্তরিত করতে পারে।
১. "ফাইন্ড মাই এवरीথিং" অ্যাপ
আমরা সবাই সেখানে আছি। আপনি অফিসের জন্য লেট হয়েছেন, এবং আপনার চাবিগুচ্ছ অন্য একটি ডাইমেনশনে লুকানো খেলা শুরু করেছে। এটি BLE ট্র্যাকারের জন্য ক্লাসিক ইউজ কেস।
পুরনো পদ্ধতি: আপনার অ্যাপটি लगাতার সক্রিয় স্ক্যান করত, আপনি উদ্দেশ্যভরে খুঁজছেন তখন ব্যাটারি ড্রেন করত। এটি আপনার বাসার প্রতিটি ট্র্যাকারের সাথে সংযোগ করত শুধু দেখতে যে সেটি সঠিকটি কিনা।
AOSP ১৬ পদ্ধতি: আপনার অ্যাপ একটি প্যাসিভ স্ক্যান ব্যাকগ্রাউন্ডে চলে আপনার ট্র্যাকারের স্পেসিফিক সার্ভিস UUID-র সাথে হার্ডওয়ার ফিল্টার দিয়ে। ব্যাটারি প্রভাব ন্যূনতম। যখন আপনি অ্যাপ খুলেন চাবি খুঁজতে, এটি ইতিমধ্যে জানে চাবিগুলো বাড়িতে আছে কারণ এটি নীরবভাবে শোনছিল। আপনি "ফাইন্ড" বাটন চাপেন, অ্যাপ সংযোগ করে, এবং আপনার চাবি সোফার কুশনের ভিতর থেকে চিৎকার করতে শুরু করে। এবং যদি সংযোগ ব্যর্থ হয়? বন্ড লস রিজন বলে দেবে ট্র্যাকারের ব্যাটারি মরেছে কিনা, যাতে আপনি একটি মরেছে ডিভাইস খুঁজতে না বসে।
২. স্মার্ট সুপারмар্কেট
একটি অ্যাপ কল্পনা করুন যা আপনাকে কুপন দেয় প্রোডাক্টের পাশ দিয়ে যাওয়ার সময়। এটি প্রক্সিমিটি মার্কেটিংয়ের স্বপ্ন, একটি স্বপ্ন যা ঐতিহাসিকভাবে ব্যাটারি ড্রেনের কারণে ভেঙে গেছে।
পুরনো পদ্ধতি: অ্যাপটিকে কনস্ট্যান্টলি বিকন স্ক্যান করতে হতো, ব্যবহারকারীর ফোনকে একটি গরম আলু এবং চেকআউট লাইন পৌঁছানোর আগে মরা ব্যাটারি বানিয়ে দিত।
AOSP ১৬ পদ্ধতি: সুপারмар্কেট প্রতিটি আইলএ BLE বিকন রাখে। আপনার অ্যাপ একটি প্যাসিভ, ব্যাচড স্ক্যান ব্যবহার করে। এটি প্রতি মিনিটে একবার জাগে, এটি যে সব বিকন দেখেছে তার লিস্ট পায়, এবং আবার ঘুমিয়ে যায়। যখন এটি দেখে আপনি কুকি আইলএ পাঁচ মিনিট ধরে লুকাচুড়ি করছেন (এটি জানে, এটি সবসময় জানে), এটি বিজ্ঞাপন থেকে সার্ভিস UUID ব্যবহার করে "কুকি আইল বিকন" আइডেন্টিফাই করে এবং আপনাকে অরিওর কুপন পাঠায়। এটি টার্গেটেড, দক্ষ, এবং আপনি বিল পেয়ার আগে আপনার ব্যাটারি মেরে দেয় না।
৩. অত্যধিক আটকে যাওয়া স্মার্ট হোম
আপনার স্মার্ট হোম হওয়া উচিত, হা, স্মার্ট। এটিকে জানা উচিত আপনি কখন বাড়িতে আছেন এবং কখন বের হয়ে গেলেন। এটিকে আপনার পেছনে দরজা লক করা উচিত এবং আপনি আসলে লাইট অন করা উচিত।
পুরনো পদ্ধতি: আপনাকে GPS (একটি 악명 높은 ব্যাটারি হগ) বা ওয়াই-ফাই সংযোগের ওপর নির্ভর করতে হতো, যা অনিশ্চিত হতে পারে। BLE একটি অপশন ছিল, কিন্তু কনস্ট্যান্ট স্ক্যানিং একটি সমস্যা।
AOSP ১৬ পদ্ধতি: আপনার ফোন হলো চাবি। আপনার স্মার্ট হাব (সেন্ট্রাল ডিভাইস হিসেবে কাজ করে) একটি কন্টিনিউয়াস, লো-পাওয়ার প্যাসিভ স্ক্যান চলে। যখন এটি আপনার ফোনের BLE বিজ্ঞাপন দেখে, এটি জানে আপনি বাড়িতে আছেন। কিন্তু যদি আপনি শুধু বাড়ির পাশ দিয়ে চলে যান? এখানেই OnFound/OnLost ফিচার কাজে লাগে। হাব কনফিগার করা যেতে পারে "স্বাগতম বাড়ি" সিকোয়েন্স শুধু তখন ট্রিগার করবে যখন এটি আপনার ডিভাইসটি এক মিনিট ধরে কনসিস্টেন্টলি দেখে (OnFound), এবং "বিদায়" সিকোয়েন্স শুধু তখন ট্রিগার করবে যখন এটি আপনাকে পাঁচ মিনিট ধরে না দেখে (OnLost)। এটি একটি স্মার্টার, আরও নির্ভরযোগ্য প্রেজেন্স ডিটেকশন সিস্টেম যা অবশেষে স্মার্ট হোমকে... স্মার্ট বানিয়ে দেয়।
৪. করপোরেট অ্যাসেট ট্র্যাকার
একটি বড় হাসপাতাল বা ওয়্যারহাউসে, ব্যয়বহুল, মোবাইল ইকুইপমেন্ট (যেমন IV পাম্প বা ফর্কলিফট) ট্র্যাক রাখা একটি বিশাল চ্যালেঞ্জ। BLE ট্যাগগুলো সমাধান।
পুরনো পদ্ধতি: কর্মীদের ট্যাবলেট নিয়ে ঘুরতে হতো, ইনভেন্টরি लेने সক্রিয় স্ক্যান করলে। এটি ধীর, ম্যানুয়াল, এবং অদক্ষ।
AOSP ১৬ পদ্ধতি: পুরো বিল্ডিংয়ে ফিক্সড BLE গেটওয়ে নেটওয়ার্ক ইনস্টল করা হয়। প্রতিটি গেটওয়ে একটি সিম্পল ডিভাইস (যেমন একটি রাস্পবেরি পাই) যা একটি কন্টিনিউয়াস প্যাসিভ স্ক্যান চলে। সেগুলো অ্যাসেট ট্যাগ থেকে সব বিজ্ঞাপন ডেটা কালেক্ট করে এবং একটি সেন্ট্রাল সার্ভারে পাঠায়। সার্ভার এখন রিয়েল-টাইমে দেখতে পাচ্ছে IV পাম্প #৩৪ রুম ২০১-এ, এবং ফর্কলিফট #৩ বর্তমানে লোডিং বে-এ। কোনো ম্যানুয়াল স্ক্যানিং দরকার নেই। প্যাসিভ স্ক্যানিংয়ের দক্ষতার কারণে এটি একটি লো-কস্ট, লো-পাওয়ার, রিয়েল-টাইম লোকেশন সিস্টেম।
এই গুলো শুধু কয়েকটি উদাহরণ। ফিটনেস ট্র্যাকার থেকে ইন্ডাস্ট্রিয়াল সেন্সর পর্যন্ত, AOSP ১৬-এ নতুন ব্লুটুথ ফিচারগুলো এমন অ্যাপ বানানোর সম্ভাবনার জগত খুলে দিয়েছে যা শুধু শক্তিশালী নয় বরং আপনার ব্যবহারকারীর ব্যাটারির প্রতি বিনয়ীও। এখন, আসুন কথা বলি কীভাবে নিশ্চিত করবো আমাদের চমৎকার নতুন অ্যাপটি সব ডিভাইসে কাজ করে, শুধু নতুনগুলোতে না।
API ভার্সন চেকিং: কীভাবে আপনার অ্যাপ ক্র্যাশ এড়াতে হয়
তো, আপনি AOSP ১৬-এর Q4 রিলিজ থেকে সব নতুন হটনেস ব্যবহার করে একটি সুন্দর, ব্যাটারি-সিপিং অ্যাপ বানালেন। আপনি এটিকে শিপ করার জন্য প্রস্তুত, করোড়পতি হওয়ার জন্য, এবং একটি প্রাইভেট দ্বীপে রিটায়ার होने। কিন্তু তারপর একটি বাগ রিপোর্ট আসে। আপনার অ্যাপ একটি ব্র্যান্ড নিউ অ্যান্ড্রয়েড ১৬ ডিভাইসে ক্র্যাশ করছে। কী হয়েছে?! স্বাগতম, বন্ধু, চমৎকার API ভার্সন চেকিংয়ের বিশ্বে। অ্যান্ড্রয়েডের নতুন রিলিজ শিডিউলের সাথে, এটি আরো গুরুত্বপূর্ণ (এবং একটু আরও জটিল) হয়ে উঠেছে।
সমস্যা: দুটি অ্যান্ড্রয়েড ১৬ের গল্প
যেমন আমরা আলোচনা করেছি, ২০২৫ আমাদের দুটি অ্যান্ড্রয়েড ১৬ রিলিজ দিয়েছে:
Q2 রিলিজ: মূল "বাকলাভা" রিলিজ। এটিকে API লেভেল ৩৬.০ বলি।
Q4 রিলিজ: মাইনর, ফিচার-ড্রপ রিলিজ। এখানেই আমাদের নতুন ব্লুটুথ প্লেগুলো বাস করে। এটিকে API লেভেল ৩৬.১ বলি।
আমাদের নতুন প্যাসিভ স্ক্যানিং API, setScanType(), শুধু ৩৬.১ এবং পরবর্তীতে আছে। আপনি যদি একটি ডিভাইসে যা ইনিশিয়াল Q2 রিলিজ (৩৬.০) রান করছে এটিকে কল করার চেষ্টা করেন, আপনার অ্যাপ NoSuchMethodError দিয়ে ক্র্যাশ করবে। এটি ডিজিটাল সমতুল্য একটি মেনু আইটেম চাওয়ার যা গত রাতই যোগ হয়েছে। শেফ (আপনার অ্যাপ) শুধু বিভ্রান্ত হয়ে মেল্টডাউন করে।
পুরনো পাহারা: SDK_INT
বছর ধরে API লেভেল চেক করার জন্য আমাদের বিশ্বস্ত বন্ধু হয়েছে Build.VERSION.SDK_INT। এটি সহজ এবং কার্যকর।
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// অ্যান্ড্রয়েড ১২ (S) বা তার উপরের API ব্যবহার করুন
}
কিন্তু SDK_INT শুধু মেজর রিলিজ জানে। অ্যান্ড্রয়েড ১৬ Q2 এবং Q4 দুটোর জন্যই, SDK_INT শুধু ৩৬ রিপোর্ট করবে। এতে মাইনর ভার্সনের কোনো ধারণা নেই। এটি কারো বয়স জিজ্ঞেস করার মতো, এবং সে শুধু বলল "ত্রিশের দশকে।" খুব স্পেসিফিক নয়।
নতুন হটনেস: SDK_INT_FULL
এই সমস্যা সমাধান করার জন্য, অ্যান্ড্রয়েড টিম আমাদের একটি নতুন, আরও প্রিসাইজ টুল দিয়েছে: Build.VERSION.SDK_INT_FULL। এই কনস্ট্যান্ট মেজর এবং মাইনর ভার্সন নম্বর দুটোই জানে। এবং এর সাথে যাবেন নতুন সেট ভার্সন কোড: Build.VERSION_CODES_FULL।
তো, আমাদের নতুন প্যাসিভ স্ক্যানিং API নিরাপদে কল করার জন্য, আমাদের একটি আরও স্পেসিফিক চেক করতে হবে:
// আমাদের ScanSettings বানাই
val scanSettingsBuilder = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
// এখন, চেক করি কি আমরা প্যাসিভ হতে পারি
if (Build.VERSION.SDK_INT_FULL >= Build.VERSION_CODES_FULL.BAKLAVA_1) {
Log.d("ApiCheck", "এই ডিভাইস কুল। প্যাসিভ যাচ্ছি।")
// এটি Q4 রিলিজ (৩৬.১) থেকে নতুন API
scanSettingsBuilder.setScanType(ScanSettings.SCAN_TYPE_PASSIVE)
} else {
Log.d("ApiCheck", "এই ডিভাইস পুরনো স্কুল। সক্রিয় স্ক্যানিং-এ থাকছি।")
// নতুন API না থাকা ডিভাইসের জন্য ফলব্যাক
// এখানে কিছু করতে হয় না, কারণ সক্রিয় ডিফল্ট
}
val scanSettings = scanSettingsBuilder.build()
গ্রেসফুল ডিগ্রেডেশন: শৈলীসহ পতনের কালা
এটি আমাদের একটি অত্যন্ত গুরুত্বপূর্ণ কনসেপ্টে নিয়ে আসে: গ্রেসফুল ডিগ্রেডেশন। এর মানে আপনার অ্যাপ পুরনো ডিভাইসে কাজ করতে হবে, এমনকি যদি এটি লেটেস্ট এবং গ্রেটেস্ট ফিচার ব্যবহার না করতে পারে। এটি গ্রেসফুলি ফলব্যাক করা উচিত।
উপরোক্ত উদাহরণে, যদি setScanType মেথড উপলব্ধ না থাকে, আমরা শুধু... কল করি না। অ্যাপটি একটি নরমাল, সক্রিয় স্ক্যানে ডিফল্ট হবে। এটি 그만큼 ব্যাটারি-দক্ষ নয়, কিন্তু এটি কাজ করবেই। পুরনো ডিভাইসের ব্যবহারকারী একটি ফাংশনাল অ্যাপ পাবে, এবং নতুন ডিভাইসের ব্যবহারকারী একটি আরও অপ্টিমাইজড এক্সপেরিয়েন্স পাবে। সবাই জিতল।
এখানে একটি টেবিল মনে রাখতে সাহায্য করবে কোন চেক কখন ব্যবহার করতে হয়:
| আপনি যদি API ব্যবহার করেন... | এই চেকটি ব্যবহার করুন... |
|---|---|
| একটি মেজর অ্যান্ড্রয়েড রিলিজ থেকে (যেমন, অ্যান্ড্রয়েড ১৬ Q2) | if (SDK_INT >= VERSION_CODES.BAKLAVA) |
| একটি মাইনর, ফিচার-ড্রপ রিলিজ থেকে (যেমন, অ্যান্ড্রয়েড ১৬ Q4) | if (SDK_INT_FULL >= VERSION_CODES_FULL.BAKLAVA_1) |
এই নতুন API চেকিং মাস্টার করা নন-নেগোশিয়েবল। এটি আধুনিক অ্যান্ড্রয়েড অ্যাপ লিখার চাবি যা একসাথে ইনোভেটিভ এবং স্থিতিশীল। এখন যেহেতু আমরা জানি কীভাবে একটি রবাস্ট অ্যাপ বানাতে হয়, চলুন কথা বলি কীভাবে এটি ঠিক করবো যখন এটি অবশ্যই ভাঙবে।
টেস্টিং এবং ডিবাগিং: মজার অংশ (যে কেউ কখনো বলেনি)
সফটওয়্যার ডেভেলপমেন্টে দুটি সার্বজনীন সত্য আছে:
এটি আমার মেশিনে কাজ করে, এবং
এটি সবচেয়ে শোভনভাবে ব্রেক করবে একটি লাইভ ডেমোর সময়।
ব্লুটুথ ডেভেলপমেন্ট, বিশেষ করে, এই দ্বিতীয় সত্যে আনন্দ লাগে বলে মনে হয়। এটি একটি চঞ্চল, অদৃশ্য শক্তি যা ডেভেলপারদের বিরুদ্ধে একটি ব্যক্তিগত শত্রুতা রেখে আছে বলে মনে হয়।
তাহলে, আমরা কীভাবে প্রতিকার করব? একটি স olid টেস্টিং এবং ডিবাগিং স্ট্র্যাটেজি দিয়ে। এটি গ্ল্যামারাস নয়, কিন্তু স엔 সifié বুদ্ধিমান থাকার একমাত্র পথ।
এমুলেটর: কল্পনার ভূমি
অ্যান্ড্রয়েড স্টুডিওর এমুলেটর একটি চমৎকার টুল। এটি দ্রুত, সুবিধাজনক, এবং সব ধরনের ডিভাইস সিমুলেট করতে পারে। এবং ব্লুটুথের জন্য? এটি... একটু সাহায্য করতে পারে। এমুলেটর ভার্চুয়াল ব্লুটুথ সাপোর্ট করে। আপনি এটি এনেবল করতে পারেন, এবং আপনার অ্যাপ মনে করবে এতে একটি ব্লুটুথ অ্যাডাপ্টার আছে। এটি আপনার UI টেস্ট করতে এবং নিশ্চিত করতে চমৎকার যে আপনার অ্যাপ ক্র্যাশ করছে না যখন এটি BluetoothLeScanner পাওয়ার চেষ্টা করে।
কিন্তু ক্যাচটি হলো: এটি রিয়েল নয়। এমুলেটর আসলে আপনার রুমের রেডিও তরঙ্গের সাথে ইন্টারঅ্যাক্ট করতে পারে না। আপনি এটি ব্যবহার করে আপনার রিয়েল-লাইফ BLE হেডফোন খুঁজে পাবেন না। সেটার জন্য, আপনাকে বাস্তব বিশ্বে যেতে হবে।
বাস্তব বিশ্ব: যেখানে বাগ বাস করে
রিয়েল, ফিজিক্যাল ডিভাইসে টেস্ট করার কোনো বিকল্প নেই। প্রতিটি ফোন ম্যানুফ্যাকচারারের নিজস্ব স্পেশাল ফ্লেভর আছে ব্লুটুথ স্ট্যাকের, নিজস্ব কুইর্কি অ্যান্টেনা ডিজাইন, এবং আপনার জীবন কষ্টকর করার নিজস্ব অনন্য পথ। একটি স্ক্যান যা গুগল পিক্সেলে পারফেক্টলি কাজ করে সেটি অন্য ব্র্যান্ডে বुरी तरह ফেল করতে পারে। জানার একমাত্র পথ হলো টেস্ট করা।
আপনার টেস্টিং আর্মেনি أواخر থাকা উচিত:
বিভিন্ন ফোন: ভিন্ন ব্র্যান্ড, ভিন্ন অ্যান্ড্রয়েড ভার্সন। যত বেশি, তত ভালো।
বিভিন্ন BLE পেরিফেরাল: শুধু এক টাইপের ডিভাইস দিয়ে টেস্ট করবেন না। কয়েকটি ভিন্ন বিকন, সেন্সর, বা ওয়েয়ারেবল আনুন। আপনি বিস্মিত হবেন কীভাবে ভিন্নভাবে তারা বিহেভ করে।
সাধারণ এরর: সাদা সspunkt
যখন আপনার স্ক্যান অবশ্যই ফেল করবে, এটি একটি এরর কোড দেবে। এখানে কয়েকটি সবচেয়ে সাধারণ ফরার:
| এরর কোড | সমস্যা | কীভাবে ফিক্স করবেন |
|---|---|---|
| SCAN_FAILED_ALREADY_STARTED | আপনি একটি রানিং স্ক্যান শুরু করার চেষ্টা করলেন। | আপনি খুব বেশি উত্তেজিত হয়ে গেলেন। নিশ্চিত করুন আপনি stopScan() কল না করলে startScan() একাধিকবার কল করছেন না। |
| SCAN_FAILED_APPLICATION_REGISTRATION_FAILED | আপনার অ্যাপের সেটআপে কিছু ফান্ডামেন্টালি ভুল। | এটি একটি অস্পষ্ট এবং অউপকারী এরর। এটি সাধারণত মানে আপনার পারমিশন বা সিস্টেমের সাথে সমস্যা আছে। ব্লুটুথ রিস্টার্ট করে দেখুন। |
| SCAN_FAILED_INTERNAL_ERROR | ব্লুটুথ স্ট্যাকের প্যানিক আটাক। | এটি ক্লাসিক "তুমি নয়, আমি" এরর। এটি ডিভাইসের ব্লুটুথ কন্ট্রোলারের একটি ইন্টারন্যাল সমস্যা। আপনি কিছুই করতে পারবেন না বাদে পরে আবার চেষ্টা করার। |
| SCAN_FAILED_FEATURE_UNSUPPORTED | আপনি একটি ফিচার ব্যবহার করার চেষ্টা করলেন যা হার্ডওয়্যার সাপোর্ট করে না। | আপনি হয়তো ব্যাচ স্ক্যানিং ব্যবহার করার চেষ্টা করছেন এমন একটি ডিভাইসে যা এটিকে সাপোর্ট করে না। আপনার API ভার্সন চেক ব্যবহার করুন! |
ডিবাগিং টুল: আপনার ভূত-শিকার কিট
যখন জিনিসগুলো ভুল হয়, আপনাকে ব্লুটুথের অদৃশ্য বিশ্বে কী হচ্ছে সেটা দেখতে সঠিক টুল দরকার।
logcat: এটি আপনার সেরা বন্ধু। আপনার লগ স্টেটমেন্টের সাথে উদার হোন। লগ করুন যখন আপনি একটি স্ক্যান শুরু করেন, যখন বন্ধ করেন, যখন একটি ডিভাইস পান, এবং যখন একটি স্ক্যান ফেল করে। আপনার অ্যাপের ট্যাগের জন্য একটি ফিল্টার তৈরি করুন যাতে আপনি শব্দের মাধ্যমে সিগন্যাল দেখতে পারেন।
অ্যান্ড্রয়েডের ব্লুটুথ HCI স্নূপ লগ: এটি ব্লুটুথ ডিবাগিংয়ের হলি গ্রেইল। এটি একটি ডেভেলপার অপশন যা আপনার ডিভাইসের ইন বা আউট যেকোনো একক ব্লুটুথ প্যাকেট রেকর্ড করে। এটি অবিশ্বাস্যভাবে ডিটেইলড এবং overwhelmeing হতে পারে, কিন্তু এটি চূড়ান্ত সত্যের উৎস। আপনি জেনারেটেড লগ ফাইলটি Wireshark-এর মতো টুলে খুলতে পারেন আপনার ফোন এবং BLE ডিভাইসের মধ্যে কাঁচা, আনফিল্টার্ড কথা বলা দেখতে। এটি রেডিও তরঙ্গের ওপর একটি ওয়ায়ারট্যাপের মতো।
nRF Connect for Mobile: এটি নর্ডিক সেমিকন্ডাক্টর থেকে একটি ফ্রি অ্যাপ, এবং যেকোনো BLE ডেভেলপারের জন্য এটি একটি অপরিহার্য টুল। এটি আপনাকে ডিভাইস স্ক্যান করতে, তাদের বিজ্ঞাপন ডেটা দেখতে, সেগুলোর সাথে সংযোগ করতে, এবং তাদের GATT সার্ভিস এক্সপ্লোর করতে দেয়। যদি আপনার অ্যাপ একটি ডিভাইস খুঁজে না পায়, আপনার যেটা প্রথমে করতে হবে সেটি হলো দেখতে nRF Connect কি পেতে পারে। যদি সেটি না পায়, সমস্যা সম্ভবত পেরিফেরালে, আপনার অ্যাপে নয়।
ব্লুটুথ টেস্টিং এবং ডিবাগিং একটি ম্যারাথন, স্প্রিন্ট নয়। এতে ধৈর্য, পদ্ধতিবদ্ধ পদ্ধতি, এবং একটি স্বাভাবিক স্ব-অপহাস্যকর হিউমার দরকার। কিন্তু সঠিক টুল এবং টেকনিকের সাথে, আপনি এই الوحশকে বশে করতে পারেন।
এখন, চলুন কথা বলি কীভাবে নিশ্চিত করবো আমাদের ভালো ম্যানার থাকা অ্যাপটি পারফরম্যান্সের দিক থেকেও একটি ভালো নাগরিক।
পারফরম্যান্স এবং সেরা অনুশীলন: কীভাবে একজন ভালো ব্লুটুথ নাগরিক হতে হয়
কাম 하는 কোড লিখা এক জিনিস। কোড লিখা যা ভালো কাজ করে, দক্ষ, এবং আপনার ব্যবহারকারীদের তাদের ফোন দেওয়ালে টুঁয়ে দিতে ইচ্ছা করাতে না – এটি সম্পূর্ণ আলাদা জিনিস। ব্লুটুথের কথা বলতে গেলে, একজন ভালো নাগরিক হওয়া মানে এক জিনিস: ব্যাটারি, ব্যাটারি, ব্যাটারি।
ব্লুটুথ রেডিও একটি শক্তিশালী হার্ডওয়্যার টুকরো, কিন্তু এটি পিপাসুও। এটি সক্রিয় থাকার প্রতি মুহূর্তে পাওয়ার সিপ করে। আপনার কাজ হলো নিশ্চিত করা যে এটি শুধু তখন সিপ করছে যখন এটি আবশ্যক। এখানে একজন ভালো ব্লুটুথ নাগরিক হওয়ার সোনালী নিয়মগুলো।
১. স্ক্যান করবেন না যদি প্রয়োজন না হয়
এটিस्पষ্ট লাগে, কিন্তু এটি সবচেয়ে সাধারণ ভুল। স্ক্যান শুরু করার बारेে সোচ্চার করার আগে, নিজে থেকে জিজ্ঞেস করুন: "আমি কি সত্যিকারের সাথে এখন এটি করতে চাই?" যদি ব্যবহারকারী সেই স্ক্রিনে না থাকে যেখানে স্ক্যান রেজাল্ট দরকার, স্ক্যান করবেন না। যদি অ্যাপটি ব্যাকগ্রাউন্ডে থাকে, অতিরিক্ত সতর্ক থাকুন। ব্যাকগ্রাউন্ড স্ক্যানিং ব্যাটারিতে огромন ড্রেন করে এবং অ্যান্ড্রয়েড এর জন্যই এটি ভারি রস্ট্রিক্ট করে।
২. আপনার স্ক্যান বন্ধ করুন!
আমি আবার বলছি কারণ এটি সত্যিকারের সাথে ততটুকু গুরুত্বপূর্ণ: আপনি কাজ শেষ হলে সবসময় আপনার স্ক্যান বন্ধ করুন। একটি চলমান স্ক্যান আপনার ব্যাটারির জন্য একটি চোরা নলের মতো। এটি ড্রেন করে থাকবে যতক্ষণ না কিছুই থাকবে। সেরা প্র্যাকটিস হলো আপনার স্ক্যান লাইফসাইকেলকে আপনার UI লাইফসাইকেলের সাথে বাঁধা।
override fun onPause() {
super.onPause()
// ব্যবহারকারী স্ক্রিন দেখতে পাচ্ছে না, তাই তাদের রেজাল্ট দরকার নেই।
stopBleScan()
}
override fun onResume() {
super.onResume()
// ব্যবহারকারী স্ক্রিনে ফিরে এল, আবার স্ক্যান শুরু করি।
startBleScan()
}
যদি আপনি যে ডিভাইস খুঁজছিলেন সেটি পেয়ে যান, অবিলম্বে স্ক্যান বন্ধ করুন। খুঁজতে থাকার কোনো প্রয়োজন নেই।
৩. সঠিক স্ক্যান মোড বেছে নিন
ScanSettings আপনাকে কয়েকটি ভিন্ন মোড দেয়। বুদ্ধিমানীপূর্বক বেছে নিন।
SCAN_MODE_LOW_POWER: এটি আপনার ডিফল্ট, প্রতিদিনের মোড। এটি ইন্টারভালে স্ক্যান করে, আবিষ্কারের গতি এবং ব্যাটারি লাইফের ভারসাম্য রক্ষা করে। অধিকাংশ ফোরগ্রাউন্ড স্ক্যানের জন্য এটি ব্যবহার করুন।
SCAN_MODE_BALANCED: একটি মধ্যম পথ। এটি লো পাওয়ার মোডের চেয়ে বেশি ফ্রিকোয়েন্টলি স্ক্যান করে।
SCAN_MODE_LOW_LATENCY: এটি "আমাকে এখনই খুঁজে বের করতে হবে" মোড। এটি নন-স্টপ স্ক্যান করে। এটি সবচেয়ে দ্রুত ডিভাইস খুঁজে পাবে, কিন্তু এটি সবচেয়ে দ্রুত আপনার ব্যাটারি ড্রেন করবেও। শুধু সংক্ষিপ্ত, ক্রিটিক্যাল অপারেশনের জন্য এটি ব্যবহার করুন।
SCAN_MODE_OPPORTUNISTIC: এটি চূড়ান্ত প্যাসিভ মোড। আপনার অ্যাপ কোনো স্ক্যান ট্রিগার করে না। এটি শুধু রেজাল্ট পায় যদি অন্য কোনো অ্যাপ স্ক্যান করছে থাকে। এটি শূন্য এক্সট্রা ব্যাটারি ব্যবহার করে, কিন্তু আপনাকে রেজাল্ট পাওয়ার কোনো গ্যারান্টি নেই। নন-ক্রিটিক্যাল ব্যাকগ্রাউন্ড আপডেটের জন্য এটি ব্যবহার করুন।
এবং অবশ্যই, আপনি যদি AOSP ১৬ QPR2 বা তার পরে থাকেন, যেকোনো সময় আপনাকে স্ক্যান রেসপন্স ডেটা দরকার নেই, setScanType(SCAN_TYPE_PASSIVE) ব্যবহার করুন। এটি পাওয়ার এফিশিয়েন্সির নতুন রাজা।
৪. হার্ডওয়ার ফিল্টারিং এবং ব্যাচিং ব্যবহার করুন
আমরা এডভান্সড সেকশনে এটা কভার করেছি, কিন্তু এটি একটি সেরা অনুশীলন যা রিপিট করা লায়ক। আপনি যদি একটি নির্দিষ্ট ডিভাইস খুঁজছেন, ScanFilter ব্যবহার করুন। আপনি যদি একটি লং-রানিং স্ক্যান করছেন, আপনার রেজাল্ট ব্যাচ করার জন্য setReportDelay() ব্যবহার করুন। এই দুটি টেকনিক কাজটি পাওয়ার-দক্ষ ব্লুটুথ কন্ট্রোলারের ওপর অফলোড করে এবং আপনার অ্যাপের কোডকে ঘুমাতে দেয়, যা ব্যাটারি বাঁচানোর নম্বর এক পথ।
৫. মেমোরির প্রতি সচেতন থাকুন
প্রতিটি ScanResult অবজেক্ট যা আপনার অ্যাপ গ্রহণ করে সেটি মেমরি নেয়। আপনি যদি সैंकড়ों BLE ডিভাইসের মাঝপথে আছেন, এবং ফিল্টার ব্যবহার না করছেন, আপনার অ্যাপ দ্রুত ওভারওয়েল্ম হয়ে যেতে পারে এবং মেমরি খالی হয়ে যেতে পারে। এটি আরেকটি কারণ যে ফিল্টারিং কতটুকু গুরুত্বপূর্ণ। শুধু সে রেজাল্টগুলো নিন যা আপনার কearen।
এই নিয়মগুলো মেনে চললে, আপনি একটি ব্লুটুথ অ্যাপ বানাতে পারবেন যা শুধু শক্তিশালী এবং ফিচার-রিচ নয়, বরং আপনার ব্যবহারকারীর ডিভাইসের প্রতি সম্মানজনকও। আপনি একজন সত্যিকার ব্লুটুথ সেনসেই হবেন। এখন, চলুন সবকিছু সমেটিয়ে ভবিষ্যতের দিকে তাকাই।
উপসংহার: ভবিষ্যত প্যাসিভ (এবং এটি ঠিক আছে)
আমরা কতটুকু দীর্ঘ যাত্রা করেছি, ঠিক না? আমরা সময়ের পেছনে যाकर ক্লাসিক ব্লুটুথের অন্ধকার যুগ দেখেছি, BLE-এর পুনর্জাগরণ 목격 করেছি, এবং AOSP ১৬-এর সাহসিক নতুন বিশ্বে উদিত হয়েছি। আমরা প্যাসিভ স্ক্যানিংয়ের সাথে নীরব নিনজা হয়ে উঠেছি, বন্ড লস কারণগুলোর সাথে জাসوس খেলেছি, এবং বিজ্ঞাপন থেকে সার্ভিস UUID-এর সাথে স্পিড ডেটিংয়ের কালা জাদু মাস্টার করেছি।
যদি এই সব থেকে এক বড় টেকওয়ে হয়, সেটি হলো অ্যান্ড্রয়েডে ব্লুটুথের ভবিষ্যত স্মার্টার, আরও দক্ষ, এবং সব থেকে বড় একটি হেল্পফুল কম فرাস্ট্রেটিং। অ্যান্ড্রয়েড টিম স্পষ্টতো ডেভেলপারদের পেইন পয়েন্ট শুনছে এবং আমাদের উন্নত, আরও ব্যাটারি-বান্ধব অ্যাপ বানাতে সাহায্য করার টুল দিচ্ছে। প্যাসিভ স্ক্যানিংয়ার அறródło শুধু একটি নতুন ফিচার নয় – এটি একটি ফিলোসফিতে পরিবর্তন। এটি একটি স্বীকৃতি যে কখনো কখনো, কমিউনিকেশন করার সেরা পথ হলো শুধু শোনা।
ডেভেলপার হিসেবে, এই নতুন টুলগুলো আমাদের সিম্পল "কানেক্ট অ্যান্ড স্ট্রিম" ইউজ কেসের বাইরে যেতে ক্ষমতা দিচ্ছে। আমরা এখন জটিল, কন্টেক্সট-আওয়্যার অ্যাপ্লিকেশন বানাতে পারি যা বিনা ব্যবহারকারীদের ফোনকে ব্যয়বহুল কাগজের টুকরো বানিয়ে তাদের চারপাশের পরিবেশ সম্পর্কে কনস্ট্যান্টলি আওয়্যার থাকতে পারে। একটি সত্যিকার স্মার্ট, সিমলেস কানেক্টেড বিশ্বের স্বপ্ন একটু আরও নিকট, এবং এটি এই পাওয়ার-দক্ষ প্রযুক্তির ওপর ভিত্তি করে তৈরি হবে।
তাহলে, आगे কী? ব্লুটুথের বিশ্ব সবসময় বিবর্তনশীল। আমাদের সামনে ব্লুটুথ ৫.৪ আছে Auracast, মেশ নেটওয়ার্কিং, এবং আরও প্রিসাইজ লোকেশন ফাইন্ডিংসহ। একটা জিনিস নিশ্চিত: টুলগুলো আরও ভালো হবে, এবং চ্যালেঞ্জগুলো আরও আকর্ষণীয় হবে।
এখন কিছুক্ষণ আমাদের অর্জিত প্রগতির প্রশংসা করুন। আপনি যখন পরবর্তীবার একটি ব্লুটুথ স্ক্যান শুরু করবেন এবং এটি শুধু কাজ করবে, এক মুহূর্তের জন্য হার্ডওয়ারিং ইঞ্জিনিয়ারদের ধন্যবাদ জানান যারা এটি সম্ভব করল। এবং আপনার অ্যাপের ব্যাটারি গ্রাফ যখন একটি সুন্দর, ফ্ল্যাট লাইন হবে একটি ভয়ঙ্কর স্কি স্লোপের বদলে, প্যাসিভ স্ক্যানিংয়ের শক্তিকে একটু নমস্কার করুন।
ব্লুটুথ الوحশটি কখনো সম্পূর্ণ বশে করা যাবে না, কিন্তু AOSP ১৬-এ, আমাদের একটি অনেক শক্তিশালী পাটা मिलেছে। এখন যান এবং চমৎকার জিনিসগুলো বানান। এবং সব পবিত্র জিনিসের জন্য, আপনার স্ক্যান বন্ধ করা মনে রাখুন।
Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started