웹사이트 검색

Kotlin을 사용하여 활동 간 Android 인텐트 처리


이 튜토리얼에서는 Android 인텐트에 대해 논의하고 애플리케이션에서 Kotlin을 사용하여 구현합니다.

무엇을 배울 것인가?

  • 의도란 무엇입니까?
  • 의도 유형
  • 활동 간 의도 사용
  • Android 인텐트를 사용하여 데이터 전송
  • Parcelable 및 Serializable을 사용하여 개체 전달
  • 속기 인텐트 만들기

Android 인텐트

이름에서 알 수 있듯이 Intent는 Android 애플리케이션의 흐름과 관련하여 일부 작업을 수행하는 데 사용되는 것입니다. 인텐트는 다음과 같은 용도로 사용할 수 있습니다.

  • 새 활동을 시작하고 일부 데이터를 전달합니다.
  • 프래그먼트 시작/프래그먼트 간 통신.
  • 서비스 시작/종료.
  • 브로드캐스트 수신기에서 활동 시작

이 튜토리얼에서는 주로 활동을 처리하기 위한 인텐트를 살펴볼 것입니다. 의도 정의는 주로 현재 활동의 인스턴스로 구성됩니다. 다음과 같은 구성 요소 이름을 설정합니다. 호출할 활동의 정규화된 클래스 이름입니다. 이 유형의 의도는 명시적 의도입니다. 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 플래그를 사용하면 활동의 다른 인스턴스가 이미 존재하는 경우 시작되지 않습니다. 이 플래그는 호출된 활동 위의 모든 활동을 지우고 스택 맨 위에 설정합니다.

인텐트를 통한 데이터 전달

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

이러한 Extras 필드는 궁극적으로 전달할 모든 데이터를 보유하는 Bundle 개체로 래핑된 후드 아래에 있습니다. 다른 활동에서 데이터를 검색하려면 번들에 대해 extras 속성을 사용해야 합니다. 새 활동에서 데이터 검색

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

intent, extras는 Java의 getIntent(), getExtras()와 동일합니다. 데이터가 없을 때 NullPointerExceptions를 방지하기 위해 nullable 유형 Bundle?을 사용했습니다. 마찬가지로 키를 사용하여 가져온 데이터의 경우 키가 올바르지 않을 때 발생할 수 있는 NPE를 방지하기 위해 nullable 유형을 사용했습니다.

Parcelable 및 Serializable 데이터 사용

때때로 우리는 한 활동에서 다른 활동으로 완전한 객체를 전달해야 합니다. Parcelable 또는 Serializable 인터페이스를 구현하지 않으면 불가능합니다. Parcelable과 Serializable의 차이점

  • Parcelable 인터페이스는 Android SDK의 일부입니다. Serializable은 Java의 표준 인터페이스입니다.
  • Parcelable에서 Parcel 객체에 전달하는 데 필요한 모든 데이터를 설정하고 writeToParcel() 메서드 등을 재정의해야 합니다. 직렬화 가능 구현에서 인터페이스는 데이터를 전달하기에 충분합니다.
  • Parcelable은 Serializable보다 빠릅니다.

Parcelable 데이터 보내기

Kotlin은 Parcelable에 데이터를 설정하기 위해 writeToParcel() 메서드를 재정의하지 않도록 몇 가지 편리한 주석을 제공합니다. 대신 아래와 같이 @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)

직렬화 가능 데이터 보내기

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")
            }
        }
    }

}

위의 코드에서는 Intent 유형별로 Button을 사용했습니다. 우리는 Kotlin의 with 표현식을 사용하여 매번 intent 개체에 데이터를 설정하는 것을 방지했습니다. 게다가 위에서 이미 논의한 것과는 별도로 세 가지 의도를 만들었습니다. 브라우저 인텐트는 브라우저 앱의 인텐트에 있는 URL을 실행하는 데 사용됩니다. Intent(Intent.ACTION_VIEW, uri)를 사용합니다. 위치 인텐트는 지도 애플리케이션에서 lat,lng 위치를 실행하는 데 사용됩니다. 둘 다 암시적 인텐트입니다. 마지막으로 우리는 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.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 with data
                val string: String? = getString("keyString")
                textView.text = string

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

                val arrayList: ArrayList<String>? = 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 {
                //Serializable Data
                val blog = getSerializable("blogData") as Blog?
                if (blog != null) {
                    textView.text = "Blog name is ${blog?.name}. Year started: ${blog?.year}"

                }
            }

            bundle.apply {
                //Parcelable Data
                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()
    }
}

안드로이드 의도