لإصدار تطبيقات لمنصة متنقلة واحدة فقط غير ملائم وتحتاج إلى الاهتمام بتطوير نسختين في وقت واحد لنظامي iOS و Android. ويمكنك هنا اختيار طريقتين: العمل بلغات البرمجة "الأصلية" لكل نظام تشغيل أو استخدام أطر عمل عبر الأنظمة الأساسية.
عند تطوير أحد المشاريع في DD Planet ، اعتمدت على الخيار الأخير. وسأتحدث في هذه المقالة عن تجربة تطوير تطبيق عبر الأنظمة الأساسية والمشكلات التي واجهناها والحلول التي تم العثور عليها.
ثلاث طرق لتطوير تطبيقات الهاتف المحمول عبر الأنظمة الأساسية
للبدء ، ضع في اعتبارك الأساليب المستخدمة عندما تحتاج إلى الحصول على تطبيقين في وقت واحد: لنظامي iOS و Android.
الأول هو الأغلى ، في الوقت والموارد على حد سواء: تطوير تطبيق منفصل لكل منصة. يكمن تعقيد هذا النهج في حقيقة أن كل نظام من أنظمة التشغيل يتطلب منهجه الخاص: يتم التعبير عن ذلك في كل من اللغة التي يجري فيها التطوير (لنظام Android - Java أو Kotlin ، لنظام iOS - Objective-C أو Swift) ، وطرق لوصف جزء واجهة المستخدم التطبيقات (axml و xib أو ملفات لوحة العمل ، على التوالي).
هذه الحقيقة وحدها تقودنا إلى حقيقة أنه من أجل هذا النهج ، من الضروري تشكيل فريقين للتطوير. بالإضافة إلى ذلك ، سيكون عليك تكرار المنطق لكل من المنصات: التفاعل مع واجهة برمجة التطبيقات ومنطق الأعمال.
ولكن ماذا لو زاد عدد واجهات برمجة التطبيقات المستخدمة؟
يطرح هذا السؤال: كيفية تقليل الكمية المطلوبة من الموارد البشرية؟ تخلص من الحاجة إلى تكرار التعليمات البرمجية لكل نظام أساسي. هناك عدد كاف من الأطر والتقنيات التي تحل هذه المشكلة.
إن استخدام إطار عمل متعدد المنصات (Xamarin.Forms ، على سبيل المثال) يجعل من الممكن كتابة التعليمات البرمجية بلغة برمجة واحدة ووصف منطق البيانات ومنطق واجهة المستخدم مرة واحدة ، في مكان واحد. لذلك ، تختفي الحاجة إلى استخدام فريقي تطوير. ونتيجة لتجميع المشروع ، نحصل على تطبيقين أصليين عند الإخراج. وهذا هو النهج الثاني.
أعتقد أن الكثيرين يعرفون ما هو Xamarin ، أو على الأقل سمعوا عنه ، ولكن كيف يعمل؟ يعتمد Xamarin على تطبيق مفتوح المصدر لمنصة .NET - Mono. يحتوي Mono على المترجم C # الخاص به ، ووقت التشغيل ، بالإضافة إلى عدد من المكتبات ، بما في ذلك تنفيذ WinForms و ASP.Net.
الهدف من المشروع هو السماح بتشغيل البرامج المكتوبة بلغة C # على أنظمة تشغيل أخرى بخلاف Windows - أنظمة Unix و Mac OS وغيرها. إطار Xamarin نفسه ، في جوهره ، عبارة عن مكتبة فئة توفر للمطورين الوصول إلى SDK للنظام الأساسي والمجمعين لهذه. Xamarin.Forms ، بدوره ، يسمح لك ليس فقط لكلا النظامين الأساسيين بنفس اللغة ، ولكن أيضًا تصميم شاشات باستخدام ترميز XAML ، مألوف لأولئك الذين لديهم بالفعل خبرة مع تطبيقات WPF. نتيجة لتجميع المشروع ، نحصل على نظرة متطابقة تقريبًا على جميع الأنظمة الأساسية ، لأنه في مرحلة التجميع يتم تحويل جميع عناصر التحكم XF إلى أصلية لكل نظام أساسي.
لا يضطر المطور إلى كتابة رمز لكل نظام أساسي إلا إذا كانت هناك حاجة إلى الوصول إلى أي ميزات للنظام الأساسي (على سبيل المثال ، ماسح بصمات الأصابع أو مستوى البطارية) أو كان من الضروري ضبط سلوك التحكم. في بعض الحالات ، عند تطوير تطبيق ، قد يكون من الضروري كتابة تعليمات برمجية تعتمد على النظام الأساسي ، ولكن حتى في هذه الحالة لا يمنع أحد نقل وظائف النظام الأساسي إلى الواجهة والتفاعل معها من المشروع العام في المستقبل.
لغة برمجة واحدة ، كود صغير ، وهكذا. يبدو كل شيء جميلًا ، ولكن Xamarin.Forms ليست رصاصة فضية ، وكل جمالها يكسر حجارة الواقع. بمجرد ظهور موقف عندما لم تعد عناصر التحكم XF المدمجة تفي بمتطلباتها ، يصبح هيكل الشاشات وعناصر التحكم أكثر تعقيدًا. لضمان العمل المريح مع الشاشات من مشروع مشترك ، يجب عليك كتابة المزيد والمزيد من العروض المخصصة.
سينتقل هذا إلى النهج الثالث ، الذي نستخدمه عند تطوير التطبيقات.
لقد اكتشفنا بالفعل أن استخدام Xamarin Forms يمكن أن يعقد العمل ، بدلاً من تبسيطه. لذلك ، لتنفيذ الشاشات المعقدة معماريًا ، تم العثور على عناصر التصميم وعناصر التحكم التي تختلف اختلافًا جوهريًا عن تلك الأصلية ، وتم التوصل إلى حل وسط وإمكانية الجمع بين النهجين الأول والثاني.
لدينا جميع المشاريع الثلاثة نفسها: مشروع PCL مشترك ، ولكن بدون Xamarin Forms ، ومشروعين Xamarin Android و Xamarin iOS. لا يزال من الممكن كتابة كل شيء بلغة واحدة ، والمنطق المشترك بين مشروعين ، ولكن لا توجد قيود على ترميز XAML واحد. يتم التحكم في مكون واجهة المستخدم بواسطة كل نظام أساسي ويستخدم أدوات أصلية على Android - AXML الأصلي ، على ملفات iOS - XIB. كل منصة لديها القدرة على الامتثال لإرشاداتها ، حيث يتم تنظيم الاتصال بين Core ومشاريع النظام الأساسي فقط على مستوى البيانات.
لتنظيم مثل هذه العلاقة ، يمكنك استخدام نمط تصميم MVVM وتطبيقه الشائع إلى حد ما لـ Xamarin - MVVMCross. يتيح لك استخدامه الاحتفاظ بنموذج ViewModel شائع لكل شاشة ، والذي يصف "منطق العمل" بأكمله للعمل ، ويعهد بتقديمه إلى النظام الأساسي. كما يسمح لمطورين اثنين بالعمل مع نفس الشاشة (أحدهما بالمنطق - والآخر مع واجهة المستخدم) وعدم التدخل في بعضهما البعض. بالإضافة إلى تنفيذ النمط ، نحصل على عدد كافٍ من الأدوات للعمل: تنفيذ DI و IoC. لرفع التفاعل مع النظام الأساسي إلى مستوى الرمز المشترك ، يحتاج المطور فقط إلى إعلان واجهة وتنفيذها على النظام الأساسي. بالنسبة للأشياء النموذجية ، يوفر MvvmCross بالفعل مجموعة من المكونات الإضافية الخاصة به. في الفريق ، نستخدم المكوّن الإضافي messenger لتبادل الرسائل بين النظام الأساسي والرمز المشترك والمكوِّن الإضافي للعمل مع الملفات (اختيار الصور من المعرض ، وما إلى ذلك).
نحل مشاكل التصميم المعقد والملاحة متعددة المستويات
كما ذكرنا سابقًا ، عند استخدام تمثيلات معقدة على الشاشة ، يمكن للإطار أن يعقد الحياة أكثر من أن يجعلها أسهل. ولكن ما يسمى العنصر المعقد؟ نظرًا لأنني منخرط بشكل رئيسي في تطوير iOS ، فسيتم النظر في مثال لهذه المنصة. على سبيل المثال ، يمكن لمثل هذا الشيء التافه مثل حقل الإدخال أن يحتوي على العديد من الحالات والمنطق الكافي للتبديل والتصور.
أثناء العمل مع إدخال المستخدم ، تم تطوير عنصر تحكم الإدخال هنا. يمكنه رفع اسمه فوق حقل الإدخال ، والعمل مع الأقنعة ، وتعيين البادئات ، والإصلاحات اللاحقة ، والإبلاغ عند الضغط على CapsLock ، والتحقق من صحة المعلومات في وضعين: حظر الإدخال وإخراج معلومات الخطأ. يستغرق المنطق داخل عنصر التحكم حوالي 1000 خط تقريبًا. وقد يبدو: ما الذي يمكن أن يكون معقدًا في تصميم مجال الإدخال؟
مثال بسيط على التحكم المعقد الذي رأيناه. ماذا عن الشاشات؟
بادئ ذي بدء ، سأوضح أنه في معظم الحالات شاشة تطبيق واحدة هي فئة واحدة - UIViewController ، تصف سلوكها. أثناء التطوير ، كان مطلوبًا إنشاء التنقل متعدد المستويات. يأتي مفهوم التطبيق المطور لإدارة عقارك والتفاعل مع الجيران والمنظمات البلدية. لذلك ، تم بناء ثلاثة مستويات من التنقل: الملكية ومستوى العرض (المنزل والمدينة والمنطقة) ونوع المحتوى. يتم إجراء جميع عمليات التحويل في شاشة واحدة.
تم ذلك حتى يفهم المستخدم ، أينما كان ، نوع المحتوى الذي يراه. لتنظيم هذا التنقل ، لا تتكون الشاشة الرئيسية للتطبيق من وحدة تحكم واحدة فقط. بصريا ، يمكن تقسيمها إلى 3 أجزاء ، ولكن يمكن لأي شخص أن يحاول تخمين عدد وحدات التحكم المستخدمة هنا؟
خمسة عشر وحدة تحكم رئيسية. وهذا فقط للمحتوى.
هنا يعيش مثل هذا الوحش على الشاشة الرئيسية ويشعر بالرضا. 15 وحدة تحكم لشاشة واحدة ، بالطبع ، الكثير. يؤثر هذا على سرعة التطبيق بالكامل ، وتحتاج إلى تحسينه بطريقة أو بأخرى.
رفضنا التهيئة المتزامنة: تتم تهيئة جميع نماذج العرض في الخلفية وفقط عند الضرورة. لتقليل وقت العرض ، قمنا أيضًا بالتخلي عن ملفات xib لهذه الشاشات: تحديد الموقع المطلق والرياضيات دائمًا أسرع من حساب التبعيات بين العناصر.
لتتبع العديد من وحدات التحكم تحتاج إلى فهم:
- ما هي حالة كل منهم ؛
- أين المستخدم؟
- ما يتوقع رؤيته عند الانتقال إلى وحدة تحكم أخرى.
للقيام بذلك ، كتبت معالج تنقل منفصلًا يخزن معلومات حول موقع المستخدم ، ونوع المحتوى الذي يشاهده ، وسجل التنقل ، وما إلى ذلك. يتحكم في ترتيب وضرورة التهيئة.
نظرًا لأن كل علامة تبويب عبارة عن شريط تمرير وحدة تحكم (من أجل إنشاء انتقال سريع عليها) ، عليك أن تفهم: يمكن أن يكون كل واحد منهم في حالته الخاصة (على سبيل المثال ، "الأخبار" مفتوحة على أحدهما ، و "التصويت" من جهة أخرى). ويلي ذلك نفس معالج الملاحة. حتى عند تغيير مستوى العرض التقديمي من المنزل إلى المنطقة ، سنبقى على نفس نوع المحتوى.
نحن نتحكم في تدفق البيانات في الوقت الحقيقي
من خلال العمل مع الكثير من البيانات في التطبيق ، تحتاج إلى تنظيم تسليم المعلومات ذات الصلة في جميع الأقسام في الوقت الحقيقي. لحل هذه المشكلة ، يمكن تمييز 3 طرق:
- الوصول إلى واجهة برمجة التطبيقات من خلال المؤقتات أو المشغلات وإعادة طلب المحتوى ذي الصلة على الشاشات ؛
- لديك اتصال دائم بالخادم وتلقي التغييرات في الوقت الحقيقي ؛
- تلقي دفعة مع تغييرات المحتوى.
لكل أسلوب إيجابياته وسلبياته ، لذا من الأفضل استخدام الثلاثة ، واختيار نقاط القوة لكل منها فقط. قمنا بتقسيم المحتوى داخل التطبيق بشكل مشروط إلى عدة أنواع: ساخنة ، عادية ، وخدمة. يتم ذلك من أجل تحديد الوقت المقبول بين الحدث وإخطار المستخدم. على سبيل المثال ، نريد أن نرى رسالة دردشة مباشرة بعد إرسالها إلينا - هذا محتوى ساخن. خيار آخر: استطلاع من الجيران. لا فرق عندما نراه الآن أو بعد دقيقة ، لأن هذا محتوى عادي. إن الإشعارات الصغيرة داخل التطبيق (الرسائل غير المقروءة ، والأوامر ، وما إلى ذلك) هي محتوى خدمة يحتاج إلى تسليم عاجل ، ولكنه لا يستهلك الكثير من البيانات.
اتضح:
- المحتوى الساخن - اتصال دائم بواجهة برمجة التطبيقات ؛
- المحتوى العادي - طلبات http إلى API ؛
- محتوى النظام - دفع الإخطارات.
الشيء الأكثر إثارة للاهتمام هو الحفاظ على اتصال مستمر. تعد كتابة عميلك للعمل مع مآخذ الويب خطوة في حفرة الأرنب ، لذلك تحتاج إلى البحث عن حلول أخرى. ونتيجة لذلك ، توقفنا عند مكتبة SignalR. دعنا نرى ما هو.
ASP.Net SignalR هي مكتبة من Microsoft تبسط التفاعل بين العميل والخادم في الوقت الحقيقي ، وتوفر اتصالًا ثنائي الاتجاه بين العميل والخادم. يتضمن الخادم واجهة برمجة تطبيقات كاملة لإدارة الاتصال وأحداث قطع الاتصال وآلية لدمج العملاء المتصلين في مجموعات والتفويض.
يمكن لـ SignalR استخدام طلبات websockets و LongPolling و http كوسيلة نقل. يمكنك تحديد نوع النقل بالقوة أو الثقة بالمكتبة: إذا كان من الممكن استخدام websocket ، فسيعمل من خلال websocket ، إذا لم يكن ذلك ممكنًا ، فسوف ينخفض حتى يجد وسيلة نقل مقبولة. تبين أن هذه الحقيقة عملية للغاية ، نظرًا لأنه من المخطط استخدامها على الأجهزة المحمولة.
المجموع ، ما هي الفوائد التي نحصل عليها:
- القدرة على تبادل الرسائل من أي نوع بين العميل والخادم ؛
- آلية التبديل تلقائيًا بين مقابس الويب وطلبات التجميع الطويل و Http ؛
- معلومات حول الوضع الحالي للاتصال ؛
- فرصة لتوحيد العملاء في مجموعات ؛
- الأساليب العملية للتعامل مع منطق إرسال الرسائل في مجموعة ؛
- القدرة على توسيع نطاق الخادم.
هذا ، بالطبع ، لا يلبي جميع الاحتياجات ، ولكنه يجعل الحياة أسهل بشكل ملحوظ.
داخل المشروع ، يتم استخدام غلاف في مكتبة SignalR ، مما يبسط العمل معه أكثر ، وهو:
- يراقب حالة الاتصال ، يعيد الاتصال وفقًا للظروف المحددة وفي حالة حدوث انقطاع ؛
- قادرة على استبدال الاتصال أو إعادة فتحه بسرعة ، مما يؤدي إلى قتل الاتصال القديم بشكل غير متزامن وإعطائه لمجمع القمامة للتمزق - كما اتضح ، تعمل طريقة الاتصال أسرع عشر مرات من طريقة الإغلاق (التخلص أو التوقف) ، وهذه هي الطريقة الوحيدة لإغلاقها ؛
- ينظم قائمة انتظار لإرسال الرسائل بحيث لا تؤدي إعادة الاتصال أو إعادة فتح الاتصال إلى مقاطعة الإرسال ؛
- التحكم في النقل إلى المندوبين المناسبين في حالة حدوث أخطاء غير متوقعة.
يعمل كل من هذه الأغلفة (نسميها عملاء) جنبًا إلى جنب مع نظام التخزين المؤقت ، وفي حالة انقطاع الاتصال ، يمكنه فقط طلب البيانات التي ربما فاتته خلال هذه الفترة. "كل" لأن العديد من المركبات النشطة يتم الاحتفاظ بها في وقت واحد. يوجد داخل التطبيق رسول كامل ، ويتم استخدام عميل منفصل لصيانته.
العميل الثاني مسؤول عن تلقي الإخطارات. كما قلت بالفعل ، يتم الحصول على النوع المعتاد من المحتوى من خلال طلبات http ، في المستقبل يقع تحديثه على هذا العميل ، الذي يبلغ عن جميع التغييرات المهمة فيه (على سبيل المثال ، انتقل التصويت من حالة إلى أخرى ، نشر أخبار جديدة).
تصور البيانات في التطبيق
الحصول على البيانات شيء ، والشيء شيء آخر. تحديث البيانات في الوقت الحقيقي له صعوباته الخاصة. كحد أدنى ، تحتاج إلى تحديد كيفية تقديم هذه التحديثات للمستخدم. في التطبيق نستخدم ثلاثة أنواع من الإخطارات:
- الإخطار بالمحتوى غير المقروء ؛
- التحديث التلقائي للبيانات على الشاشة ؛
- عرض المحتوى.
الطريقة الأكثر شيوعًا والعادية لإظهار وجود محتوى جديد في مكان ما هي إبراز رمز القسم. وبالتالي ، فإن جميع الرموز تقريبًا لديها القدرة على إظهار منبه المحتوى غير المقروء كنقطة حمراء. أكثر الأشياء إثارة للاهتمام مع التحديثات التلقائية.
لا يمكن تحديث البيانات تلقائيًا إلا عندما لا يعيد المحتوى الجديد ترتيب الشاشة ولا يغير حجم عناصر التحكم. على سبيل المثال ، على شاشة الاستطلاع: ستغير المعلومات حول الأصوات فقط قيمة شريط التقدم والنسب المئوية. لن تستتبع مثل هذه التغييرات أي تغيير حجم ؛ يمكن تطبيقها على الفور دون مشاكل.
تنشأ الصعوبات عندما تحتاج إلى إضافة محتوى جديد إلى القوائم. في الواقع ، جميع القوائم في التطبيق هي ScrollView ولها العديد من الخصائص: حجم النافذة وحجم المحتوى وموضع التمرير. كل منها لها بداية ثابتة (أعلى الشاشة بإحداثيات 0 ؛ 0) ويمكن أن تتوسع لأسفل. إضافة محتوى جديد أسفل القائمة ، في النهاية ، لا يقدم أي مشاكل ، القائمة ستستمر. ولكن يجب أن يظهر محتوى جديد في الأعلى ، وهذه هي الصورة:
كوننا على 3 عناصر ، سنكون على 2 - سوف ترتد التمرير لأعلى. وبما أن المحتوى الجديد يمكن أن يصل باستمرار ، فلن يتمكن المستخدم من التمرير بشكل طبيعي. قد تقول: لماذا لا تحسب حجم المحتوى الجديد وتحول التمرير لأسفل إلى هذه القيمة؟ نعم ، يمكن القيام بذلك. ولكن عليك بعد ذلك التحكم يدويًا في موضع التمرير ، وإذا انتقل المستخدم في هذه اللحظة في أي اتجاه ، فسيتم مقاطعة الإجراء. لهذا السبب لا يمكن تحديث هذه الشاشات في الوقت الحقيقي دون موافقة المستخدم.
الحل الأفضل في هذه الحالة هو إبلاغ المستخدم بأنه أثناء تمرير الخلاصة ، نشر شخص ما محتوى جديدًا. في تصميمنا ، يبدو وكأنه دائرة حمراء في زاوية الشاشة. من خلال النقر عليه ، يمنح المستخدم موافقته المشروطة على إعادتنا إلى أعلى الشاشة وعرض محتوى جديد.
وباتباع هذا النهج ، تجنبنا بالطبع مشكلات "التلاعب" بالمحتوى ، ولكن لا يزال يتعين حلها. أي على شاشة الدردشة ، لأنه أثناء الاتصال والتفاعل مع الشاشة ، يجب عرض محتوى جديد في أماكن مختلفة.
الفرق بين الدردشة والقوائم العادية هو أن المحتوى الجديد موجود في أسفل الشاشة. نظرًا لأن هذا "ذيل" ، يمكنك إضافة محتوى هناك دون صعوبة كبيرة. يقضي المستخدم 90 ٪ من الوقت هنا ، مما يعني أنك بحاجة إلى الحفاظ على وضع التمرير باستمرار وتحويله لأسفل عند تلقي الرسائل وإرسالها. في محادثة مباشرة ، يجب القيام بهذه الإجراءات في كثير من الأحيان.
النقطة الثانية: تحميل السجل أثناء التمرير لأعلى. فقط عند تحميل القصة ، نجد أنفسنا في موقف يكون فيه من الضروري وضع الرسائل فوق مستوى المراجعة (مما يستتبع تحيزًا) ، بحيث يكون التمرير سلسًا ومستمرًا. وكما نعلم بالفعل ، حتى لا تزعج المستخدم ، من المستحيل التحكم يدويًا في موضع التمرير.
ووجدنا حلاً: سلمناه. حل قلب الشاشة مشكلتين في وقت واحد:
- ذيل القائمة في الأعلى ، حتى نتمكن من إضافة قصة بسلاسة دون التدخل في تمرير المستخدم ؛
- توجد الرسالة الأخيرة دائمًا في أعلى القائمة ولا نحتاج إلى تمرير الشاشة قبلها.
ساعد هذا الحل أيضًا على تسريع العرض ، مما أدى إلى القضاء على العمليات غير الضرورية باستخدام التحكم في التمرير.
الحديث عن الأداء. في الإصدارات الأولى من الشاشة ، تم الكشف عن عمليات سحب ملحوظة عند تمرير الرسائل.
نظرًا لأن المحتوى الموجود في "النقود" متنوع - النصوص والملفات والصور - يجب عليك دائمًا إعادة حساب حجم الخلية وإضافة عناصر في النقود وإزالتها. لذلك ، كان من المطلوب تحسين الفقاعة. لقد فعلنا نفس الشيء كما هو الحال مع الشاشة الرئيسية ، مما يجعل العجين جزئيًا مع تحديد الموقع المطلق.عند العمل مع القوائم في iOS ، قبل رسم خلية ، تحتاج إلى معرفة ارتفاعها. لذلك ، قبل إضافة رسالة جديدة إلى القائمة ، تحتاج إلى إعداد جميع المعلومات اللازمة للعرض في دفق منفصل ، وحساب ارتفاع الخلايا ، ومعالجة بيانات المستخدم ، وفقط بعد اكتشاف كل ما هو مطلوب وتخزينه مؤقتًا ، أضف الخلية إلى القائمة.ونتيجة لذلك ، نحصل على تمرير سلس وليس دفق واجهة مستخدم زائد الحمل.لتلخيص:
- يوفر التطوير عبر الأنظمة الأساسية الوقت والمال ؛
- , , ;
- , ;
- ;
- SignalR – - ;
- ;
- , , ;
- , SignalR-, , , , .