ویک لاک اینڈرائیڈ کے آسان ترین APIs میں سے ایک ہے اور غلط استعمال کرنے میں آسان ترین ہے۔ اسے حاصل کرنے کے لیے کوڈ کی ایک لائن لگتی ہے۔ اگر آپ اسے آف کرنا بھول جاتے ہیں، تو آپ کے فون کا CPU گھنٹوں تک فعال رہے گا، آپ کی بیٹری راتوں رات ختم ہو جائے گی، اور آپ کو Google Play پر اپنی ایپ کے لیے انتباہی لیبل نظر آ سکتا ہے۔
زیادہ تر ویک لاک کیڑے خود API کو غلط سمجھنے کی وجہ سے نہیں ہوتے ہیں۔ یہ ایک عام کنٹرول بہاؤ کے مسئلے سے پیدا ہوتا ہے۔ acquire() اور release()ان میں قبل از وقت واپسی، کال بیکس جن پر عمل نہیں ہوا، یا غیر متوازن حوالہ شمار شامل ہیں۔
یہ کیڑے کوڈ کے جائزے میں تلاش کرنا مشکل ہیں اور عام جانچ کے دوران پوشیدہ ہیں۔ کیونکہ آلہ عام طور پر جڑا ہوا اور بیدار ہوتا ہے۔
یہ مضمون بتاتا ہے کہ ویک لاکس اصل میں سسٹم لیول پر کیا کرتے ہیں، ایپس کے ویک لاکس کو لیک کرنے کے سب سے عام طریقوں کو دیکھتا ہے، اور لیک کو روکنے اور ان کا پتہ لگانے کے لیے مخصوص پیٹرن اور ٹولز دکھاتا ہے۔ یہ جدید APIs کا بھی احاطہ کرتا ہے جنہیں زیادہ تر معاملات میں دستی ویک لاک کی ضرورت نہیں ہوتی ہے۔
انڈیکس
شرطیں
آپ کو کوٹلن میں اینڈرائیڈ ایپس لکھنے میں ماہر ہونا چاہیے اور آپ کو سروسز، براڈکاسٹ ریسیورز، اور کوروٹائنز کی بنیادی باتیں جاننا چاہیے۔
آپ کو بھی ضرورت ہے adb ڈیبگنگ سیکشن کے لیے ایک فزیکل ڈیوائس انسٹال ہے۔ ایمولیٹر پاور سٹیٹس کو حقیقت پسندانہ انداز میں ماڈل نہیں کرتا، اس لیے بعد میں اس مضمون میں کچھ کمانڈز غلط نتائج پیدا کرتی ہیں۔
جب آپ کا Android آلہ سلیپ موڈ میں چلا جاتا ہے تو کیا ہوتا ہے؟
جب اسکرین آف ہوتی ہے اور کسی اور چیز کو CPU کی ضرورت نہیں ہوتی ہے، تو اینڈرائیڈ ایپلیکیشن پروسیسر کو کم طاقت والی معطل حالت میں رکھتا ہے۔ اس حالت میں، CPU ہدایات پر عمل کرنا بند کر دیتا ہے، زیادہ تر RAM خود کو تازگی بخشتی ہے، اور صرف چند ہارڈویئر بلاکس (موڈیم، ریئل ٹائم کلاک، سینسر ہب، اور چند انٹرپٹ کنٹرولرز) زندہ رہتے ہیں۔ آلہ بیکار دکھائی دیتا ہے، لیکن جب کوئی خلل آتا ہے تو جلدی بیدار ہونے کے لیے تیار ہوتا ہے۔
اینڈرائیڈ ایک طریقہ کار استعمال کرتا ہے جسے خودکار نیند کہتے ہیں۔ دانا جیسے ہی جاگتے رہنے کی کوئی وجہ نہیں ہے سسٹم کو معطل کرنے کی کوشش کرے گا۔ کوئی بھی چیز جس کو چلتے رہنے کے لیے CPU کی ضرورت ہو اسے واضح طور پر یہ کہنا چاہیے، ایک ویک اپ سورس کو پکڑ کر (کرنل کی اصطلاح جسے اینڈرائیڈ ویک اپ لاک کہتا ہے)۔ دانا اس وقت تک معطل کرنے سے انکار کرتا ہے جب تک کہ کم از کم ایک ویک اپ سورس فعال ہو۔
Screen off
|
v
Any active wakeup sources? == yes ==> CPU stays awake
|
no
|
v
Kernel suspends CPU
|
v
Interrupt (alarm, network packet, button press)
|
v
CPU resumes, driver or framework takes a wake lock to handle the event
اوپر دیا گیا خاکہ اسکرین آف ہونے کے دوران دانا کے بار بار کیے جانے والے فیصلوں کو دکھاتا ہے۔ جب تک ویک اپ کا ذریعہ برقرار رہتا ہے، سی پی یو چلتا رہتا ہے۔ اگر کچھ نہیں رکھا جاتا ہے، تو سسٹم اس وقت تک موقوف رہے گا جب تک کہ ہارڈ ویئر میں خلل نہ آجائے۔ جب آلہ دوبارہ شروع ہوتا ہے، کوڈ جو مداخلت کو ہینڈل کرتا ہے عام طور پر ایک مختصر ویک لاک حاصل کرتا ہے تاکہ کرنل دوبارہ معطل کرنے کی کوشش کرنے سے پہلے اپنا کام مکمل کر سکے۔
اہم تفصیل یہ ہے کہ جاگنے کا انتخاب کیا گیا ہے۔ اسکرین آف ہونے پر آپ کے کوڈ کو CPU کا وقت نہیں ملتا جب تک کہ اس کی بجائے کوئی چیز ویک لاک کو پکڑے ہوئے ہو۔
ویک لاک کیا کرتا ہے۔
ایک ایپ لیول ویک لاک کی درخواستیں: PowerManagerServiceسسٹم سروسز اندرونی طور پر چل رہی ہیں۔ system_server. جب ایپ کال کرتی ہے۔ acquire()کال بائنڈر کے ذریعے مناسب سروس تک جاتی ہے، جو درخواست کو UID، ٹیگ سٹرنگ، اور لاک ٹائپ کے ساتھ لاگ کرتی ہے۔ PowerManagerService تمام ایپ کی درخواستوں کو جمع کرتا ہے اور ایپ کی تمام درخواستوں کی جانب سے ایک واحد کرنل ویک اپ سورس رکھتا ہے۔ آخری ایپ لیول لاک کے جاری ہونے کے بعد، کرنل ویک سورس کو حذف کر دیا جاتا ہے اور آلہ معطل کرنے کے لیے آزاد ہے۔
اس ڈیزائن کے دو قابل فہم نتائج ہیں۔ سب سے پہلے، سسٹم کو بخوبی معلوم ہوتا ہے کہ کون سی ایپس ہر لاک کو پکڑے ہوئے ہیں اور کتنی دیر تک، جس طرح بیٹری کے اعدادوشمار اور پلے کنسول وائٹلز ایپس پر الزام لگا سکتے ہیں۔
دوسرا PowerManagerService ہم ہر تالا کو بائنڈر ایگزٹ نوٹیفکیشن کے ساتھ ایک پروسیس سے منسلک کرتے ہیں۔ جب عمل ختم ہو جاتا ہے، تو نظام خود بخود اسے غیر مقفل کر دیتا ہے۔ لہذا لیک عمل کی زندگی تک محدود ہے، لیکن پیش منظر کی خدمات کو چلانے والے یا سسٹم کے ذریعہ فعال رکھے جانے والے عمل کئی گھنٹوں تک برقرار رہ سکتے ہیں۔
ویک لاکس کی اقسام
کہ PowerManager کلاس کئی لاکنگ لیولز کی وضاحت کرتی ہے۔ ان میں سے زیادہ تر فرسودہ ہیں اور ایپ ڈویلپرز کے لیے صرف ایک چیز واقعی اہم ہے:
| پانی کی سطح | سی پی یو کو آن رکھیں | اسکرین کو آن رکھیں | صورت حال |
|---|---|---|---|
PARTIAL_WAKE_LOCK |
ہاں | نہیں | ابھی کے لیے، یہ واحد چیز ہے جسے زیادہ تر ایپس کو استعمال کرنے کی ضرورت ہے۔ |
SCREEN_DIM_WAKE_LOCK |
ہاں | ہاں، اندھیرا ہو گیا۔ | API 17 سے فرسودہ |
SCREEN_BRIGHT_WAKE_LOCK |
ہاں | جی ہاں، پوری چمک | API 13 سے فرسودہ |
FULL_WAKE_LOCK |
ہاں | ہاں، کی بورڈ بیک لائٹ شامل کریں۔ | API 17 سے فرسودہ |
PROXIMITY_SCREEN_OFF_WAKE_LOCK |
نہیں | قربت کے سینسر کے قریب اسکرین آف ہو جاتی ہے۔ | فی الحال ایپ کالز کے لیے استعمال کیا جاتا ہے۔ |
پہلا کالم وہ مستقل ہے جسے آپ گزر رہے ہیں۔ newWakeLock()اگلے دو بیان کرتے ہیں کہ تالا کو کیا طاقت دیتا ہے۔ اسکرین سے متعلقہ لیولز فرسودہ ہیں کیونکہ ایپ صارف کے جانے کے بعد بھی، ڈسپلے کو آن رکھنے اور بیٹری ختم کرنے کے بعد بھی اسی سطح پر رہے گی۔
اسکرین کو آن چھوڑنے کا ایک متبادل ہے۔ FLAG_KEEP_SCREEN_ON کھڑکی کے جھنڈے (یا android:keepScreenOn لے آؤٹ میں) ونڈو مینیجر ونڈو کی مرئیت سے منسلک ہے، لہذا یہ لیک نہیں ہو سکتا۔ قربت کا تالا مصروف اسکرین کا ایک خاص معاملہ ہے۔ ہر چیز کے لیے، جدید اینڈرائیڈ میں، "ویک لاک” کا مطلب ہے: PARTIAL_WAKE_LOCKاس مضمون کا بقیہ حصہ اسی پر مرکوز ہے۔
ویک لاک کو کیسے حاصل کریں اور جاری کریں۔
ویک لاک استعمال کرنے کے لیے، آپ کو ضرورت ہے: WAKE_LOCK اپنے مینی فیسٹ میں اجازت دیں۔ یہ ایک عام اجازت ہے اور انسٹالیشن کے دوران اشارہ کیے بغیر دی جاتی ہے۔
یہ لائن اعلان کرتی ہے کہ ایپ ویک لاک کو روک سکتی ہے۔ اس کے بغیر، acquire() پھینکنا SecurityException. چونکہ اجازت خود بخود مل جاتی ہے، اس لیے صارفین کو اندازہ نہیں ہوتا کہ آپ کی ایپ ویک لاک استعمال کر رہی ہے جب تک کہ وہ بیٹری کے استعمال کی جانچ نہ کریں۔ یہ صارفین کے محتاط رہنے کی ایک اور وجہ ہے۔
سب سے بنیادی استعمال مندرجہ ذیل ہے:
val powerManager = context.getSystemService(PowerManager::class.java)
val wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK,
"myapp:upload"
)
wakeLock.acquire(10 * 60 * 1000L) // 10 minutes
uploadPendingFiles()
wakeLock.release()
کوڈ ہے۔ PowerManager سسٹم سروس بنائیں WakeLock جزوی سطحوں اور ٹیگز کے ساتھ ایک آبجیکٹ۔ خود سے کسی چیز کو بنانے سے کچھ نہیں ہوتا۔ CPU صرف اس کے درمیان بیدار ہے: acquire() اور release().
ٹیگ "myapp:upload" میں ظاہر ہوتا ہے dumpsysآپ کو بیٹری ہسٹورین اور پلے کنسول کے ذریعے ایپس اور مخصوص کاموں دونوں کی شناخت کرنی ہوگی۔
کہ app:component فارمیٹ گوگل کی طرف سے تجویز کردہ ایک کنونشن ہے اور ڈیبگنگ کے دوران حقیقی وقت کی بچت کرتا ہے۔ کے بارے میں دعوی کرتا ہے acquire() ملی سیکنڈ میں ٹائم آؤٹ۔ اگر تالا 10 منٹ کے بعد بھی رہتا ہے، تو سسٹم اسے کھول دے گا۔
یہ ٹکڑا درست لگتا ہے، لیکن اس میں ایک حقیقی بگ ہے۔ uploadPendingFiles() پھینکنا، release() کبھی نہیں چلتا۔ ٹائم آؤٹ نقصان کو 10 منٹ تک محدود کرتا ہے، لیکن پھر بھی ہر خرابی کے لیے آپ کی بیٹری 10 منٹ تک ختم ہو جائے گی۔ اگلا حصہ بتاتا ہے کہ اس مسئلے کو کیسے حل کیا جائے اور دوسرے کون سے پھندے ہیں۔
حوالہ گنتی کیسے کام کرتی ہے۔
ویک لاکس بطور ڈیفالٹ حوالہ شمار ہوتے ہیں۔ ہر کال ہے۔ acquire() اندرونی کاؤنٹر کو بڑھاتا ہے۔ release() ہم اسے کم کرتے ہیں اور تالا صرف اس وقت صاف ہوتا ہے جب کاؤنٹر 0 تک پہنچ جاتا ہے۔
wakeLock.acquire(60_000L) // count = 1, lock held
wakeLock.acquire(60_000L) // count = 2
wakeLock.release() // count = 1, still held
wakeLock.release() // count = 0, lock released
wakeLock.release() // throws RuntimeException: WakeLock under-locked
پہلی دو کالیں گنتی کو 2 تک لے جاتی ہیں۔ پہلی ریلیز اسے واپس ایک میں واپس کر دے گی، اس لیے اگر آپ کا کوڈ اسے "ریلیز” کرتا ہے، تب بھی تالا برقرار رہے گا۔
دوسری ریلیز میں اصل میں لاک ہٹا دیا جائے گا۔ تیسری ریلیز تک رہائی کے لیے کچھ نہیں بچا تھا۔ RuntimeException پیغام "ویک لاک انڈر لاک” ظاہر ہوتا ہے۔ یہ پیداوار میں تنازعہ ہے، لہذا کچھ ڈویلپرز لیبز شروع کرتے ہیں. release() کو try بلاک کریں یا چیک کریں۔ isHeld پہلے
حوالہ شمار مفید ہے جب کوڈ کے متعدد آزاد ٹکڑے کوڈ کے ایک ٹکڑے کا اشتراک کرتے ہیں۔ WakeLock اعتراض یہ ایک مسئلہ بن جاتا ہے اگر حصول کی تعداد اور رہائی کے اوقات مماثل نہ ہوں۔ مثال کے طور پر، اگر ایک فنکشن کو دو بار بلایا جاتا ہے، لیکن تکمیل کال بیک صرف ایک بار عمل میں آتا ہے۔
آپ حوالہ کی گنتی کو استعمال کرکے بند کر سکتے ہیں: setReferenceCounted(false)اس کے بعد، ایک release() تالے کو صاف کرتا ہے اس سے قطع نظر کہ اسے کتنی بار حاصل کیا گیا تھا۔ جب ایک جزو تالا کا مالک ہوتا ہے، تو یہ اکثر ایک آسان ماڈل ہوتا ہے۔
عام طریقے ایپ لیک ویک لاک
ویک لاک لیک تقریبا ہمیشہ ہی پیٹرن کی ایک چھوٹی تعداد میں آتے ہیں۔ اس کو تسلیم کرنا کوڈ کے جائزوں کو زیادہ موثر بناتا ہے۔
مستثنیات اور ابتدائی واپسی۔
اوپر کی بنیادی مثال سب سے عام لیک ہے۔ کوئی بھی کوڈ پاتھ جو درمیان کسی فنکشن کو ختم کرتا ہے۔ acquire() اور release() اگر آپ ریلیز نہیں چلاتے ہیں تو یہ لیک ہو جائے گی۔ اس میں مستثنیات شامل ہیں۔ return ایک بیان جو بعد میں کسی ایسے شخص کے ذریعہ شامل کیا گیا جس نے تالے کو نہیں دیکھا، اور break یا continue اندرونی لوپ.
fun syncContacts() {
wakeLock.acquire(5 * 60 * 1000L)
val account = accountStore.current() ?: return
api.pushContacts(account)
wakeLock.release()
}
جب بھی کرنٹ اکاؤنٹ نہیں ہوتا ہے تو یہ فیچر لاک کو لیک کرتا ہے۔ return رہائی چھوڑ دیں۔ یہ بھی لیک ہو جاتا ہے۔ pushContacts() نیٹ ورک کی رعایت ہوتی ہے۔ دونوں راستے آزادانہ طور پر بے ضرر دکھائی دیتے ہیں۔ اگرچہ ٹائم آؤٹ لیک کے مستقل ہونے کا سبب نہیں بنتا، لیکن کسی بھی مطابقت پذیری کی ناکامی کے نتیجے میں اب بھی 5 منٹ جاگنے کا وقت ہوگا۔
غیر مطابقت پذیر آپریشنز جو کال کرنے والے کو پیچھے چھوڑ دیتے ہیں۔
ویک لاکس اکثر ایک جگہ حاصل کیے جاتے ہیں اور دوسری جگہ کال بیک کے ذریعے جاری کیے جاتے ہیں۔ اگر کال بیک پر عمل نہیں کیا جاتا ہے، تو لاک جاری نہیں کیا جائے گا۔
fun startDownload(url: String) {
wakeLock.acquire()
downloader.enqueue(url, object : Callback {
override fun onComplete() {
wakeLock.release()
}
override fun onError(e: Throwable) {
log("download failed", e)
}
})
}
یہاں دو غلطیاں ہیں۔ ایرر کال بیک لاک کو جاری کرنا بھول جاتا ہے، لہذا جب بھی ڈاؤن لوڈ ناکام ہوجاتا ہے تو یہ لاک کو لیک کرتا ہے۔ اور چونکہ تالا بغیر کسی ٹائم آؤٹ کے حاصل کیا جاتا ہے، اس لیے یہ عمل ختم ہونے تک لیک برقرار رہتا ہے۔
ایک تیسرا، کم واضح خطرہ ہے۔ downloader اگر آپ کال بیک پر کال کیے بغیر کسی درخواست کو منسوخ یا حذف کرتے ہیں تو لاک بھی لیک ہو جائے گا۔ کوئی بھی ڈیزائن جس کی ریلیز صارف کو واپس کال کرنے والے تیسرے فریق پر انحصار کرتی ہے اسے حفاظتی جال کے طور پر ٹائم آؤٹ کی ضرورت ہوتی ہے۔
غیر متوازن حوالہ شمار
اگر ریلیز کیے جانے سے زیادہ مشترکہ حوالہ شماری کے تالے حاصل کیے جاتے ہیں، تو لاک ہولڈ رکھا جائے گا چاہے تمام کوڈ پاتھ لاک کو جاری کرتے دکھائی دیں۔
class LocationTracker(private val wakeLock: PowerManager.WakeLock) {
fun onStart() {
wakeLock.acquire(30 * 60 * 1000L)
startUpdates()
}
fun onStop() {
stopUpdates()
wakeLock.release()
}
}
یہ کلاس اچھی طرح سے متوازن نظر آتی ہے۔ مسئلہ اس وقت ظاہر ہوتا ہے جب: onStart() دو بار کال کی گئی (مثال کے طور پر کنفیگریشن میں تبدیلی یا ڈپلیکیٹ ارادے کے بعد) onStop() اسے ایک بار کہا جاتا ہے۔ گنتی 1 پر ختم ہوتی ہے اور ٹائم آؤٹ ختم ہونے تک لاک رکھا جاتا ہے۔ حوالہ شمار یا محفوظ حصول کو بند کر دیں۔ if (!wakeLock.isHeld)یہ اس مخصوص مسئلے کو ٹھیک کرتا ہے۔
براڈکاسٹ ریسیور جو پس منظر کا کام شروع کرتا ہے۔
سسٹم آپ کے لیے ویک لاک رکھتا ہے۔ onReceive() دوڑو۔ جیسے ہی onReceive() جب یہ واپس آجاتا ہے، تو اس کا لاک جاری ہوجاتا ہے اور اس عمل کو بیکار چھوڑ دیا جاسکتا ہے۔
ایک عام غلطی الگ ویک لاک حاصل کرنا ہے۔ onReceive()ایک دھاگہ شروع کرتا ہے اور دھاگے پر ایک تالا جاری کرتا ہے۔ ریلیز اس وقت نہیں ہوتی جب کوئی عمل ختم ہوجاتا ہے یا تھریڈ میں خلل پڑتا ہے۔
بڑی عمر WakefulBroadcastReceiver اس پیٹرن کے لیے ایک مددگار بنایا گیا تھا، لیکن اب ان مسائل کی وجہ سے فرسودہ ہے۔ اس مضمون میں بعد میں نئے متبادلات کا احاطہ کیا گیا ہے۔
لامحدود آپریشنز کے لیے تالے کو برقرار رکھیں
بغیر ٹائم آؤٹ کے نیٹ ورک کی درخواست، انتظار CountDownLatch کوئی بھی آپریشن جس کی کوئی ڈیڈ لائن نہیں ہے اور بلوٹوتھ ساکٹ سے پڑھنے میں رکاوٹیں ہمیشہ کے لیے لٹک سکتی ہیں۔ اگر ان میں سے کسی ایک کے ارد گرد ویک لاک لگا ہوا ہے تو تالا بھی ٹوٹ جاتا ہے۔ کوڈ تکنیکی طور پر تمام راستوں پر تالے کو جاری کرتا ہے، لیکن رہائی کبھی نہیں پہنچ پاتی کیونکہ آپریشن کبھی مکمل نہیں ہوتا ہے۔
لیک کو روکنے کے لئے پیٹرن
ان لیکس کا حل چند عادات پر آتا ہے جن کا لگاتار اطلاق کرنا آسان ہے۔
ہمیشہ وقت کی حدود کا استعمال کریں۔
تمام کرنسیوں acquire() آپ کو ٹائم آؤٹ پاس کرنا ہوگا۔ اینڈروئیڈ لنٹ اسے اس کے ذریعے نافذ کرتا ہے: WakelockTimeout کال پر جھنڈا لگانے کے لیے چیک کریں۔ acquire() بغیر کسی دلیل کے۔
ایک وقت کی حد کا انتخاب کریں جو آپریشن میں لگنے والے وقت سے زیادہ ہو، لیکن اتنا چھوٹا ہو کہ رساو برقرار رہے۔ مطابقت پذیری کی کارروائیوں کے لیے جو عام طور پر 20 سیکنڈ میں مکمل ہو جاتے ہیں، چند منٹ قابل قبول ہیں۔ ایک گھنٹے کے لیے نہیں۔
آخر میں بلاک سے رہائی
سٹرکچرڈ ریلیز واحد سب سے مؤثر حل ہیں۔ کوٹلن میں، چھوٹی ایکسٹینشن کم غلطی کا شکار ہیں۔
inline fun PowerManager.WakeLock.withLock(
timeoutMs: Long,
block: () -> T
): T {
acquire(timeoutMs)
try {
return block()
} finally {
if (isHeld) release()
}
}
یہ فنکشن مطلوبہ ٹائم آؤٹ کے ساتھ لاک حاصل کر لیتا ہے اور کالر کے بلاک پر عمل کرنے کے بعد لاک کو جاری کرتا ہے۔ finally I. کیونکہ finallyریلیز عام تکمیل پر، کسی بلاک سے جلد واپسی پر، یا جب کوئی استثناء پھیلتا ہے۔
کہ isHeld چیک کرنا ضروری ہے کیونکہ اگر کوئی بلاک طویل عرصے سے چل رہا ہے، تو ہو سکتا ہے کہ وقت ختم ہونے کی وجہ سے وہ پہلے ہی غیر مقفل ہو چکا ہو۔ اگر آپ کے پاس چیک نہیں ہے، release() کال "ویک لاک انڈر لاک” کو اٹھاتی ہے اور اصل میں بلاک کے ذریعے پھینکے گئے کسی بھی استثناء کو بدل دیتی ہے۔ فنکشن ڈسپلے inline بھیجنے والا استعمال کرسکتا ہے۔ return بلاک کے اندر منسلک فنکشن میں finally دفعات اب بھی نافذ ہیں۔
اسسٹنٹ کا ہونا سابقہ رابطے کی مطابقت پذیری کی خصوصیت کو محفوظ تر بناتا ہے۔
fun syncContacts() = wakeLock.withLock(timeoutMs = 5 * 60 * 1000L) {
val account = accountStore.current() ?: return
api.pushContacts(account)
}
ابتدائی واپسی اور مستثنیات pushContacts() اب دونوں پاس ہیں۔ finally رکاوٹ withLock، لہذا یہ تمام راستوں میں کھلا ہے۔ کال سائٹس بھی چھوٹی ہیں، اور جائزہ لینے والے ہر برانچ کو ٹریک کیے بغیر ایک نظر میں درستگی کو چیک کر سکتے ہیں۔
کوروٹائنز پر احتیاط سے اسکوپ لاکنگ
ایک ہی پیٹرن ایک کورٹین کے اندر کام کرتا ہے، کیونکہ کوٹلن میں منسوخی کو اس طرح پھیلایا جاتا ہے: CancellationException اور finally جب کوروٹین منسوخ ہو جاتی ہے، تو بلاک کو عمل میں لایا جاتا ہے۔
suspend fun uploadAll(files: List) {
wakeLock.acquire(10 * 60 * 1000L)
try {
withTimeout(9 * 60 * 1000L) {
files.forEach { api.upload(it) }
}
} finally {
if (wakeLock.isHeld) wakeLock.release()
}
}
لاک آپریشن شروع ہونے سے پہلے حاصل کیا جاتا ہے اور آپریشن مکمل ہونے پر اسے جاری کیا جاتا ہے۔ finallyشامل دائرہ کار کی تکمیل، ناکامی اور منسوخی پر عملدرآمد کرتا ہے۔ withTimeout اپ لوڈ پر ہی ایک سخت ڈیڈ لائن لگائیں اور اسے ویک لاک ٹائم آؤٹ سے تھوڑا چھوٹا سیٹ کریں، جس سے سسٹم لاک کو جاری کرنے سے پہلے کوروٹین ترک کرنے پر مجبور ہو جاتا ہے۔ اس سے پہلے بیان کردہ غیر محدود کارروائیوں کا مسئلہ حل ہوتا ہے۔
ایک انتباہ: inline ایک ان لائن لیمبڈا جب توقف کے سیاق و سباق میں بلایا جاتا ہے تو ایک توقف فنکشن کو کال کرسکتا ہے، لہذا پچھلے حصے سے مددگار بھی یہاں استعمال کیا جاسکتا ہے۔
ہر کام کو اس کا اپنا لاک دیں۔
ایک کا اشتراک کریں WakeLock غیر متعلقہ فنکشنز میں اشیاء کی تخلیق سے حوالہ شماری کا اندازہ لگانا مشکل ہو جاتا ہے اور بیٹری کے اعدادوشمار بیکار ہو جاتے ہیں۔ اس کی وجہ یہ ہے کہ تمام فنکشن ایک ہی ٹیگز کی اطلاع دیتے ہیں۔ ہر کام کے لیے وضاحتی ٹیگز کے ساتھ علیحدہ تالے بنانے میں بہت کم لاگت آتی ہے اور یہ لیک کے ذریعہ کا تعین کرنا بہت آسان بنا دیتا ہے۔
اگر تالے کا اشتراک کیا گیا ہے، تو حوالہ شمار کو غیر فعال کریں یا اس بات کو یقینی بنائیں کہ ایک ہی مالک تالا کا انتظام کرتا ہے۔
پراپرٹی صحیح طریقے سے کام کرتی ہے۔
اگر آپ کا کوڈ کسی اور ایپ یا UID (سسٹم سروسز اور SDKs میں عام) کی جانب سے ویک لاک حاصل کرتا ہے، تو کال کریں: setWorkSource() لہذا بیٹری کی قیمت کا بل صحیح فریق کو دیا جائے گا۔ ایپس کو شاذ و نادر ہی اس خصوصیت کی ضرورت ہوتی ہے، لیکن پلیٹ فارم اور OEM انجینئرز کو یہ جاننے کی ضرورت ہوتی ہے کہ یہ موجود ہے۔
جب آپ کو ویک لاک کی بالکل بھی ضرورت نہ ہو۔
ویک لاک لیک کو روکنے کا بہترین طریقہ یہ ہے کہ ویک لاک کو نہ پکڑا جائے۔ جدید اینڈرائیڈ میں بیک گراؤنڈ کے زیادہ تر کاموں کو اعلیٰ سطح کے APIs کا استعمال کرنا چاہیے جو اندرونی طور پر ویک لاک کا انتظام کرتے ہیں۔
| ضروری | دستی ویک لاک کے بجائے استعمال کریں۔ |
|---|---|
| موخر پس منظر کے کام (مطابقت پذیری، اپ لوڈ، صفائی) | ورک مینیجر یا جاب شیڈیولر |
| وہ کام جن کو عین وقت پر انجام دیا جانا چاہیے۔ | AlarmManager.setExactAndAllowWhileIdle() اس کے علاوہ یہ ایک مختصر کام ہے۔ |
| براڈکاسٹ کے بعد مختصر فالو اپ | BroadcastReceiver.goAsync() |
| طویل عرصے سے چلنے والے کام جن کے بارے میں آپ جانتے ہیں (موسیقی، نیویگیشن، ورزش) | پیش منظر کی خدمت کی مناسب قسم |
| سرگرمی دکھاتے وقت اسکرین کو آن رکھیں | FLAG_KEEP_SCREEN_ON |
| صارف کی طرف سے شروع کردہ ڈیٹا ٹرانسفر | صارف کے ذریعے شروع کردہ ڈیٹا ٹرانسفر آپریشنز (API 34 اور اس سے زیادہ) |
بائیں کالم اس بات کی وضاحت کرتا ہے کہ ایپ کیا کرنے کی کوشش کر رہی ہے، اور دائیں کالم API کا نام دیتا ہے جو اسے ہینڈل کرتا ہے۔ عام دھاگہ یہ ہے کہ ان APIs میں سے ہر ایک آپ کی طرف سے ویک لاک لیتا ہے اور آپریشن مکمل ہونے یا وقت ختم ہونے پر اسے جاری کرتا ہے۔
ورک مینیجر اور جاب شیڈیولر مندرجہ ذیل ادوار کے لیے ویک لاک رکھتے ہیں: doWork() یا onStartJob() عملدرآمد کی حدیں لاگو کریں۔ goAsync() سسٹم کے براڈکاسٹ ویک لاک کو تقریباً 10 سیکنڈ تک بڑھاتا ہے جب یہ اپنا کام مکمل کرتا ہے۔ FLAG_KEEP_SCREEN_ON یہ ونڈو کی مرئیت سے منسلک ہے اور صارف کے جانے پر غائب ہو جاتا ہے۔
یہاں ایک ورک مینیجر کارکن ہے جو اپ لوڈ کی مثال کو بدل دیتا ہے:
class UploadWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
uploadPendingFiles()
Result.success()
} catch (e: IOException) {
Result.retry()
}
}
}
CoroutineWorker چلائیں doWork() جاب شیڈیولر کے پاس ویک لاک ہے۔ جب doWork() ویک لاک جاری کیا جاتا ہے اس سے قطع نظر کہ نتیجہ کامیابی، ناکامی، یا دوبارہ کوشش ہے۔ اگر کوئی ٹاسک زیادہ دیر تک چلتا ہے، تو سسٹم ٹاسک کو روکتا ہے اور کورٹین کو منسوخ کر دیتا ہے۔ موجود نہیں ہے acquire() یا release() یہ کوڈ میں ہے، لہذا لیک کرنے کے لئے کچھ نہیں ہے. واپسی کے راستے میں Result.retry() I/O کی ناکامی پر، CPU کو فعال رکھنے کے بجائے جب آپ کی ایپ دوبارہ کوشش کرتی ہے تو، WorkManager دوسری کوشش کو شیڈول کرنے کے لیے بیک آف کا استعمال کر سکتا ہے۔
دستی ویک لاک اب بھی موجود ہے۔ میڈیا پلیئرز، VoIP سٹیکس، اور ایپس جو بلوٹوتھ کے ذریعے منسلک آلات سے ڈیٹا کو سٹریم کرتی ہیں، اکثر پیش منظر سروس کے فعال ہونے کے دوران CPU کو چلتے رہنے کی ضرورت ہوتی ہے۔ اس صورت میں، سروس لائف سائیکل کی مالک ہے اور ویک لاک کو اس سے حاصل کیا جانا چاہیے: onCreate() یا جب پلے بیک شروع ہوتا ہے اور میچنگ اسٹاپ پاتھ سے ریلیز ہوتا ہے۔ onDestroy().
یہ جاننا بھی مددگار ہے کہ نیند کا موڈ محدود کرتا ہے کہ آپ ویک لاک کے ساتھ کیا کر سکتے ہیں۔ جب آلہ سلیپ موڈ میں داخل ہوتا ہے، تو سسٹم کسی بھی جزوی ویک لاک کو نظر انداز کر دیتا ہے جو ایپس کے ذریعہ رکھے گئے ہیں جو بیٹری آپٹیمائزیشن وائٹ لسٹ میں نہیں ہیں۔ یہ لیک ہونے والا لائسنس نہیں ہے، کیونکہ مینٹیننس ونڈو کے دوران اور ڈیوائس کے فعال ہونے کے دوران لاک کا حساب لگانا جاری رہتا ہے، لیکن یہ بتاتا ہے کہ کیوں اکیلا ویک لاک اس بات کی ضمانت نہیں دیتا کہ آپ کا کوڈ رات بھر چلتا رہے گا۔
ویک لاک لیک کا پتہ لگانے کا طریقہ
ڈویلپمنٹ کے دوران لیک شاذ و نادر ہی ظاہر ہوتے ہیں کیونکہ ڈیوائس منسلک ہے، اسکرین آن ہے، اور کوئی اور چیز اسے جگا رہی ہے۔ آپ کو اسے جان بوجھ کر تلاش کرنا ہوگا۔
ڈمپسی پاور کے ساتھ فی الحال رکھے ہوئے تالے کو چیک کریں۔
پوچھنا تلاش کرنے کا تیز ترین طریقہ ہے۔ PowerManagerService اب تم کیا پکڑے ہوئے ہو؟
adb shell dumpsys power | grep -A 20 "Wake Locks:"
یہ کمانڈ پاور مینیجر کی پوری حالت کو ڈمپ کرتا ہے اور اسے ویک لاک سیکشن میں فلٹر کرتا ہے۔ آؤٹ پٹ تمام ایپ لیول ویک لاکس کی فہرست دیتا ہے جو فی لائن رکھے ہوئے ہیں۔
Wake Locks: size=2
PARTIAL_WAKE_LOCK 'myapp:upload' ACQ=-14m32s110ms (uid=10245 pid=8123)
PARTIAL_WAKE_LOCK 'AudioMix' ACQ=-3s204ms (uid=1041 ws=WorkSource{10112})
ہر لائن میں لاک لیول، ٹیگ، اور حاصل شدہ وقت ہوتا ہے (ACQ) کا مالک UID، اور عمل۔
اس مثال میں myapp:upload اسے 14 منٹ سے زیادہ کے لیے رکھا گیا ہے، جو کہ عام طور پر لیک ہونے کا ایک مضبوط اشارہ ہوتا ہے اگر اسے اپ لوڈ کرنے میں چند سیکنڈ لگتے ہیں۔ دوسرا آئٹم دوسرے ایپ کی جانب سے آڈیو سرور کے پاس ہے، جیسا کہ اس کے ایکشن سورس میں اشارہ کیا گیا ہے۔ اسکرین کو بند کریں، ایک لمحہ انتظار کریں، اور پھر اس کمانڈ کو دوبارہ چلائیں۔ ایپ میں کوئی بھی تالے اب بھی اس کے بڑھتے ہی موجود رہتے ہیں۔ ACQ یہ تحقیق کرنے کے لیے آپ کے وقت کے قابل ہے۔
بیٹری کے اعدادوشمار کے ساتھ ٹوٹل کی پیمائش کریں۔
dumpsys power موجودہ لمحہ دکھاتا ہے۔ وقت کی ایک مدت میں جمع ہونے والے ویک لاک ٹائم کو دیکھنے کے لیے بیٹری کے اعدادوشمار استعمال کریں۔
adb shell dumpsys batterystats --reset
# unplug, use the device normally or leave it idle for a while
adb shell dumpsys batterystats com.example.myapp | grep -i "wake lock"
پہلی کمانڈ جمع کردہ اعدادوشمار کو صاف کرتی ہے اور پیمائش کو نئے سرے سے شروع کرنے کا سبب بنتی ہے۔ ڈیوائس کو بیٹری پر تھوڑی دیر تک چلانے کے بعد (کم از کم ایک گھنٹہ مفید نمبر دینا چاہیے)، دوسری کمانڈ ویک لاک لائن کے ذریعے فلٹر کیے گئے پیکیجز کے اعدادوشمار پرنٹ کرتی ہے۔
ہر ٹیگ اس کے کل قبضے کے وقت اور حصول کی تعداد کے ساتھ ظاہر ہوتا ہے۔ زیادہ کل وقت اور کم حصولی گنتی والے ٹیگز کا عام طور پر مطلب یہ ہوتا ہے کہ تالا کافی دیر تک رکھا گیا تھا اور اسے فوری طور پر جاری نہیں کیا گیا تھا۔
بصری ٹائم لائن کے لیے، استعمال کرتے ہوئے بگ رپورٹس کیپچر کریں: adb bugreport بیٹری ہسٹورین میں لوڈ کریں یا پرفیٹو ٹریس کو ریکارڈ کریں۔ android.power ڈیٹا سورس چالو ہو گیا ہے۔ دونوں ویک لاکس اسکرین کی حالت، نیٹ ورک کی سرگرمی، اور CPU فریکوئنسی کے ساتھ والی ٹائم لائن پر بارز کے طور پر ڈسپلے کرتے ہیں، جس سے یہ دیکھنا آسان ہو جاتا ہے کہ کون سے تالے منسلک سرگرمی کے رکنے کے بعد طویل عرصے تک برقرار رہتے ہیں۔
Play Console پر Android Vitals دیکھیں
گوگل پلے اینڈرائیڈ وائٹلز میٹرک کے ساتھ حد سے زیادہ جزوی ویک لاک کے استعمال کو ٹریک کرتا ہے۔ صارف کے سیشن کو حد سے تجاوز سمجھا جاتا ہے اگر ایپ کے پاس بیک گراؤنڈ میں رہتے ہوئے ایک طویل مجموعی مدت (موجودہ تعریف 24 گھنٹے کی ونڈو کے اندر 2 گھنٹے سے زیادہ ہے) کے لیے ناقابل معافی جزوی ویک لاک رکھتا ہے۔ کچھ سسٹم ایڈمنسٹریشن ٹاسکس، جیسے آڈیو پلے بیک یا مخصوص قسم کے کاموں کے ذریعے برقرار رکھنے والا ویک لاک مستثنیٰ ہے۔
پلے نے اسے ایک اہم عنصر بنا دیا ہے۔ اس کا مطلب یہ ہے کہ جو ایپس برے رویے کے معیار سے تجاوز کرتی ہیں ان کی اسٹور میں مرئیت کم ہو سکتی ہے اور ان کی فہرستوں میں انتباہات نظر آ سکتے ہیں۔ عین حد اور استثنیٰ وقت کے ساتھ بدلتے رہتے ہیں، اس لیے تازہ ترین Android Vitals دستاویزات چیک کریں۔ تاہم، عملی مضمرات مستحکم ہیں۔ ایک لیکی ویک لاک اب صرف بیٹری کی شکایت نہیں ہے، یہ اسٹور کی درجہ بندی کا مسئلہ ہے۔
لنٹ چیکنگ کو فعال کریں۔
Android Lint میں دو متعلقہ چیک شامل ہیں: Wakelock فلیگ کوڈ والے راستے جہاں ایک لاک حاصل کر لیا گیا ہے لیکن اسے جاری نہیں کیا جا سکتا۔ WakelockTimeout جھنڈے بغیر ٹائم آؤٹ کے جیت جاتے ہیں۔ دونوں بطور انتباہ بطور ڈیفالٹ فعال ہیں۔ اگر کوئی غلطی ہو جائے۔ lint.xml یا آپ کی گریڈل کنفیگریشن نئی لیکس کو ضم کرنا بند کر دیتی ہے۔
android {
lint {
error += listOf("Wakelock", "WakelockTimeout")
}
}
یہ گریڈل بلاک انتباہ سے غلطی تک دونوں چیکوں کو فروغ دیتا ہے، لہذا اگر کسی بھی پیٹرن کا سامنا ہوتا ہے، تو تعمیر ناکام ہوجائے گی۔ Lint جامد ہے اور ہر غیر مطابقت پذیر راستے کی پیروی نہیں کر سکتا، لہذا یہ پہلے بیان کردہ کال بیک لیکس کو نہیں پکڑتا، لیکن یہ سادہ کیسز کو سستے میں پکڑتا ہے۔
ویک لاک سلوک کی جانچ کیسے کریں۔
ویک لاک کو ہینڈل کرنا روبو الیکٹرک کا استعمال کرتے ہوئے یونٹ ٹیسٹ کے ساتھ آسانی سے ہینڈل کیا جا سکتا ہے، جو درج ذیل شیڈو نفاذ فراہم کرتا ہے: PowerManager.
@RunWith(RobolectricTestRunner::class)
class ContactSyncTest {
@Test
fun releasesWakeLockWhenSyncFails() {
val context = ApplicationProvider.getApplicationContext()
val syncer = ContactSyncer(context, api = FailingApi())
runCatching { syncer.syncContacts() }
val lock = ShadowPowerManager.getLatestWakeLock()
assertThat(lock).isNotNull()
assertThat(lock.isHeld).isFalse()
}
}
ٹیسٹ جعلی API کا استعمال کرتے ہوئے ٹیسٹ کے تحت جزو بناتے ہیں، جو ہمیشہ ہوتا ہے۔ مطابقت پذیری کو چلائیں، کسی بھی استثناء کو نظر انداز کریں، اور پھر Robolectric سے حال ہی میں بنائے گئے ویک لاک کے لیے پوچھیں۔
دونوں دعوے اس بات کی تصدیق کرتے ہیں کہ تالا دراصل استعمال کیا گیا تھا اور یہ کہ ناکامی کے بعد اسے مزید منعقد نہیں کیا جائے گا۔ اس طرح کے ٹیسٹ تمام خرابی کے راستوں کے لیے لکھنے کے لیے سستے ہیں اور جائزے میں انتہائی مشکل استثنیٰ اور قبل از وقت واپسی کے لیک کو درست طریقے سے پکڑتے ہیں۔ انجکشن WakeLock (یا اس کے ارد گرد ایک چھوٹا ریپر انٹرفیس) آپ کی کلاس کے لیے یہ آسان بناتا ہے، کیونکہ آپ روبو الیکٹرک کے بغیر سادہ جعلی استعمال کر سکتے ہیں۔
فریم ورک کے تحت ویک لاک
اگر آپ پلیٹ فارم کوڈ، HAL، یا بنیادی ڈیمون کے ساتھ کام کر رہے ہیں، تو آپ ویک لاک کو ایک پرت نیچے ہینڈل کرتے ہیں۔ مقامی کوڈ کہا جا سکتا ہے۔ acquire_wake_lock() اور release_wake_lock() سے libpowerموجودہ اینڈرائیڈ ورژنز کے لیے، یہ اقدامات ہیں: SystemSuspend ایک خدمت جو دانا کے انٹرفیس کی مالک ہے۔ /sys/power/wake_lock فائل
اے آئی ڈی ایل انٹرفیس SystemSuspend واپسی IWakeLock اعتراض کا acquireWakeLock(). تالے کو تب تک رکھا جاتا ہے جب تک آبجیکٹ زندہ رہتا ہے اور آخری حوالہ حذف ہونے پر جاری کیا جاتا ہے۔ یہ مقامی کوڈ کو کوٹلن کی طرح لمبی عمر کا دائرہ فراہم کرتا ہے۔ finally پیٹرن
دانا جاگنے کے ذرائع کی مکمل فہرست کو ظاہر کرتا ہے (بشمول ڈرائیور کے پاس موجود)۔ PowerManagerService) نیچے /sys/class/wakeup/ حالیہ دانا میں یا /sys/kernel/debug/wakeup_sources ڈیبگ کے ساتھ پرانے میں۔ پڑھنے کے لیے اعلیٰ مراعات کی ضرورت ہے۔ جب dumpsys power اگر آپ کو ایپ ویک لاک نظر نہیں آتا ہے، لیکن ڈیوائس پھر بھی معطل کرنے سے انکار کرتی ہے، تو یہ عام طور پر ڈرائیور یا ڈیفالٹ ڈیمون ہوتا ہے جو کرنل ویک سورس رکھتا ہے، اور یہ فائل آپ کو دکھائے گی کہ کون سا ہے۔
خلاصہ
ویک لاک سسٹم کو بتاتا ہے کہ اسکرین آف ہونے پر بھی کچھ کوڈ کو CPU کو بیدار رکھنے کی ضرورت ہے۔ ایپ لیول ویک لاکز کا انتظام اس کے ذریعے کیا جاتا ہے: PowerManagerServiceUID اور ٹیگ کے ذریعے تمام تالے کو ٹریک کرتا ہے اور تمام تالے کے لیے ایک ہی کرنل ویک اپ سورس رکھتا ہے۔ اگر یہ غیر مقفل نہیں ہے کیونکہ اسے جاگتے رہنے کے لیے منتخب کیا گیا ہے، تو آلہ رکے گا نہیں اور بیٹری اس وقت تک ختم ہو جائے گا جب تک کہ اس کا وقت ختم نہ ہو جائے یا عمل ختم نہ ہو جائے۔
زیادہ تر رساو عام کنٹرول کے بہاؤ میں ہوتا ہے (استثناء اور قبل از وقت واپسی کو چھوڑ دیا جاتا ہے)۔ release()ایسی خرابی والی کال بیکس ہیں جنہیں آپ جاری کرنا بھول جاتے ہیں، حوالہ شمار جو توازن سے باہر ہیں، اور مکمل نہ ہونے والے کاموں پر تالے لگ جاتے ہیں۔
ترمیمات بھی اتنی ہی غیر معمولی ہیں۔ ہمیشہ ٹائم آؤٹ پاس کریں اور تھوڑی دیر بعد ریلیز کریں۔ finally بلاکس (مثالی طور پر ایک چھوٹے مددگار کے ذریعے withLock)، ہر ٹاسک کو منفرد طور پر ٹیگ شدہ لاک دیتا ہے اور لاک کے اندر کاموں پر ایک مقررہ تاریخ سیٹ کرتا ہے۔
زیادہ تر معاملات میں، ایک بہتر انتخاب دستی ویک لاک سے مکمل طور پر گریز کرنا ہے۔ ورک مینیجر، جاب شیڈیولر، goAsync()پیش منظر کی خدمت اور FLAG_KEEP_SCREEN_ON ہر کوئی آپ کے لیے تالے کا انتظام کرتا ہے اور جب آپ کام کر لیتے ہیں تو انہیں کھول دیتا ہے۔ اگر آپ کو واقعی اس کی ضرورت ہے تو اسے یہاں چیک کریں: dumpsys power, batterystatsپرفیٹو یا بیٹری ہسٹورین، لِنٹ، اور غلطی کے راستوں کا احاطہ کرنے والے یونٹ ٹیسٹ۔
Play اب ضرورت سے زیادہ ویک لاکس کو ایک اہم عنصر کے طور پر دیکھتا ہے، لہذا ریلیز سے پہلے ان لیکس کو پکڑنا آپ کے صارفین کی بیٹری اور اسٹور میں آپ کی ایپ کی مرئیت کے لیے اہم ہے۔