معالجة Android Intent بين الأنشطة باستخدام Kotlin

في هذا البرنامج التعليمي، سنناقش نوايا Android وتنفيذها باستخدام Kotlin في تطبيقنا.

ما الذي ستتعلمه؟

  • ما هي النوايا؟
  • أنواع النوايا؟
  • استخدام النوايا بين الأنشطة
  • إرسال البيانات باستخدام Android Intents
  • استخدام Parcelable و Serializable لتمرير الكائنات
  • إنشاء نوايا اختصارية

Android Intents

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

  • بدء نشاط جديد وتمرير بعض البيانات.
  • بدء Fragments/التواصل بين الفراغات.
  • بدء/إنهاء الخدمة.
  • بدء الأنشطة من مستقبل البث

في هذا البرنامج التعليمي، سننظر بشكل رئيسي إلى النوايا للتعامل مع الأنشطة. تتكون تعريف النية بشكل رئيسي من مثيل للنشاط الحالي. نحدد اسم المكون الذي يمكن أن يكون: الاسم الكامل المؤهل للفئة للنشاط المراد استدعاؤه. هذا النوع من النية هو نية صريحة. إجراء مثل عنوان URL أو رقم هاتف أو موقع. سيتم عرض جميع التطبيقات المتاحة من هذه الأنواع. يندرج ذلك تحت فئة النية الضمنية. في Kotlin، يتبع الطريقة التالية لإنشاء نشاط.

val intent = Intent(this, OtherActivity::class.java)
startActivity(intent)

startActivity ستضيف OtherActivity على مكدس الأنشطة وتطلقها. كيف يدرك تطبيقنا أي نشاط يتم استدعاؤه أولا؟ في AndroidManifest.xml نقوم بتعيين عامل التصفية النائية مع الإجراء android.intent.action.MAIN والفئة android.intent.category.LAUNCHER على أول نشاط يتم تشغيله عند فتح تطبيقنا. finish() يستخدم لتدمير نشاط وإزالته من المكدس.

علامات النوايا

العلامات مثل الخيارات التي يمكن تعيينها على النوايا لتخصيص عملية الإطلاق. إذا بدأت نفس النشاط في كل مرة، سيتم إنشاء نسخة جديدة وإضافتها إلى مكدس الأنشطة. لمنع هذا، يمكنك استخدام العلامات: FLAG_ACTIVITY_SINGLE_TOP – إذا تم تعيينها، لن يتم تشغيل النشاط إذا كان قيد التشغيل بالفعل في أعلى مكدس الأنشطة.

intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP

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

تمرير البيانات من خلال النوايا

لتمرير البيانات إلى الأنشطة الجديدة، نستخدم أزواج المفاتيح والقيم داخل الدالة putExtra، putStringArrayListExtra وما إلى ذلك. تمرر putExtra عادة الأنواع الأساسية مثل Int، Float، Char، Double، Boolean، String بالإضافة إلى IntArray… الخ

val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("keyString", "Androidly String data")

تكون حقول Extras هذه تحت الغطاء ملفوفة داخل كائن Bundle الذي يحتوي في النهاية على جميع البيانات التي سيتم تمريرها. لاسترجاع البيانات في النشاط الآخر، نحتاج إلى استخدام خاصية extras على bundles. استرجاع البيانات في النشاط الجديد

val bundle: Bundle? = intent.extras
val string: String? = intent.getString("keyString")
val myArray: ArrayList<String>? = intent.getStringArrayList("myArray")

intent، extras يعادلان getIntent()، getExtras() في Java. قد استخدمنا نوعًا قابلًا للتعيين Bundle؟ لمنع حدوث NullPointerExceptions عندما لا توجد بيانات. بالمثل، للبيانات التي تم جلبها باستخدام المفاتيح، قد استخدمنا الأنواع القابلة للتعيين لتجنب NPE التي يمكن أن تحدث عندما يكون المفتاح غير صحيح.

استخدام بيانات Parcelable وSerializable

في بعض الأحيان نحتاج إلى تمرير كائن كامل من نشاط إلى آخر. لا يمكن فعل ذلك ما لم نقم بتنفيذ واجهة Parcelable أو Serializable. الفرق بين Parcelable وSerializable

  • واجهة Parcelable هي جزء من Android SDK. Serializable هي واجهة قياسية في Java.
  • في Parcelable، تحتاج إلى تعيين جميع البيانات التي تحتاج إلى تمريرها في كائن Parcel وأيضاً تجاوز أساليب writeToParcel() وما إلى ذلك. أما في Serializable، فإن تنفيذ الواجهة يكفي لتمرير البيانات.
  • Parcelable أسرع من Serializable.

إرسال بيانات Parcelable

تأتي Kotlin مع بعض التعليقات المفيدة لتوفيرنا من تجاوز طريقة writeToParcel() لتعيين البيانات على Parcelable. بدلاً من ذلك، يمكننا استخدام @Parcelize كما هو موضح أدناه:

@Parcelize
data class Student(
        val name: String = "Anupam",
        val age: Int = 24
) : Parcelable

ملاحظة: حاليًا في build.gradle الخاص بك، يجب عليك إضافة الكود التالي لعمل تعليق @Parcelize:

android {
    androidExtensions {
        experimental = true
    }
//..
....
}

في نشاطك، قم بفعل الآتي:

val student = Student()
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("studentData", student)
startActivity(intent)

إرسال بيانات Serializable

data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable

val blog = Blog("a", 1)
val intent = Intent(this, OtherActivity::class.java)
intent.putExtra("blogData", blog as Serializable)
startActivity(intent)

لنستخدم المعرفة أعلاه في مشروعنا في Android Studio.

هيكل المشروع

رمز التخطيط

الرمز الخاص بتخطيط activity_main.xml يُظهر أدناه:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/btnSimpleIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SIMPLE INTENT" />


    <Button
        android:id="@+id/btnSimpleIntentAndData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="SIMPLE INTENT WITH DATA" />


    <Button
        android:id="@+id/btnParcelableIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Parcelable Intent" />


    <Button
        android:id="@+id/btnSerializableIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Serializable Intent" />

    <Button
        android:id="@+id/btnBrowserIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Browser Intent" />


    <Button
        android:id="@+id/btnMapsIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Maps Intent" />


    <Button
        android:id="@+id/btnGenericIntent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Generic Intent" />

</LinearLayout>

الرمز الخاص بتخطيط activity_other.xml يُظهر أدناه:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Intent Data goes here" />


</LinearLayout>

رمز النشاط

الرمز الخاص بفئة MainActivity.kt يُظهر أدناه:

package net.androidly.androidlyintents

import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Parcelable
import android.view.View
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_main.*
import java.io.Serializable


@Parcelize
data class Student(
        val name: String = "Anupam",
        val age: Int = 24
) : Parcelable

data class Blog(val name: String = "Androidly", val year: Int = 2018) : Serializable


class MainActivity : AppCompatActivity(), View.OnClickListener {


    fun Context.gotoClass(targetType: Class<*>) =
            ComponentName(this, targetType)

    fun Context.startActivity(f: Intent.() -> Unit): Unit =
            Intent().apply(f).run(this::startActivity)

    inline fun <reified T : Activity> Context.start(
            noinline createIntent: Intent.() -> Unit = {}
    ) = startActivity {
        component = gotoClass(T::class.java)
        createIntent(this)
    }


    var arrayList = ArrayList<String>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        btnSimpleIntent.setOnClickListener(this)
        btnSimpleIntentAndData.setOnClickListener(this)
        btnParcelableIntent.setOnClickListener(this)
        btnSerializableIntent.setOnClickListener(this)
        btnBrowserIntent.setOnClickListener(this)
        btnMapsIntent.setOnClickListener(this)
        btnGenericIntent.setOnClickListener(this)

        arrayList.add("Androidly")
        arrayList.add("Android")
        arrayList.add("Intents")
    }

    override fun onClick(v: View?) {
        when (v?.id) {
            R.id.btnSimpleIntent -> {
                val intent = Intent(this, OtherActivity::class.java)
                startActivity(intent)
            }
            R.id.btnSimpleIntentAndData -> {
                val intent = Intent(this, OtherActivity::class.java)
                with(intent)
                {
                    putExtra("keyString", "Androidly String data")
                    putStringArrayListExtra("arrayList", arrayList)
                    putExtra("keyBoolean", true)
                    putExtra("keyFloat", 1.2f)
                }
                startActivity(intent)
            }
            R.id.btnParcelableIntent -> {

                val student = Student()
                val intent = Intent(this, OtherActivity::class.java)
                intent.putExtra("studentData", student)
                startActivity(intent)
            }
            R.id.btnSerializableIntent -> {
                val blog = Blog("a", 1)
                val intent = Intent(this, OtherActivity::class.java)
                intent.putExtra("blogData", blog as Serializable)
                startActivity(intent)
            }
            R.id.btnBrowserIntent -> {
                val url = "https://www.androidly.net"
                val uri = Uri.parse(url)
                val intent = Intent(Intent.ACTION_VIEW, uri)

                if (intent.resolveActivity(packageManager) != null) {
                    startActivity(intent)
                } else {
                    Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
                }
            }
            R.id.btnMapsIntent -> {
                val loc = "12.9538477,77.3507442"

                val addressUri = Uri.parse("geo:0,0?q=" + loc)
                val intent = Intent(Intent.ACTION_VIEW, addressUri)


                if (intent.resolveActivity(packageManager) != null) {
                    startActivity(intent)
                } else {
                    Toast.makeText(applicationContext, "No application found", LENGTH_LONG).show()
                }
            }
            else -> start<OtherActivity> {
                putExtra("keyString", "Androidly Generic Intent")
            }
        }
    }

}

في الرمز أعلاه، قمنا باستخدام الأزرار لكل نوع من النوايا (Intents). لقد استخدمنا تعبير with في Kotlin لمنع تعيين البيانات عبر كائن النية intent في كل مرة. بالإضافة إلى ذلك، قمنا بإنشاء ثلاث نيات مختلفة بصرف النظر عن تلك التي تم مناقشتها بالفعل. يُستخدم توجيه المتصفح لفتح عنوان URL الموجود في النية في تطبيق المتصفح. ويستخدم Intent(Intent.ACTION_VIEW، uri). تُستخدم النية المتعلقة بالموقع لفتح الموقع في تطبيق الخرائط. كلاهما نيات ضمنية. أخيرًا، قمنا باستخدام نية عامة نستخدم فيها وظائف التمديد وتعبيرات اللامبدا في Kotlin لإنشاء وظيفة مختصرة لفتح نية. لهذا الغرض، نستخدم الوظائف التالية:

fun Context.gotoClass(targetType: Class<*>) =
            ComponentName(this, targetType)

    fun Context.startActivity(createIntent: Intent.() -> Unit): Unit =
            Intent().apply(createIntent).run(this::startActivity)

    inline fun <reified T : Activity> Context.start(
            noinline createIntent: Intent.() -> Unit = {}
    ) = startActivity {
        component = gotoClass(T::class.java)
        createIntent(this)
    }

startActivity هو دالة تمديد تبحث عن دالة ترتيب عالية كمعلمة لها. بفضل هذا، يمكننا الآن إطلاق النوايا في عدد قليل من الأسطر كما في: start<OtherActivity> الشيفرة لفئة OtherActivity.kt معطاة أدناه.

package net.androidly.androidlyintents

import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_other.*

class OtherActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_other)

        val bundle: Bundle? = intent.extras

        bundle?.let {

            bundle.apply {
                //Intent مع بيانات
                val string: String? = getString("keyString")
                textView.text = string

                val myArray: ArrayList? = getStringArrayList("myArray")
                showToast(message = "MyArrayList size:${myArray?.size}")

                val arrayList: ArrayList? = getStringArrayList("arrayList")
                showToast(message = "ArrayList size:${arrayList?.size}")

                val float: Float? = bundle.get("keyFloat") as Float?
                var boolean = bundle.get("boolean") as? Boolean

                showToast(message = "Float data is:$float")
                showToast(message = "Boolean data is:$boolean")
                boolean = bundle.get("keyBoolean") as? Boolean
                showToast(message = "Boolean correct key data is:$boolean")

            }



            bundle.apply {
                //بيانات قابلة للتسلسل
                val blog = getSerializable("blogData") as Blog?
                if (blog != null) {
                    textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"

                }
            }

            bundle.apply {
                //بيانات Parcelable
                val student: Student? = getParcelable("studentData")
                if (student != null) {
                    textView.text = "Name is ${student?.name}. Age: ${student?.age}"
                }
            }
        }
    }

    private fun showToast(context: Context = applicationContext, message: String, duration: Int = Toast.LENGTH_SHORT) {
        if (!message.contains("null"))
            Toast.makeText(context, message, duration).show()
    }
}

لقد استخدمنا let و apply للتعامل مع أنواع nullable وتجنب عمل bundle.field في كل سطر. إخراج التطبيق أعلاه في العمل معروض أدناه: هذا ينهي هذا البرنامج التعليمي حول النوايا في Android بلغة Kotlin. يمكنك تحميل المشروع من الرابط أدناه.

AndroidlyIntents

Source:
https://www.digitalocean.com/community/tutorials/android-intent-handling-between-activities-using-kotlin