استخدام أنماط لغة Kotlin الشائعة مع Android

يركّز هذا الموضوع على بعض الجوانب الأكثر فائدة في لغة Kotlin. عند التطوير لأجهزة Android.

التعامل مع الأجزاء

تستخدم الأقسام التالية أمثلة باللغة Fragment لتسليط الضوء على بعض من أمثلة أفضل الميزات.

الاكتساب

يمكنك الإعلان عن فئة في لغة Kotlin باستخدام الكلمة الرئيسية class. في ما يلي على سبيل المثال، LoginFragment هي فئة فرعية من Fragment. يمكنك الإشارة إلى التوريث باستخدام عامل التشغيل : بين الفئة الفرعية والفئة الرئيسية التابعة لها:

class LoginFragment : Fragment()

في بيان الفئة هذا، تكون الدالة LoginFragment مسؤولة عن استدعاء الدالة دالة إنشاء لفئتها الفائقة، Fragment.

ضمن LoginFragment، يمكنك إلغاء عدد من معاودة الاتصال بمراحل النشاط إلى الاستجابة لتغييرات الحالة في "Fragment" لإلغاء دالة، استخدم دالة الاستدعاء كلمة رئيسية واحدة (override)، كما هو موضّح في المثال التالي:

override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    return inflater.inflate(R.layout.login_fragment, container, false)
}

للإشارة إلى دالة في الفئة الرئيسية، استخدِم الكلمة الرئيسية super على النحو الموضّح في المثال التالي:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
}

القدرة على الإلغاء والإعداد

في الأمثلة السابقة، كان لبعض المعلمات في الطرق التي تم إلغاؤها الأنواع اللاحقة بعلامة استفهام ?. يشير هذا إلى أن الوسيطات يمكن أن تكون قيمة تمرير هذه المعلمات فارغة. تأكد من التعامل مع القيم الفارغة بطريقة آمنة.

في Kotlin، يجب تهيئة خصائص الكائن عند تعريف الكائن. وهذا يعني أنه عند الحصول على مثيل لفئةٍ ما، يمكنك على الفور يشير إلى أي من خصائصه التي يمكن الوصول إليها. الكائنات View في Fragment، ومع ذلك، لن تكون جاهزة لتكبير حجمها حتى يتم الاتصال بـ Fragment#onCreateView، لذلك ستحتاج إلى طريقة لتأجيل إعداد السمة View.

تتيح لك lateinit تأجيل إعداد الموقع. عند استخدام lateinit، عليك إعداد موقعك في أقرب وقت ممكن.

يوضّح المثال التالي استخدام lateinit لتعيين عناصر View في onViewCreated:

class LoginFragment : Fragment() {

    private lateinit var usernameEditText: EditText
    private lateinit var passwordEditText: EditText
    private lateinit var loginButton: Button
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        usernameEditText = view.findViewById(R.id.username_edit_text)
        passwordEditText = view.findViewById(R.id.password_edit_text)
        loginButton = view.findViewById(R.id.login_button)
        statusTextView = view.findViewById(R.id.status_text_view)
    }

    ...
}

إحالة ناجحة إلى SAM

يمكنك الاستماع إلى أحداث النقر في Android من خلال تنفيذ واجهة OnClickListener. يحتوي Button عنصرًا على setOnClickListener(). التي تتطلب تنفيذ OnClickListener.

تتضمن الدالة OnClickListener طريقة تجريدية واحدة، وهي onClick()، والتي يجب عليك وتنفيذها. لأنّ السمة setOnClickListener() تتخذ دائمًا OnClickListener كـ وسيطة، ولأن OnClickListener تحتوي دائمًا على نفس ملخص الرسم البياني يمكن تمثيل هذا التنفيذ باستخدام دالة مجهولة في لغة Kotlin. تُعرف هذه العملية باسم تحويل الطريقة التجريدية الواحدة، أو الإحالة الناجحة إلى SAM

تحويل SAM يمكن أن يجعل الرمز الخاص بك أكثر وضوحًا. المثال التالي كيفية استخدام الإحالات الناجحة من خلال استراتيجية التسويق (SAM) لتنفيذ OnClickListener Button:

loginButton.setOnClickListener {
    val authSuccessful: Boolean = viewModel.authenticate(
            usernameEditText.text.toString(),
            passwordEditText.text.toString()
    )
    if (authSuccessful) {
        // Navigate to next screen
    } else {
        statusTextView.text = requireContext().getString(R.string.auth_failed)
    }
}

يتم تمرير الرمز داخل الدالة المجهولة إلى setOnClickListener() ويتم تنفيذه عندما ينقر المستخدم على loginButton.

الكائنات المصاحبة

العناصر المصاحبة توفير آلية لتحديد المتغيرات أو الدوال المرتبطة من الناحية النظرية إلى نوع ولكنها غير مرتبطة بكائن معين. مرافق من هذه العناصر استخدام الكلمة الرئيسية static في Java للمتغيّرات والطرق.

في المثال التالي، تكون TAG قيمة ثابتة String. لا تحتاج إلى عنوان URL فريد مثيل String لكل مثيل LoginFragment، لذلك يجب عليك تعريفها في كائن مصاحب:

class LoginFragment : Fragment() {

    ...

    companion object {
        private const val TAG = "LoginFragment"
    }
}

يمكنك تحديد TAG في المستوى الأعلى من الملف، ولكن ملف قد يحتوي أيضًا على عدد كبير من المتغيرات والدوال والفئات والتي تم تحديدها أيضًا على المستوى الأعلى. العناصر المصاحبة للمساعدة في الربط والمتغيرات والدوال وتعريف الفئة دون الإشارة إلى أي مثيل معين من تلك الفئة.

تفويض المواقع

عند إعداد الخصائص، يمكنك تكرار بعض أساليب Android الأكثر شيوعًا مثل الوصول إلى ViewModel داخل Fragment. لتجنُّب المبالغة يمكنك استخدام بنية تفويض الموقع في Kotlin.

private val viewModel: LoginViewModel by viewModels()

يوفِّر تفويض المواقع طريقة تنفيذ شائعة يمكنك إعادة استخدامها. في جميع أنحاء التطبيق. توفّر لك منصة Android KTX بعض تفويضات العقارات. على سبيل المثال، يسترد viewModels قيمة ViewModel التي تم تحديد نطاقها على Fragment الحالي.

يستخدم تفويض المواقع الانعكاس، مما يضيف بعض النفقات العامة للأداء. المقايضة هي بناء جملة موجز يوفر وقت التطوير.

القابلية للإلغاء

توفر لغة Kotlin قواعد صارمة حول قابلية القيم الفارغة للحفاظ على أمان النوع طوال تطبيقك. في Kotlin، لا يمكن أن تحتوي مراجع الكائنات على قيم فارغة عن طريق الافتراضي. لتعيين قيمة فارغة لمتغير، يجب الإعلان عن القيمة nullable نوع المتغير من خلال إضافة ? إلى نهاية نوع القاعدة.

التعبير التالي غير قانوني في لغة Kotlin، على سبيل المثال. name من النوع String وغير قابلة للإلغاء:

val name: String = null

للسماح بقيمة خالية، يجب استخدام نوع String قابل للقيم String?، باعتباره كما هو موضح في المثال التالي:

val name: String? = null

إمكانية التشغيل التفاعلي

إن قواعد Kotlin الصارمة تجعل التعليمة البرمجية أكثر أمانًا وإيجازًا. تقل هذه القواعد احتمالات وجود NullPointerException يمكن أن يؤدي إلى والأعطال. علاوة على ذلك، فإنها تقلل عدد الشيكات الفارغة التي تحتاج إلى إجرائها في الرمز.

في كثير من الأحيان، يجب أيضًا استدعاء رمز غير لغة البرمجة Kotlin عند كتابة تطبيق Android، كما تتم كتابة معظم واجهات برمجة تطبيقات Android بلغة برمجة Java.

وتُعد إمكانية القيم من المجالات الرئيسية التي تختلف فيها لغة Java وKotlin في السلوك. لغة Java أقل صارمة بشأن بناء جملة القيم الفارغة.

على سبيل المثال، تتضمّن الفئة Account بعض السمات، من بينها السمة String. يسمى name. لا تمتلك Java قواعد Kotlin حول قابلية القيم الفارغة، بدلاً من ذلك، نعتمد على التعليقات التوضيحية الاختيارية المتعلقة بقابلية القيم الفارغة ما إذا كان بإمكانك تعيين قيمة فارغة.

نظرًا لأن إطار عمل Android مكتوب بشكل أساسي بلغة Java، قد تواجه هذا السيناريو عند طلب الدخول إلى واجهات برمجة التطبيقات بدون التعليقات التوضيحية التي تؤدي إلى قبول القيم الفارغة.

أنواع المنصات

إذا كنت تستخدم لغة Kotlin للإشارة إلى عضو name بدون تعليقات توضيحية تم تحديده في فئة Account في Java، لا يعرف برنامج التحويل البرمجي ما إذا كانت String مرتبطة String أو String? في Kotlin. يتم تمثيل هذا الغموض من خلال نوع المنصة، String!.

ليس لـ String! أي معنى خاص في برنامج التجميع بلغة Kotlin. يمكن أن يمثّل String! إما String أو String?، ويتيح لك برنامج التحويل البرمجي تعيين قيمة أيًا من النوعين. تجدر الإشارة إلى أنّك تخاطر برمي NullPointerException إذا تمثيل النوع على أنّه String وتعيين قيمة فارغة.

لمعالجة هذه المشكلة، يجب استخدام التعليقات التوضيحية التي تؤدي إلى قبول القيم الفارغة عندما تكتب التعليمات البرمجية في Java. تساعد هذه التعليقات التوضيحية مطوّري كل من Java وKotlin.

على سبيل المثال، إليك الفئة Account كما هو محدّد في Java:

public class Account implements Parcelable {
    public final String name;
    public final String type;
    private final @Nullable String accessId;

    ...
}

تمت إضافة تعليقات توضيحية إلى أحد متغيرات العضو، accessId، باستخدام @Nullable، مما يشير إلى أنه يمكن أن يحتوي على قيمة فارغة. سيتعامل Kotlin بعد ذلك مع accessId. بصفتك String?.

للإشارة إلى أنّه لا يمكن أن يكون أحد المتغيّرات فارغًا مطلقًا، استخدِم التعليق التوضيحي @NonNull:

public class Account implements Parcelable {
    public final @NonNull String name;
    ...
}

في هذا السيناريو، تُعتبر السمة name String غير قابلة للقيم الفارغة في لغة Kotlin.

تم تضمين التعليقات التوضيحية للقيم الفارغة في جميع واجهات برمجة تطبيقات Android الجديدة والعديد من واجهات برمجة تطبيقات Android. أضافت العديد من مكتبات Java تعليقات توضيحية ل قبول القيم الفارغة تدعم مطوري لغتَي Kotlin وJava.

التعامل مع إمكانية قبول القيم الفارغة

إذا لم تكن متأكدًا من نوع جافا، فيجب أن تعتبره قابلاً للقيم الفارغة. على سبيل المثال، لا تتم إضافة تعليقات توضيحية للعضو name في الفئة Account، وبالتالي يمكنك من المفترض أن تكون قيمة String قابلة للقيم الفارغة.

إذا كنت تريد قطع السمة name بحيث لا تشتمل قيمتها على بادئة أو مسافة بيضاء لاحقة، يمكنك استخدام دالة trim في Kotlin. يمكنك قطع String? بعدة طرق مختلفة تتمثل إحدى هذه الطرق في استخدام القيمة not-null كما هو موضَّح في المثال التالي: !!

val account = Account("name", "type")
val accountName = account.name!!.trim()

يتعامل عامل التشغيل !! مع كل شيء في جانبه الأيسر على أنه غير فارغ، ولذلك في في هذه الحالة، فأنت تتعامل مع name على أنها String غير فارغة. إذا كانت نتيجة التعبير على يساره فارغ، ثم يعرض تطبيقك NullPointerException. عامل التشغيل هذا سريع وسهل، ولكن يجب استخدامه باعتدال، لأنه يمكن أعِد إدخال مثيلات NullPointerException في الرمز الخاص بك.

هناك خيار أكثر أمانًا هو استخدام عامل تشغيل المكالمة الآمن، ?.، كما هو موضّح في القسم المثال التالي:

val account = Account("name", "type")
val accountName = account.name?.trim()

باستخدام عامل تشغيل الاتصال الآمن، إذا لم يكن name قيمة خالية، تكون نتيجة name?.trim() هي قيمة اسم بدون مسافة بيضاء بادئة أو لاحقة. في حال حذف name قيمة خالية، فإن نتيجة name?.trim() هي null. هذا يعني أنّ لا يمكن لتطبيقك إطلاق علامة NullPointerException عند تنفيذ هذه العبارة.

على الرغم من أنّ عامل تشغيل المكالمات الآمنة ينقذك من NullPointerException محتمَل، فإنها تمرر قيمة فارغة إلى العبارة التالية. يمكنك بدلاً من ذلك التعامل مع القيم الفارغة على الفور باستخدام عامل تشغيل Elvis (?:)، كما هو موضّح في ما يلي مثال:

val account = Account("name", "type")
val accountName = account.name?.trim() ?: "Default name"

إذا كانت نتيجة التعبير على الجانب الأيسر من عامل تشغيل Elvis هي فارغ، يتم تعيين القيمة الموجودة على الجانب الأيمن إلى accountName. هذا النمط مفيد لتوفير قيمة افتراضية كانت بخلاف ذلك تكون فارغة.

يمكنك أيضًا استخدام عامل التشغيل Elvis للعودة من دالة مبكرًا، كما هو موضح في المثال التالي:

fun validateAccount(account: Account?) {
    val accountName = account?.name?.trim() ?: "Default name"

    // account cannot be null beyond this point
    account ?: return

    ...
}

التغييرات في واجهة برمجة تطبيقات Android

أصبحت واجهات برمجة تطبيقات Android متوافقة بشكل متزايد مع لغة Kotlin. العديد من ميزات Android تحتوي أكثر واجهات برمجة التطبيقات شيوعًا، بما في ذلك AppCompatActivity وFragment، على التعليقات التوضيحية لقابلية القيم الفارغة، وبعض الطلبات مثل Fragment#getContext لديها المزيد من البدائل المتوافقة مع Kotlin.

على سبيل المثال، يكون الوصول إلى Context لـ Fragment غالبًا غير فارغ، نظرًا لأن معظم الاتصالات التي تجريها في Fragment تحدث أثناء استخدام Fragment مُلحق بـ Activity (فئة فرعية من Context). ومع ذلك، لا تعرض Fragment#getContext دائمًا قيمة غير فارغة، حيث تتوفر حيث لا يتم ربط Fragment بـ Activity. ومن ثم، يكون نوع Fragment#getContext قابل للقيم الفارغة.

بما أنّ قيمة Context المعروضة من Fragment#getContext قابلة للقيم الفارغة ( عليه التعليق كـ @Nullable)، فيجب التعامل معه كـ Context? في رمز Kotlin. يعني ذلك تطبيق أحد عوامل التشغيل المذكورة سابقًا لمعالجة قبول القيم الفارغة قبل الوصول إلى خصائصها ودوالها. بالنسبة لبعض هذه الأشياء يحتوي Android على واجهات برمجة تطبيقات بديلة توفر هذه الراحة. على سبيل المثال، تعرض الدالة Fragment#requireContext قيمة Context غير خالية وطرح IllegalStateException إذا تم طلبها عندما تكون قيمة Context فارغة. بهذه الطريقة، يمكنك التعامل مع Context الناتج على أنّه غير خالٍ بدون الحاجة إلى الاتصال الآمن أو الحلول البديلة.

إعداد الموقع

لا يتم إعداد الخصائص في Kotlin تلقائيًا. يجب إعدادها عند تهيئة فئة التضمين.

يمكنك إعداد المواقع بعدّة طرق مختلفة. المثال التالي توضح كيفية إعداد متغيّر index من خلال تحديد قيمة له في تصريح الفئة:

class LoginFragment : Fragment() {
    val index: Int = 12
}

يمكن أيضًا تحديد هذا الإعداد في كتلة أداة الإعداد:

class LoginFragment : Fragment() {
    val index: Int

    init {
        index = 12
    }
}

في الأمثلة أعلاه، يتم إعداد index عندما تكون LoginFragment البيانات التي تم إنشاؤها.

ومع ذلك، قد تكون لديك بعض السمات التي لا يمكن إعدادها أثناء الكائن. البناء. على سبيل المثال، ننصحك بالرجوع إلى View من داخل Fragment، ما يعني أنّه يجب تضخيم تنسيق التنسيق أولاً. التضخم لا تحدث عند إنشاء Fragment. بدلاً من ذلك، يتم تضخيم صوت الجهاز عند إجراء الاتصال. Fragment#onCreateView

تتمثل إحدى طرق معالجة هذا السيناريو في إعلان طريقة العرض على أنها قابلة للقيم الفارغة عليكم إعداده في أقرب وقت ممكن، كما هو موضح في المثال التالي:

class LoginFragment : Fragment() {
    private var statusTextView: TextView? = null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView?.setText(R.string.auth_failed)
    }
}

على الرغم من أنّ هذه العملية تعمل على النحو المتوقّع، عليك الآن إدارة إمكانية قبول القيم الفارغة في View. متى احتجت إليها. الحل الأفضل هو استخدام lateinit للنطاق الزمني View كما هو موضح في المثال التالي:

class LoginFragment : Fragment() {
    private lateinit var statusTextView: TextView

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)

            statusTextView = view.findViewById(R.id.status_text_view)
            statusTextView.setText(R.string.auth_failed)
    }
}

تسمح لك الكلمة الرئيسية lateinit بتجنّب إعداد موقع عند إنشاء الكائن. إذا تمت الإشارة إلى موقعك قبل إعداده، تُطرح لغة Kotlin UninitializedPropertyAccessException، لذا يُرجى التأكّد من لإعداد موقعك في أسرع وقت ممكن.