تطبيق أندرويد أصلي مبني في مواجهة واجهة برمجة REST لعروض الشغل: التسجيل أو تسجيل الدخول، وتصفح العروض صفحةً بعد صفحة، والاحتفاظ محليًا بما يستحق العودة إليه — مع معالجة دورة حياة الرمز وطبقة HTTP مرة واحدة في مكان مركزي، بدل إعادة تعريفها في كل شاشة.
المشكلة والقيود
يطرح أي تطبيق جوّال يواجه واجهة برمجة محمية ثلاث مشكلات يسهل حلّها بشكل سيّئ: الرمز يصل بعد الإقلاع ويتغير عند الدخول والخروج، وكل طلب يحتاج الترويسات نفسها، وإعادة بناء طبقة HTTP مع كل استدعاء تُهدر مجمّع الاتصالات بصمت. كان القيد هو جمع ذلك كله في مكان واحد، بحيث لا تتعامل الشاشات إلا مع استدعاءات ذات أنواع محددة ونتائج.
النهج
- نسخة واحدة من Retrofit محفوظة بشكل ساكن ولا يُعاد بناؤها إلا عند تغيّر الرمز فعلًا، حتى يبقى مجمّع الاتصالات ومجمّع الخيوط في OkHttp حيًّا بين الطلبات.
- معترض OkHttp يضيف
Accept: application/json، ويضيفAuthorization: Bearer …عند وجود رمز — فلا تلمس الشاشات الترويسات أبدًا. - سطح الواجهة مقسوم حسب المسؤولية إلى واجهات ذات أنواع محددة:
AuthApi(تسجيل / دخول / خروج / الحساب) وJobsApi(قائمة / إنشاء)، مع غلاف عامPagedResponse<T>بحيث تُنمذَج التجزئة إلى صفحات مرة واحدة لا مع كل نقطة نهاية. - تُبدَّل المفضّلات محليًا على شكل
Set<String>من معرّفات العروض، فتبقى القائمة بعد إعادة التشغيل وتُقرأ دون رحلة إلى الخادم.
الوصف الكامل:
- activities — typed calls only → AuthApi — register, login, — logout, me
- activities — typed calls only → JobsApi — list, create — PagedResponse<T>
- activities — typed calls only → SharedPreferences — favorites: Set<String> (local)
- AuthApi — register, login, — logout, me → Retrofit + OkHttp — cached, keyed on token — Accept + Bearer
- JobsApi — list, create — PagedResponse<T> → Retrofit + OkHttp — cached, keyed on token — Accept + Bearer
- Retrofit + OkHttp — cached, keyed on token — Accept + Bearer → job-board API (bearer)
المفاضلات
Decision
نسخة Retrofit محفوظة ومرتبطة بالرمز بدل إنشاء واحدة مع كل استدعاء: تحافظ على إعادة استخدام الاتصالات وتتجنب إعادة تخصيص العميل مع كل طلب، مقابل منطق إبطال صريح — إذ على النسخة أن تعرف متى تغيّر الرمز، وهي حالة صغيرة أتحمّلها بدل تجاهلها.
Decision
المفضّلات في SharedPreferences بدل جدول Room: مجموعة من المعرّفات الرقمية لا تحتاج مخططًا ولا ترحيلًا ولا كائن وصول، وكلفة القراءة والكتابة مهملة عند هذا الحجم. أما Room وWorkManager فموجودان لمسار المزامنة، حيث تحتاج نسخة محلية حقيقية من قائمة العروض إلى بنية فعلية.
النتائج
دورة عملية من تسجيل الدخول إلى التصفح المجزّأ إلى الإضافة للمفضّلة، في مواجهة واجهة برمجة حيّة، مع عزل شؤون HTTP والمصادقة خلف واجهتين ومعترض واحد بدل تشتيتها في الشاشات.
ما كنت سأفعله بشكل مختلف
جعل HttpLoggingInterceptor بمستوى Level.BODY مشروطًا بـ
BuildConfig.DEBUG — فكما هو الآن، ستكتب نسخة الإصدار رموز Bearer
ومحتوى الاستجابات كاملًا في logcat، وهذا هو التغيير الوحيد الذي كنت
سأجريه قبل نشر أي شيء. وكنت سأنقل المفضّلات إلى الخادم أيضًا: فهي اليوم
مرتبطة بالجهاز، ما يعني أن الحساب نفسه على هاتف ثانٍ يبدأ فارغًا.