使用Kotlin在活动之间处理Android Intent

在本教程中,我们将讨论 Android Intents 并使用 Kotlin 在我们的应用程序中实现它们。

你将学到什么?

  • 什么是 Intents?
  • Intents 的类型?
  • 在活动之间使用 Intents
  • 使用 Android Intents 发送数据
  • 使用 Parcelable 和 Serializable 传递对象
  • 创建快捷 Intent

Android Intents

如其名称所示,Intent 是用于根据 Android 应用程序的流程执行某些操作的东西。Intents 可以用于:

  • 启动新活动并传递一些数据。
  • 启动片段/在片段之间进行通信。
  • 启动/停止服务。
  • 从广播接收器启动活动

在本教程中,我们主要将关注处理活动的 Intents。Intent 定义主要包括当前活动的一个实例。我们设置组件名称,可以是:要调用的活动的完全限定类名。这种类型的 Intent 是显式 Intent。例如 URL、电话号码、位置的操作。它将显示所有这些类型的可用应用程序。这属于隐式 Intent类别。在 Kotlin 中,以下是创建活动的方式。

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

`startActivity` 会在活动堆栈上添加 `OtherActivity` 并启动它。我们的应用程序如何知道要调用哪个活动? 在 AndroidManifest.xml 中,我们在第一个要启动的活动上设置意图过滤器,使用操作 android.intent.action.MAIN 和类别 android.intent.category.LAUNCHERfinish() 用于销毁活动并将其从堆栈中移除。

意图标志

标志就像是可以设置在意图上以自定义启动过程的选项。如果每次启动相同的活动,都会创建一个新实例并将其添加到活动堆栈上。为了防止这种情况发生,您可以使用标志:FLAG_ACTIVITY_SINGLE_TOP – 如果设置,如果活动已经在活动堆栈的顶部运行,则不会启动该活动。

intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP

类似地,使用标志 FLAG_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对象中,最终保存所有要传递的数据。要在其他活动中检索数据,我们需要使用bundles上的extras属性。在新的活动中检索数据

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

intentextras相当于Java中的getIntent()getExtras()。我们使用了可空类型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
    }
//..
....
}

在您的`Activity`中执行以下操作:

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>

Activity 代码

下面是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")
            }
        }
    }

}

在上面的代码中,我们为每种类型的Intent使用了按钮。我们使用了Kotlin的with表达式来避免每次在intent对象上设置数据。此外,我们创建了三种不同的Intent,除了已经讨论过的。浏览器Intent用于在浏览器应用程序中打开Intent中存在的URL。它使用Intent(Intent.ACTION_VIEW, uri)。位置Intent用于在地图应用程序中启动lat,lng位置。这两个都是隐式Intent。最后,我们使用了一个通用Intent,在其中使用Kotlin的扩展函数和lambda表达式来创建一个启动Intent的简写函数。为此,我们使用以下函数:

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 {
                //带有数据的意图
                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 {
                //可传递数据
                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()
    }
}

我们使用letapply来处理可空类型,避免在每一行中都使用bundle.field。上述应用的输出如下: 这就结束了关于Kotlin中Android意图的教程。您可以从下面的链接下载该项目。

AndroidlyIntents

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