관리 메뉴

나만을 위한 블로그

[Kotlin] 안드로이드 인텐트로 액티비티 간 데이터 전달하는 법 본문

개인 공부/Kotlin

[Kotlin] 안드로이드 인텐트로 액티비티 간 데이터 전달하는 법

참깨빵위에참깨빵 2020. 10. 17. 18:10
728x90
반응형

안드로이드 스튜디오에서 코틀린으로 앱을 만들 때 자바를 쓸 때처럼 인텐트로 액티비티를 전환하거나, 인텐트에 데이터를 넣어 다른 액티비티에서 사용해야 할 필요가 있다.

이 글에선 이런 처리들을 어떻게 하는지 포스팅하려고 한다.

 

먼저 MainActivity의 XML 코드다. 아이디와 비밀번호만 사용할 거기 때문에 간단하게 짰다.

 

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="메인 액티비티"
        android:textSize="30sp"
        android:gravity="center"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dp"
        android:orientation="horizontal"
        android:weightSum="1">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight=".3"
            android:gravity="end"
            android:text="아이디"
            android:layout_marginRight="30dp"/>

        <EditText
            android:id="@+id/edit_id"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight=".7"
            android:hint="아이디 입력"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:orientation="horizontal"
        android:weightSum="1">

        <TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight=".3"
            android:gravity="end"
            android:text="비밀번호"
            android:layout_marginRight="30dp"/>

        <EditText
            android:id="@+id/edit_pw"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight=".7"
            android:hint="비밀번호 입력"/>

    </LinearLayout>

    <Button
        android:id="@+id/login_btn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="50dp"
        android:text="로 그 인"/>

    <Button
        android:id="@+id/register_btn"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="50dp"
        android:text="회 원 가 입"/>

</LinearLayout>

다음은 .kt 파일을 코딩한다.

 

class MainActivity : AppCompatActivity()
{
    private val TAG: String = "MainActivity"

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

        login_btn1.setOnClickListener {
            val id = edit_id.text.toString()
            val pw = edit_pw.text.toString()

            if (id == "123" && pw == "123")
            {
                Log.e(TAG, "로그인 성공")
            }

            val intent = Intent(this, AfterLoginActivity::class.java)
            intent.putExtra("id", id)
            intent.putExtra("pw", pw)
            startActivity(intent)
        }
    }
}

아이디, 비밀번호에 모두 123이란 값을 넣고 버튼을 누르면 로그인 성공이라는 로그가 나오고 다른 액티비티로 이동되도록 했다.

그럼 이동된 액티비티에서 로그를 찍어서 정상적으로 데이터가 이동됐는지 확인하자.

 

class AfterLoginActivity : AppCompatActivity()
{
    private val TAG: String = "AfterLoginActivity"

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

        if (intent.hasExtra("id") && intent.hasExtra("pw"))
        {
            val id = intent.getStringExtra("id")
            val pw = intent.getStringExtra("pw")
            Log.e(TAG, "메인에서 받아온 id : $id, pw : $pw")
        }
        else
        {
            Log.e(TAG, "가져온 데이터 없음")
        }
    }
}

if()에 hasExtra()를 넣어서 키값이 있는지 체크하는 부분이 있는 걸 빼면 별로 특별한 코드는 없다.

이제 앱을 빌드해보면 아래와 같은 로그가 찍힌다.

 

깔끔하게 로그에 찍혀나오는 걸 볼 수 있다.

 

여기서 생각해봐야 할 것이 지금은 아이디와 비밀번호라는 2가지 값만 이동하지만, 만약 10개가 넘는 데이터를 옮겨야 한다면 어떨까?

그 때마다 일일이 putExtra()로 값 넣고 목표지에서 getStringExtra()같은 get()으로 인텐트를 풀고 처리하는 방법도 있다.

그리고 새로 클래스를 하나 파서 그 클래스의 변수를 넘기게 하는 방식도 있을 것이다.

아래에서 설명하는 방식이 바로 클래스의 변수를 인텐트에 넣어 이동시키는 방식이다.

예시기 때문에 위에서 사용한 아이디, 비밀번호 2개 변수만 갖는 Person이라는 클래스를 만들겠다.

 

import android.os.Parcel
import android.os.Parcelable

class Person constructor(val id: String, val pw: String) : Parcelable
{
    constructor(parcel: Parcel) : this(
        parcel.readString()?: "",
        parcel.readString()?: ""
    )
    {
        //
    }

    override fun writeToParcel(dest: Parcel, flags: Int)
    {
        dest.writeString(id)
        dest.writeString(pw)
    }

    override fun describeContents(): Int
    {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Person>
    {
        override fun createFromParcel(parcel: Parcel): Person
        {
            return Person(parcel)
        }

        override fun newArray(size: Int): Array<Person?>
        {
            return arrayOfNulls(size)
        }
    }
}

constructor() 뒤의 : 표시는 자바의 implements 키워드다. : Parcelable은 implements Parcelable과 같은 효과를 낸다.

그런데 Parcelable은 뭐하는 키워드일까?

 

Parcelable은 안드로이드에서 액티비티, 프래그먼트 간에 데이터를 주고받을 때 구현하는 기법이다.

직렬화/역직렬화라는 이름으로 불리기도 한다. Parcelable 말고도 Serializable이라는 기법도 있지만 이 글에선 생략한다.

Parcelable은 안드로이드 SDK의 라이브러리로, Serializable보다 속도가 빠르고 안드로이드에서 사용하기를 권장하는 기법이다.

단점이라면 개발자가 만들어줘야 하는 요소들이 좀 있어서 키보드를 두드려야 하는 것 정도다.

실제로 저 코드를 처음부터 만든다면 이런 꼴이 된다. 먼저 Person이란 클래스를 만들고, 생성자로 id와 pw를 만든 뒤, 뒤에 : Parcelable 키워드를 붙였다.

 

class Person constructor(val id: String, val pw: String) : Parcelable
{
}

이 상태에서 class Person을 보면 이 부분에 빨간 줄이 그어져 있다.

이 부분에 커서를 찍고 Alt + Enter를 누르면 아래와 같은 화면이 나온다.

 

여기서 1번 'Add Parcelable Implementation'을 선택하면 갑자기 3개의 메서드들이 자동으로 구현된다.

 

class Person constructor(val id: String, val pw: String) : Parcelable
{
    constructor(parcel: Parcel) : this(
        parcel.readString(), parcel.readString()
    )
    {
        //
    }

    override fun writeToParcel(parcel: Parcel, flags: Int)
    {
        parcel.writeString(id)
        parcel.writeString(pw)
    }

    override fun describeContents(): Int
    {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Person>
    {
        override fun createFromParcel(parcel: Parcel): Person
        {
            return Person(parcel)
        }

        override fun newArray(size: Int): Array<Person?>
        {
            return arrayOfNulls(size)
        }
    }
}

그런데 constructor(parcel: Parcel) : this() 안의 readString() 두 곳에서 빨간 줄이 그어져 있다.

이 부분은 위에서 먼저 보여준 코드로 바꾸거나, Alt + Enter를 눌러 toString()을 붙여주면 사라진다.

toString()을 붙인다면 아래와 같은 코드가 된다.

 

class Person constructor(val id: String, val pw: String) : Parcelable
{
    constructor(parcel: Parcel) : this(
        parcel.readString().toString(), parcel.readString().toString()
    )
    {
        //
    }

    override fun writeToParcel(parcel: Parcel, flags: Int)
    {
        parcel.writeString(id)
        parcel.writeString(pw)
    }

    override fun describeContents(): Int
    {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Person>
    {
        override fun createFromParcel(parcel: Parcel): Person
        {
            return Person(parcel)
        }

        override fun newArray(size: Int): Array<Person?>
        {
            return arrayOfNulls(size)
        }
    }
}

어떤 형태를 써도 상관없는 듯하니 자신에게 편한 방법을 사용하자.

그럼 이제 Person 클래스를 써서 생성자에 아이디, 비밀번호를 정의한 뒤 다른 액티비티로 이동시켜보자.

MainActivity와 AfterLoginActivity에 코드를 몇 줄 추가한다.

 

class MainActivity : AppCompatActivity()
{
    private val TAG: String = "MainActivity"

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

        login_btn1.setOnClickListener {
            val id = edit_id.text.toString()
            val pw = edit_pw.text.toString()

            if (id == "123" && pw == "123")
            {
                Log.e(TAG, "로그인 성공")
            }

            val intent = Intent(this, AfterLoginActivity::class.java)
            intent.putExtra("id", id)
            intent.putExtra("pw", pw)

            val person = Person("123", "123") // <- 추가된 부분
            intent.putExtra("person", person)
            startActivity(intent)
        }
    }
}
class AfterLoginActivity : AppCompatActivity()
{
    private val TAG: String = "AfterLoginActivity"

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

        if (intent.hasExtra("id") && intent.hasExtra("pw"))
        {
            val id = intent.getStringExtra("id")
            val pw = intent.getStringExtra("pw")
            val person = intent.getParcelableExtra<Person>("person")

            if (person != null)  // Null 체크
            {
                Log.e(TAG, "메인에서 받아온 id : $id, pw : $pw, person : ${person.id} / ${person.pw}")
            }
        }
        else
        {
            Log.e(TAG, "가져온 데이터 없음")
        }
    }
}

주의할 것은 AfterLoginActivity에 있는 if (person != null)문의 중괄호 안이다.

코틀린은 문자열 안이라도 $만 붙인다면 변수를 가져와 쓸 수 있다. 그런데 Parcelable을 구현한 클래스의 변수값을 표시하는 곳은 앞뒤로 {}가 붙어있는 걸 볼 수 있다.

이 괄호들을 없애고 그냥 $person.id와 $person.pw라고 쓰면 무슨 일이 일어날까? 결과는 직접 확인해보고 왜 그런지도 알아보자.

 

위처럼 만들고 앱을 빌드하면 아래와 같은 로그가 출력된다.

 

생성자로 넣어준 2개의 123이 로그에 찍혀나오는 걸 볼 수 있다.

반응형
Comments