مقدمة إلى Kotlin: برمجة Android للبشر
نشرت: 2022-03-11في عالم Android المثالي ، تكون اللغة الرئيسية لجافا حديثة وواضحة وأنيقة حقًا. يمكنك أن تكتب أقل عن طريق القيام بالمزيد ، وكلما ظهرت ميزة جديدة ، يمكن للمطورين استخدامها فقط عن طريق زيادة الإصدار في Gradle. ثم أثناء إنشاء تطبيق لطيف للغاية ، يبدو أنه قابل للاختبار بالكامل ، وقابل للتوسيع ، ويمكن صيانته. أنشطتنا ليست كبيرة ومعقدة للغاية ، يمكننا تغيير مصادر البيانات من قاعدة بيانات إلى ويب دون الكثير من الاختلافات ، وما إلى ذلك. يبدو رائعا ، أليس كذلك؟ لسوء الحظ ، عالم Android ليس هذا مثاليًا. لا تزال Google تسعى جاهدة لتحقيق الكمال ، لكننا نعلم جميعًا أن العوالم المثالية غير موجودة. وبالتالي ، علينا أن نساعد أنفسنا في تلك الرحلة الرائعة في عالم Android.
ما هي لغة Kotlin ولماذا يجب عليك استخدامها؟
إذن ، اللغة الأولى. أعتقد أن Java ليست سيد الأناقة أو الوضوح ، وهي ليست حديثة ولا معبرة (وأعتقد أنك توافق على ذلك). العيب هو أنه أسفل Android N ، ما زلنا مقيدًا بـ Java 6 (بما في ذلك بعض الأجزاء الصغيرة من Java 7). يمكن للمطورين أيضًا إرفاق RetroLambda لاستخدام تعبيرات lambda في التعليمات البرمجية الخاصة بهم ، وهو أمر مفيد جدًا أثناء استخدام RxJava. فوق Android N ، يمكننا استخدام بعض وظائف Java 8 الجديدة ، لكنها لا تزال جافا القديمة والثقيلة. كثيرًا ما أسمع مطوري Android يقولون "أتمنى أن يدعم Android لغة أفضل ، كما يفعل iOS مع Swift". وماذا لو أخبرتك أنه يمكنك استخدام لغة لطيفة جدًا وبسيطة ، مع أمان فارغ ، ولامدا ، والعديد من الميزات الجديدة الرائعة؟ مرحبًا بك في Kotlin.
ما هي لغة Kotlin؟
Kotlin هي لغة جديدة (يشار إليها أحيانًا باسم Swift لنظام Android) ، تم تطويرها بواسطة فريق JetBrains ، وهي الآن في إصدار 1.0.2. ما يجعله مفيدًا في تطوير Android هو أنه يجمع إلى JVM bytecode ، ويمكن أيضًا تجميعه إلى JavaScript. إنه متوافق تمامًا مع Java ، ويمكن تحويل كود Kotlin ببساطة إلى كود Java والعكس صحيح (هناك مكون إضافي من JetBrains). هذا يعني أن Kotlin يمكنها استخدام أي إطار عمل أو مكتبة وما إلى ذلك مكتوبة بلغة Java. على Android ، يتكامل بواسطة Gradle. إذا كان لديك تطبيق Android حالي وتريد تنفيذ ميزة جديدة في Kotlin دون إعادة كتابة التطبيق بالكامل ، فما عليك سوى البدء في الكتابة في Kotlin ، وسيعمل.
ولكن ما هي "الميزات الجديدة الرائعة"؟ اسمحوا لي أن أسرد القليل:
معلمات الوظيفة الاختيارية والمسماة
fun createDate(day: Int, month: Int, year: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) { print("TEST", "$day-$month-$year $hour:$minute:$second") }
يمكننا استدعاء طريقة createDate بطرق مختلفة
createDate(1,2,2016) prints: '1-2-2016 0:0:0' createDate(1,2,2016, 12) prints: '1-2-2016 12:0:0' createDate(1,2,2016, minute = 30) prints: '1-2-2016 0:30:0'
سلامة لاغية
إذا كان من الممكن أن يكون المتغير فارغًا ، فلن يتم ترجمة الكود إلا إذا أجبرناهم على فعل ذلك. سيكون للشفرة التالية خطأ - قد يكون nullableVar فارغًا:
var nullableVar: String? = “”; nullableVar.length;
للترجمة ، علينا التحقق مما إذا لم يكن فارغًا:
if(nullableVar){ nullableVar.length }
أو أقصر:
nullableVar?.length
بهذه الطريقة ، إذا كانت قيمة nullableVar خالية ، فلن يحدث شيء. بخلاف ذلك ، يمكننا وضع علامة على المتغير على أنه غير قابل للإلغاء ، بدون علامة استفهام بعد النوع:
var nonNullableVar: String = “”; nonNullableVar.length;
يتم تجميع هذا الرمز ، وإذا أردنا تعيين قيمة خالية إلى nonNullableVar ، فسيظهر المترجم خطأ.
هناك أيضًا عامل Elvis مفيد جدًا:
var stringLength = nullableVar?.length ?: 0
بعد ذلك ، عندما تكون قيمة nullableVar خالية (لذا فإن قيمة nullableVar؟.
المتغيرات المتغيرة والثابتة
في المثال أعلاه ، أستخدم var عند تعريف متغير. هذا قابل للتغيير ، يمكننا إعادة تعيينه وقتما نريد. إذا أردنا أن يكون هذا المتغير غير قابل للتغيير (يجب علينا في كثير من الحالات) ، فإننا نستخدم val (كقيمة وليس متغير):
val immutable: Int = 1
بعد ذلك ، لن يسمح لنا المترجم بإعادة التخصيص إلى ثابت.
لامداس
نعلم جميعًا ما هي لامدا ، لذا سأوضح هنا كيف يمكننا استخدامها في Kotlin:
button.setOnClickListener({ view -> Log.d("Kotlin","Click")})
أو إذا كانت الوظيفة هي الوسيطة الوحيدة أو الأخيرة:
button.setOnClickListener { Log.d("Kotlin","Click")}
ملحقات
الإضافات هي ميزة لغوية مفيدة للغاية ، وبفضلها يمكننا "توسيع" الفصول الحالية ، حتى عندما تكون نهائية أو لا يمكننا الوصول إلى كود المصدر الخاص بها.
على سبيل المثال ، للحصول على قيمة سلسلة من تحرير النص ، بدلاً من الكتابة في كل مرة ، يمكننا كتابة الدالة editText.text.toString ():
fun EditText.textValue(): String{ return text.toString() }
أو أقصر:
fun EditText.textValue() = text.toString()
والآن ، مع كل مثيل من EditText:
editText.textValue()
أو يمكننا إضافة خاصية إرجاع نفس الشيء:
var EditText.textValue: String get() = text.toString() set(v) {setText(v)}
زيادة الحمولة على المشغل
أحيانًا يكون مفيدًا إذا أردنا إضافة كائنات أو ضربها أو مقارنتها. يسمح Kotlin بالتحميل الزائد للعوامل الثنائية (زائد ، ناقص ، plusAssign ، نطاق ، إلخ) ، مشغلات المصفوفات (get ، set ، get range ، set range) ، والعمليات المتساوية والأحادية (+ a ، -a ، إلخ.)
فئة البيانات
كم عدد سطور التعليمات البرمجية التي تحتاجها لتنفيذ فئة مستخدم في Java بثلاث خصائص: copy ، و equals ، و hashCode ، و toString؟ تحتاج في Kaotlin إلى سطر واحد فقط:
data class User(val name: String, val surname: String, val age: Int)
توفر فئة البيانات هذه أساليب equals () و hashCode () و copy () ، وكذلك toString () ، والتي تطبع User كـ:
User(name=John, surname=Doe, age=23)
توفر فئات البيانات أيضًا بعض الوظائف والخصائص المفيدة الأخرى ، والتي يمكنك رؤيتها في وثائق Kotlin.
ملحقات أنكو
أنت تستخدم ملحقات Butterknife أو Android ، أليس كذلك؟ ماذا لو لم تكن بحاجة حتى إلى استخدام هذه المكتبة ، وبعد التصريح عن طرق العرض في XML ، استخدمها فقط من الكود بواسطة معرّفها (كما هو الحال مع XAML في C #):
<Button android: android:layout_width="match_parent" android:layout_height="wrap_content" />
loginBtn.setOnClickListener{}
يحتوي Kotlin على امتدادات Anko مفيدة جدًا ، وبهذا لا تحتاج إلى إخبار نشاطك بما هو loginBtn ، فهو يعرف ذلك فقط عن طريق "استيراد" xml:
import kotlinx.android.synthetic.main.activity_main.*
هناك العديد من الأشياء المفيدة الأخرى في Anko ، بما في ذلك بدء الأنشطة وعرض الخبز المحمص وما إلى ذلك. ليس هذا هو الهدف الرئيسي لـ Anko - فهو مصمم لإنشاء تخطيطات بسهولة من التعليمات البرمجية. لذلك ، إذا كنت بحاجة إلى إنشاء تخطيط برمجيًا ، فهذه هي أفضل طريقة.
هذه ليست سوى نظرة قصيرة على Kotlin. أوصي بقراءة مدونة أنطونيو ليفا وكتابه - Kotlin for Android Developers ، وبالطبع موقع Kotlin الرسمي.
ما هو MVP ولماذا؟
لغة جميلة وقوية وواضحة لا تكفي. من السهل جدًا كتابة تطبيقات فوضوية بكل لغة بدون بنية جيدة. غالبًا ما يعطي مطورو Android (معظمهم ممن بدأوا العمل ، ولكن أيضًا المطورين الأكثر تقدمًا) مسؤولية النشاط عن كل شيء من حولهم. يقوم النشاط (أو جزء ، أو طريقة عرض أخرى) بتنزيل البيانات ، وإرسالها للحفظ ، وتقديمها ، والاستجابة لتفاعلات المستخدم ، وتحرير البيانات ، وإدارة جميع طرق العرض الفرعية. . . وغالبًا أكثر من ذلك بكثير. إنه كثير جدًا بالنسبة لمثل هذه الكائنات غير المستقرة مثل الأنشطة أو الأجزاء (يكفي تدوير الشاشة والنشاط يقول "وداعًا ...").
فكرة جيدة للغاية هي عزل المسؤوليات عن وجهات النظر وجعلها غبية قدر الإمكان. يجب أن تكون طرق العرض (الأنشطة أو الأجزاء أو العروض المخصصة أو أي بيانات تعرض على الشاشة) مسؤولة فقط عن إدارة طرق العرض الفرعية الخاصة بهم. يجب أن يكون لدى المشاهدات مقدمو عروض يتواصلون مع النموذج ويخبرونهم بما يجب عليهم فعله. هذا ، باختصار ، هو نمط عرض النموذج (Model-View-Presenter) (بالنسبة لي ، يجب تسميته Model-Presenter-View لإظهار الاتصالات بين الطبقات).
"مرحبًا ، أعرف شيئًا من هذا القبيل ، ويسمى MVC!" - ألا تعتقد؟ لا ، MVP ليس هو نفسه MVC. في نمط MVC ، يمكن أن يتواصل العرض مع النموذج. أثناء استخدام MVP ، لا تسمح بأي اتصال بين هاتين الطبقتين - الطريقة الوحيدة التي يمكن لـ View من خلالها التواصل مع النموذج هي من خلال Presenter. يمكن أن يكون الشيء الوحيد الذي يعرفه "العرض" عن النموذج هو بنية البيانات. تعرف طريقة العرض كيفية ، على سبيل المثال ، عرض المستخدم ، ولكنها لا تعرف متى. إليك مثال بسيط:
تعرف طريقة العرض "أنا نشاط ، لدي نصان تحرير وزر واحد. عندما ينقر شخص ما على الزر ، يجب أن أخبره لمقدمي ، وأقوم بتمرير قيم EditTexts له. وهذا كل شيء ، يمكنني النوم حتى النقرة التالية أو يخبرني مقدم العرض ماذا أفعل ".
يعرف المقدم أن هناك طريقة عرض في مكان ما ، وهو يعرف العمليات التي يمكن أن يؤديها هذا العرض. وهو يعلم أيضًا أنه عندما يتلقى سلسلتين ، يجب عليه إنشاء مستخدم من هاتين السلسلتين وإرسال البيانات إلى نموذج لحفظها ، وإذا نجح الحفظ ، أخبر العرض "إظهار معلومات النجاح".
يعرف النموذج فقط أين توجد البيانات ، وأين يجب حفظها ، وما العمليات التي يجب إجراؤها على البيانات.
من السهل اختبار التطبيقات المكتوبة بلغة MVP وصيانتها وإعادة استخدامها. يجب أن لا يعرف مقدم العرض الصافي شيئًا عن نظام Android الأساسي. يجب أن تكون فئة Java نقية (أو Kotlin ، في حالتنا). بفضل هذا يمكننا إعادة استخدام مقدمنا في مشاريع أخرى. يمكننا أيضًا كتابة اختبارات الوحدة بسهولة ، واختبار النموذج والعرض والمقدم بشكل منفصل.
القليل من الاستطراد: يجب أن يكون MVP جزءًا من الهندسة المعمارية النظيفة الخاصة بالعم بوب لجعل التطبيقات أكثر مرونة ومصممة بشكل جيد. سأحاول الكتابة عن ذلك في المرة القادمة.
عينة التطبيق مع MVP و Kotlin
هذه نظرية كافية ، دعنا نرى بعض التعليمات البرمجية! حسنًا ، دعنا نحاول إنشاء تطبيق بسيط. الهدف الرئيسي لهذا التطبيق هو إنشاء مستخدم. ستحتوي الشاشة الأولى على نصي تحرير (الاسم واللقب) وزر واحد (حفظ). بعد إدخال الاسم واللقب والنقر فوق "حفظ" ، يجب أن يظهر التطبيق "تم حفظ المستخدم" والانتقال إلى الشاشة التالية ، حيث يتم عرض الاسم واللقب المحفوظين. عندما يكون الاسم أو اللقب فارغًا ، يجب ألا يحفظ التطبيق المستخدم ويظهر خطأ يشير إلى الخطأ.
أول شيء بعد إنشاء مشروع Android Studio هو تكوين Kotlin. يجب عليك تثبيت البرنامج المساعد Kotlin ، وبعد إعادة التشغيل ، في Tools> Kotlin يمكنك النقر فوق "Configure Kotlin in Project". سيضيف IDE تبعيات Kotlin إلى Gradle. إذا كان لديك أي رمز موجود ، فيمكنك تحويله بسهولة إلى Kotlin عن طريق (Ctrl + Shift + Alt + K أو Code> تحويل ملف Java إلى Kotlin). إذا كان هناك خطأ ما ولم يتم ترجمة المشروع ، أو لم يتمكن Gradle من رؤية Kotlin ، فيمكنك التحقق من رمز التطبيق المتاح على GitHub.

الآن بعد أن أصبح لدينا مشروع ، فلنبدأ بإنشاء عرضنا الأول - CreateUserView. يجب أن يحتوي هذا العرض على الوظائف المذكورة سابقًا ، حتى نتمكن من كتابة واجهة لذلك:
interface CreateUserView : View { fun showEmptyNameError() /* show error when name is empty */ fun showEmptySurnameError() /* show error when surname is empty */ fun showUserSaved() /* show user saved info */ fun showUserDetails(user: User) /* show user details */ }
كما ترى ، فإن Kotlin تشبه Java في إعلان الوظائف. كل هذه وظائف لا تُرجع شيئًا ، والأخيرة لها معلمة واحدة. هذا هو الاختلاف ، نوع المعلمة يأتي بعد الاسم. واجهة العرض ليست من Android - إنها واجهتنا البسيطة الفارغة:
interface View
يجب أن تحتوي واجهة مقدم العرض الأساسي على خاصية نوع العرض ، وعلى الأقل على الطريقة (onDestroy على سبيل المثال) ، حيث سيتم تعيين هذه الخاصية على قيمة خالية:
interface Presenter<T : View> { var view: T? fun onDestroy(){ view = null } }
هنا يمكنك رؤية ميزة أخرى في Kotlin - يمكنك إعلان الخصائص في الواجهات ، وكذلك تنفيذ الطرق هناك.
يحتاج برنامج CreateUserView الخاص بنا إلى التواصل مع CreateUserPresenter. الوظيفة الإضافية الوحيدة التي يحتاجها هذا المقدم هي saveUser مع وسيطتين سلسلتين:
interface CreateUserPresenter<T : View>: Presenter<T> { fun saveUser(name: String, surname: String) }
نحتاج أيضًا إلى تعريف النموذج - لقد تم ذكر فئة البيانات السابقة:
data class User(val name: String, val surname: String)
بعد التصريح عن جميع الواجهات ، يمكننا البدء في تنفيذ.
سيتم تنفيذ CreateUserPresenter في CreateUserPresenterImpl:
class CreateUserPresenterImpl(override var view: CreateUserView?): CreateUserPresenter<CreateUserView> { override fun saveUser(name: String, surname: String) { } }
السطر الأول مع تعريف الصنف:
CreateUserPresenterImpl(override var view: CreateUserView?)
هو مُنشئ ، نستخدمه لتعيين خاصية العرض ، المحددة في الواجهة.
يحتاج MainActivity ، وهو تطبيق CreateUserView الخاص بنا ، إلى مرجع إلى CreateUserPresenter:
class MainActivity : AppCompatActivity(), CreateUserView { private val presenter: CreateUserPresenter<CreateUserView> by lazy { CreateUserPresenterImpl(this) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) saveUserBtn.setOnClickListener{ presenter.saveUser(userName.textValue(), userSurname.textValue()) /*use of textValue() extension, mentioned earlier */ } } override fun showEmptyNameError() { userName.error = getString(R.string.name_empty_error) /* it's equal to userName.setError() - Kotlin allows us to use property */ } override fun showEmptySurnameError() { userSurname.error = getString(R.string.surname_empty_error) } override fun showUserSaved() { toast(R.string.user_saved) /* anko extension - equal to Toast.makeText(this, R.string.user_saved, Toast.LENGTH_LONG) */ } override fun showUserDetails(user: User) { } override fun onDestroy() { presenter.onDestroy() } }
في بداية الفصل ، حددنا مقدمنا:
private val presenter: CreateUserPresenter<CreateUserView> by lazy { CreateUserPresenterImpl(this) }
يتم تعريفه على أنه غير قابل للتغيير (val) ، ويتم إنشاؤه بواسطة المندوب الكسول ، والذي سيتم تعيينه في المرة الأولى المطلوبة. علاوة على ذلك ، نحن على يقين من أنه لن يكون فارغًا (لا توجد علامة استفهام بعد التعريف).
عندما ينقر المستخدم فوق الزر "حفظ" ، يرسل "عرض" معلومات إلى مقدم العرض بقيم EditTexts. عندما يحدث ذلك ، يجب حفظ المستخدم ، لذلك يتعين علينا تنفيذ طريقة saveUser في Presenter (وبعض وظائف النموذج):
override fun saveUser(name: String, surname: String) { val user = User(name, surname) when(UserValidator.validateUser(user)){ UserError.EMPTY_NAME -> view?.showEmptyNameError() UserError.EMPTY_SURNAME -> view?.showEmptySurnameError() UserError.NO_ERROR -> { UserStore.saveUser(user) view?.showUserSaved() view?.showUserDetails(user) } } }
عندما يتم إنشاء مستخدم ، يتم إرساله إلى UserValidator للتحقق من الصلاحية. بعد ذلك ، وفقًا لنتيجة التحقق ، يتم استدعاء الطريقة المناسبة. بناء when () {} هو نفسه switch / case في Java. لكنها أكثر قوة - تسمح Kotlin باستخدام ليس فقط التعداد أو int في "الحالة" ، ولكن أيضًا النطاقات أو السلاسل أو أنواع الكائنات. يجب أن تحتوي على جميع الاحتمالات أو أن يكون لها تعبير آخر. هنا ، يغطي جميع قيم UserError.
باستخدام view؟ .showEmptyNameError () (مع علامة استفهام بعد المشاهدة) ، نحن محميون من NullPointer. يمكن إبطال العرض في طريقة onDestroy ، ومع هذا البناء ، لن يحدث شيء.
عندما لا يحتوي نموذج المستخدم على أخطاء ، فإنه يخبر UserStore بحفظه ، ثم يوجه View لإظهار النجاح وإظهار التفاصيل.
كما ذكرنا سابقًا ، يتعين علينا تنفيذ بعض الأشياء النموذجية:
enum class UserError { EMPTY_NAME, EMPTY_SURNAME, NO_ERROR } object UserStore { fun saveUser(user: User){ //Save user somewhere: Database, SharedPreferences, send to web... } } object UserValidator { fun validateUser(user: User): UserError { with(user){ if(name.isNullOrEmpty()) return UserError.EMPTY_NAME if(surname.isNullOrEmpty()) return UserError.EMPTY_SURNAME } return UserError.NO_ERROR } }
الشيء الأكثر إثارة للاهتمام هنا هو UserValidator. باستخدام كلمة الكائن ، يمكننا إنشاء فئة فردية ، دون قلق بشأن الخيوط والمُنشئين الخاصين وما إلى ذلك.
الشيء التالي - في طريقة validateUser (مستخدم) ، يوجد تعبير (مستخدم) {}. يتم تنفيذ الكود داخل هذه الكتلة في سياق الكائن ، ويتم تمريره بالاسم واللقب من خصائص المستخدم.
هناك أيضا شيء صغير آخر. كل الكود أعلاه ، من التعداد إلى UserValidator ، يتم وضع التعريف في ملف واحد (تعريف فئة المستخدم موجود هنا أيضًا). لا يجبرك Kotlin على جعل كل فئة عامة في ملف واحد (أو فئة الاسم تمامًا مثل الملف). وبالتالي ، إذا كان لديك بعض الأجزاء القصيرة من التعليمات البرمجية ذات الصلة (فئات البيانات ، الامتدادات ، الوظائف ، الثوابت - لا تتطلب Kotlin فئة للوظيفة أو الثابت) ، يمكنك وضعها في ملف واحد بدلاً من الانتشار عبر جميع الملفات في المشروع.
عندما يتم حفظ مستخدم ، يجب أن يعرض تطبيقنا ذلك. نحتاج إلى عرض آخر - يمكن أن يكون أي عرض Android أو عرض مخصص أو جزء أو نشاط. اخترت النشاط.
لذلك ، دعونا نحدد واجهة UserDetailsView. يمكن أن تظهر للمستخدم ، ولكن يجب أن تظهر أيضًا خطأ عندما لا يكون المستخدم موجودًا:
interface UserDetailsView { fun showUserDetails(user: User) fun showNoUserError() }
بعد ذلك ، UserDetailsPresenter. يجب أن يكون لها خاصية مستخدم:
interface UserDetailsPresenter<T: View>: Presenter<T> { var user: User? }
سيتم تنفيذ هذه الواجهة في UserDetailsPresenterImpl. يجب أن تتجاوز خاصية المستخدم. في كل مرة يتم فيها تعيين هذه الخاصية ، يجب تحديث المستخدم في العرض. يمكننا استخدام محدد الخاصية لهذا:
class UserDetailsPresenterImpl(override var view: UserDetailsView?): UserDetailsPresenter<UserDetailsView> { override var user: User? = null set(value) { field = value if(field != null){ view?.showUserDetails(field!!) } else { view?.showNoUserError() } } }
تطبيق UserDetailsView ، UserDetailsActivity ، بسيط للغاية. تمامًا كما كان من قبل ، لدينا كائن مقدم تم إنشاؤه بواسطة التحميل البطيء. يجب أن يتم تمرير المستخدم المراد عرضه عبر النية. توجد مشكلة صغيرة مع هذا في الوقت الحالي ، وسنحلها بعد قليل. عندما يكون لدينا مستخدم من النية ، يحتاج View إلى تعيينه لمقدم العرض. بعد ذلك ، سيتم تحديث المستخدم على الشاشة ، أو إذا كان فارغًا ، فسيظهر الخطأ (وسينتهي النشاط - لكن المقدم لا يعرف ذلك):
class UserDetailsActivity: AppCompatActivity(), UserDetailsView { private val presenter: UserDetailsPresenter<UserDetailsView> by lazy { UserDetailsPresenterImpl(this) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_user_details) val user = intent.getParcelableExtra<User>(USER_KEY) presenter.user = user } override fun showUserDetails(user: User) { userFullName.text = "${user.name} ${user.surname}" } override fun showNoUserError() { toast(R.string.no_user_error) finish() } override fun onDestroy() { presenter.onDestroy() } }
يتطلب تمرير الكائنات عبر النوايا أن يقوم هذا الكائن بتنفيذ واجهة Parcelable. هذا عمل "قذر" للغاية. أنا شخصياً أكره القيام بذلك بسبب كل من CREATORs وخصائصه وحفظه واستعادته وما إلى ذلك. لحسن الحظ ، هناك مكون إضافي مناسب ، Parcelable لـ Kotlin. بعد تثبيته ، يمكننا إنشاء Parcelable بنقرة واحدة فقط.
آخر ما يجب فعله هو تنفيذ showUserDetails (المستخدم: المستخدم) في MainActivity الخاص بنا:
override fun showUserDetails(user: User) { startActivity<UserDetailsActivity>(USER_KEY to user) /* anko extension - starts UserDetailsActivity and pass user as USER_KEY in intent */ }
و هذا كل شيء.
لدينا تطبيق بسيط يحفظ المستخدم (في الواقع ، لا يتم حفظه ، ولكن يمكننا إضافة هذه الوظيفة دون لمس مقدم العرض أو العرض) وتقديمه على الشاشة. في المستقبل ، إذا أردنا تغيير طريقة عرض المستخدم على الشاشة ، مثل من نشاطين إلى جزأين في نشاط واحد ، أو عرضين مخصصين ، فستكون التغييرات في فئات العرض فقط. بالطبع ، إذا لم نغير الوظيفة أو هيكل النموذج. المقدم ، الذي لا يعرف بالضبط ما هو العرض ، لن يحتاج إلى أي تغييرات.
ماذا بعد؟
في تطبيقنا ، يتم إنشاء Presenter في كل مرة يتم فيها إنشاء نشاط. هذا النهج ، أو عكسه ، إذا كان يجب على مقدم العرض الاستمرار عبر حالات النشاط ، هو موضوع نقاش كبير عبر الإنترنت. بالنسبة لي ، يعتمد الأمر على التطبيق واحتياجاته والمطور. أحيانًا يكون من الأفضل تدمير مقدم العرض ، وأحيانًا لا. إذا قررت الاستمرار في استخدام أحد الأساليب ، فإن أسلوبًا مثيرًا للاهتمام هو استخدام LoaderManager من أجل ذلك.
كما ذكرنا من قبل ، يجب أن يكون MVP جزءًا من هندسة العم بوب النظيفة. علاوة على ذلك ، يجب على المطورين الجيدين استخدام Dagger لحقن تبعيات مقدمي العروض في الأنشطة. كما أنه يساعد في الحفاظ على الشفرة واختبارها وإعادة استخدامها في المستقبل. حاليًا ، يعمل Kotlin جيدًا مع Dagger (لم يكن الأمر سهلاً قبل الإصدار الرسمي) ، وكذلك مع مكتبات Android الأخرى المفيدة.
يتم إحتوائه
بالنسبة لي ، فإن لغة Kotlin هي لغة رائعة. إنه حديث وواضح ومعبر بينما لا يزال يتم تطويره بواسطة أشخاص عظماء. ويمكننا استخدام أي إصدار جديد على أي جهاز وإصدار يعمل بنظام Android. كل ما يجعلني غاضبًا من Java ، يتحسن Kotlin.
بالطبع ، كما قلت لا شيء مثالي. لدى Kotlin أيضًا بعض العيوب. أحدث إصدارات البرنامج المساعد gradle (بشكل رئيسي من ألفا أو بيتا) لا تعمل بشكل جيد مع هذه اللغة. يشتكي العديد من الأشخاص من أن وقت الإنشاء أطول قليلاً من وقت Java الخالص ، وأن التطبيقات تحتوي على بعض وحدات MB الإضافية. لكن Android Studio و Gradle لا يزالان يتحسنان ، وتحتوي الهواتف على مساحة أكبر للتطبيقات. لهذا السبب أعتقد أن Kotlin يمكن أن تكون لغة لطيفة جدًا لكل مطور Android. فقط جربها وشاركها في قسم التعليقات أدناه ما تعتقده.
كود المصدر لعينة التطبيق متاح على Github: github.com/tomaszczura/AndroidMVPKotlin