كيف تحمي تسجيلك الصوتي على أندرويد من 8 أخطاء قاتلة؟

فريق جلتش
٧ أبريل ٢٠٢٦0 مشاهدة4 دقائق
كيف تحمي تسجيلك الصوتي على أندرويد من 8 أخطاء قاتلة؟

"تُعد تسجيلات الصوت المستمرة على أندرويد تحديًا برمجيًا معقدًا يتجاوز مجرد استخدام MediaRecorder. يستعرض هذا المقال ثمانية أنواع من الانقطاعات المحتملة، من المكالمات الهاتفية وفقدان تركيز الصوت إلى مشكلات Android 14، وكيفية التعامل معها بكفاءة لضمان استمرارية التسجيل."

على الرغم من أن تسجيل الصوت قد يبدو مهمة بسيطة في عالم تطبيقات الهاتف المحمول، إلا أن الواقع على نظام أندرويد يختلف تمامًا. فبناء تطبيق يسجل الصوت بشكل مستمر يتطلب 10% من التركيز على MediaRecorder و90% على الدفاع ضد كل ما يحاول إيقافه.

إن إنشاء واجهة مستخدم بسيطة بقائمة وأزرار يمكن التنبؤ به، لكن تصميم تطبيق لتسجيل الصوت باستمرار ليس كذلك. فخلال بناء AudioMemo، وهو تطبيق أندرويد يسجل الصوت في مقاطع مدتها 30 ثانية ويحولها إلى نصوص عبر OpenAI Whisper ويلخصها باستخدام GPT-4o-mini، اتضح أن تهيئة MediaRecorder هي الجزء الأسهل. التحدي الحقيقي يكمن في التعامل مع كل ما يحاول إيقاف التسجيل دون سابق إنذار. هذه الانقطاعات تتراوح بين المكالمات الهاتفية، وتطبيقات مثل Spotify التي تستولي على تركيز الصوت (Audio Focus)، وإيقاف المستخدم للميكروفون من الإعدادات السريعة، وحتى تعطل خدمة الواجهة الأمامية (Foreground Service) على Android 14 بسبب سطر مفقود في ملف البيان (manifest). كل من هذه المشاكل تتطلب حلاً مختلفًا، والأسوأ من ذلك، يمكن أن تتداخل عدة انقطاعات، مما يخلق فئة جديدة من الأخطاء. يعتمد AudioMemo على بنية قوية تتضمن MediaRecorder، وRoom للتخزين المحلي، وWorkManager للمهام الخلفية، وForeground Service للحفاظ على التسجيل حيًا، بالإضافة إلى AudioInterruptionManager المخصص لمعالجة منطق الانقطاعات. استراتيجية التقطيع إلى أجزاء مدتها 30 ثانية هي خط الدفاع الأول، حيث يضمن ذلك عدم فقدان الجلسة بأكملها عند حدوث انقطاع، بل فقط الجزء الحالي.

قبل الخوض في تفاصيل الانقطاعات الفردية، يعتبر قرار التصميم المعماري الأكثر أهمية في AudioMemo هو منطق الإيقاف المؤقت المزدوج (بل الثلاثي) باستخدام الأعلام (flags). تخيل سيناريو حيث تصل مكالمة هاتفية بينما فقد تطبيقك تركيز الصوت بالفعل لتطبيق Spotify. عندما تنتهي المكالمة، سيتلقى تطبيقك CALL_STATE_IDLE. تنفيذ ساذج قد يستأنف التسجيل فورًا، لكن تركيز الصوت لا يزال مفقودًا، مما يؤدي إلى استئناف في حالة معطلة. يتتبع AudioMemo ثلاثة أعلام منطقية مستقلة داخل AudioInterruptionManager: pausedForCall, pausedForFocus, وpausedForMicMute. يتم استدعاء onResumeRequested() فقط عندما تكون جميع الأعلام الثلاثة غير نشطة، مما يمنع الاستئناف المبكر. أما AudioFocus، فهو نظام التحكم المروري في أندرويد للصوت. يجب على كل تطبيق يقوم بتشغيل أو تسجيل الصوت طلب التركيز أولاً. يعد استخدام USAGE_VOICE_COMMUNICATION بدلاً من USAGE_MEDIA أمرًا حيويًا، لأنه يخبر النظام بأن تطبيقك يسجل كلامًا، مما يغير كيفية استجابة التطبيقات الأخرى لطلب تركيز الصوت الخاص بك. بالنسبة للمكالمات الهاتفية، فإن المكالمة الواردة تستحوذ فعليًا على الميكروفون على مستوى الجهاز. يستمع AudioMemo لتغيرات حالة المكالمة ويوقف التسجيل قبل الاستيلاء على الميكروفون، باستخدام واجهة برمجة التطبيقات الحديثة (TelephonyCallback) على Android 12+ والواجهة القديمة (PhoneStateListener) للإصدارات الأقدم.

يُعد نوع خدمة الواجهة الأمامية (Foreground Service Type) على Android 14 سببًا شائعًا للتعطل في تطبيقات تسجيل الصوت التي تستهدف هذا الإصدار. فبدونه، يتعطل تطبيقك فور محاولته الوصول إلى الميكروفون من خدمة واجهة أمامية. يجب توفر ثلاثة أمور: إعلان الخدمة في ملف البيان (manifest) مع android:foregroundServiceType="microphone"، وإذن android.permission.FOREGROUND_SERVICE_MICROPHONE، واستدعاء startForeground مع ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE. إذا فقدت أيًا من هذه الثلاثة، سيتعطل تطبيقك على كل جهاز يعمل بنظام Android 14. إضافة إلى ذلك، أضاف Android 12 زر تبديل للميكروفون على مستوى الجهاز في الإعدادات السريعة. يمكن للمستخدم تعطيل الميكروفون على مستوى النظام بينما يسجل تطبيقك. الجزء الوحشي هنا هو أنك تحصل على صمت، وليس استثناءً؛ MediaRecorder يستمر في العمل بسعادة، وينتج ملفات صوتية فارغة. يتعامل AudioMemo مع هذا عن طريق التحقق من الحالة الأولية للميكروفون عند بدء التسجيل، ويسجل لمستقبل بث لتغيير حالة كتم الصوت (mute) عبر ACTION_MICROPHONE_MUTE_CHANGED.

مع تقديم Android 11 لإعادة تعيين الأذونات تلقائيًا للتطبيقات غير المستخدمة لفترات طويلة، وإضافة Android 12 لأذونات الاستخدام لمرة واحدة (one-time permissions)، قد يسحب المستخدم إذن الميكروفون، مما يؤدي إلى SecurityException في الجلسة التالية. يتطلب الحل إعادة التحقق من الإذن في كل مرة يتم فيها إنشاء شاشة التسجيل، وليس فقط عند الإطلاق الأول، بالإضافة إلى معالجة SecurityException بشكل منفصل في الخدمة. أما بالنسبة لبلوتوث SCO (Synchronous Connection Oriented)، فإذا كان المستخدم يرتدي سماعة رأس بلوتوث، يجب أن يتم توجيه التسجيل إلى ميكروفون السماعة. ولكن اتصال بلوتوث SCO يتم بشكل غير متزامن. إذا بدأت التسجيل قبل اتصال SCO، فإنك تعود بصمت إلى ميكروفون الجهاز أو تسجل لا شيء على الإطلاق. يتحقق AudioMemo من وجود جهاز إدخال SCO متصل عند بدء التشغيل ولا يبدأ التسجيل إلا داخل رد الاتصال (callback) الخاص بالاتصال، باستخدام AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.

عندما يقتل نظام التشغيل عملية تطبيقك أثناء التسجيل (بسبب ضغط الذاكرة أو تحسينات البطارية القوية)، تُفقد الشريحة الحالية. لكن الشرائح التي تم تحميلها بالفعل تظل آمنة في Room. يحل AudioMemo هذه المشكلة باستخدام ChunkFinalizationWorker الذي يتم جدولته بتأخير 15 ثانية لحظة بدء التسجيل؛ إذا ماتت العملية، يقوم WorkManager بتحديد الشرائح قيد المعالجة كـ FAILED ويعيد جدولتها للمحاولة. أما فشل الشبكة أثناء النسخ الصوتي، فيعتمد AudioMemo على ميزة إعادة المحاولة المضمنة في WorkManager مع التراجع الأسي (exponential backoff). على الرغم من هذه الجهود المعمارية الشاملة، يظل التحدي الأكبر لتطبيقات الصوت المستمرة هو التكيف المستمر مع التغييرات في سلوك النظام وتحسينات مصنعي الأجهزة الأصلية (OEMs) التي قد تؤثر على موثوقية العمليات الخلفية، مما يجعلها معركة مستمرة لضمان تجربة مستخدم سلسة في كل الظروف.

أعجبك المقال؟ شاركه

النشرة البريدية

كن أول من يعرف بمستقبل التقنية

أهم الأخبار والتحليلات التقنية مباشرة في بريدك.