MOBILE PROGRAMING

[모바일 프로그래밍] Activity와 Intent(양방향)

ch010104 2025. 10. 29. 20:42

1. 액티비티 간 양방향 데이터 전달 (Activity Result API)

  • Activity Result API를 이용하여 액티비티 간 양방향 데이터 전달을 구현함.
  • 실행 흐름은 크게 Part 1 (SecondActivity 실행)Part 2 (데이터 회신)로 구성됨.

Part 1: ActivityResultLauncher 실행 (MainActivity)

  • 1. ActivityResultLauncher 객체 생성 (호출 측)
    • <MainActivity>에서 registerForActivityResult()를 이용해 ActivityResultLauncher 객체를 생성하고 초기화.
    • 이때 실제 작업자인 Contract 객체와, 결과 수신 시 호출될 콜백(callback) 메소드를 등록.
      val requestLauncher: ActivityResultLauncher<Intent> = registerForActivityResult(
          ActivityResultContracts.StartActivityForResult()
      ) { 
          /* * SecondActivity에서 결과를 받았을 때 호출될 콜백 함수 구현 
           * (예: it.resultCode, it.data 등을 확인)
           */
      }
      
  • 2. ActivityResultLauncher 실행 (호출)
    • 호출할 대상 액티비티(예: SecondActivity)를 명시한 인텐트(Intent)를 생성.
    • (필요시) intent.putExtra()를 사용해 데이터 추가.
    • activityResultLauncher.launch(intent) 메소드를 호출하여 인텐트를 실행.
      val intent: Intent = Intent(applicationContext, SecondActivity::class.java)
      // intent.putExtra(...) // 필요시 데이터 추가
      requestLauncher.launch(intent)
      
  • 3. 데이터 수신 (피호출측)
    • <SecondActivity>(피호출측)는 getIntent()를 통해 인텐트를 수신.
    • getExtra() 등을 사용해 <MainActivity>가 전달한 데이터를 추출.

Part 2: 데이터 회신 (SecondActivity)

  • 4. 결과 인텐트 생성 (피호출측)
    • <SecondActivity><MainActivity>로 회신할 txIntent (전송용 인텐트)를 생성.
    • txIntent.putExtra()를 사용해 반환할 데이터를 추가.
  • 5. 결과 설정 및 액티비티 종료 (피호출측)
    • setResult(<result-code>, txIntent) 메소드를 호출하여 결과 코드(예: RESULT_OK)와 데이터(txIntent)를 설정.
    • (이후 finish()가 호출되면 액티비티가 종료되고 결과가 전달됨)
  • 6. 결과 수신 (호출측)
    • <MainActivity>에서 ActivityResultLauncher 생성 시 등록했던 콜백 메소드가 실행됨.
    • 콜백 메소드는 <result-code>와 데이터를 포함한 ActivityResult 객체를 파라미터로 받음.

2. 실습 : 양방향 데이터 전달 (덧셈 + 뺄셈)

  • Activity Result API를 이용해 MainActivityAddActivity로 구성된 양방향 데이터 전달 앱 구현.
  • 실행 흐름:
    1. MainActivity에서 숫자 2개(예: 10, 20)를 입력하고 'ADD' 버튼 클릭.
    2. AddActivity가 실행됨.
    3. AddActivity는 'RESPOND THE SUM' 버튼을 눌러 합계(예: 30)를 MainActivity로 회신.
    4. MainActivity는 콜백을 통해 결과를 받아 "Sum: 30"을 화면에 출력.
  • 확장:
    • 기존 덧셈 앱에 뺄셈 연산을 위한 SubActivity를 추가.
    • MainActivity는 '더하기' 버튼과 '빼기' 버튼을 가짐.
    • MainActivity result-code를 통해 결과를 반환한 액티비티를 구분.
      • AddActivity  setResult() <result-code> = 1001을 반환.
      • SubActivity  setResult() <result-code> = 1002를 반환.

// MainActivity.kt

package com.cookandroid.activityexcercise2

import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.cookandroid.activityexcercise2.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var bindingMain : ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
//        enableEdgeToEdge()
//        setContentView(R.layout.activity_main)
//        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
//            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
//            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
//            insets
//        }

        bindingMain = ActivityMainBinding.inflate(layoutInflater)
        setContentView(bindingMain.root)

        val requestLauncher : ActivityResultLauncher<Intent> = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()){
            // AddActivity로부터 결과를 수신 받으면 호출할 callback 함수

            if(it.resultCode == 1001) { // resultCode를 AddActivity로부터 확인
                val responseIntent : Intent? = it.data
                val sum : Int? = responseIntent?.getIntExtra("Sum", 0)
                Toast.makeText(this, "Sum = " + sum?.toString(), Toast.LENGTH_LONG).show()
            }

            if(it.resultCode == 1002) { // resultCode를 SubActivity로부터 확인
                val responseIntent : Intent? = it.data
                val sub : Int? = responseIntent?.getIntExtra("Sub", 0)
                Toast.makeText(this, "Sub = " + sub?.toString(), Toast.LENGTH_LONG).show()
            }
        }

        bindingMain.btnAdd.setOnClickListener(object : View.OnClickListener {
            override fun onClick(p0: View?){
                val mIntent : Intent = Intent(applicationContext, AddActivity::class.java)
                mIntent.putExtra("Num1", Integer.parseInt(bindingMain.edtNum1.text.toString()))
                mIntent.putExtra("Num2", Integer.parseInt(bindingMain.edtNum2.text.toString()))

                requestLauncher.launch(mIntent)
            }
        })

        bindingMain.btnSub.setOnClickListener(object : View.OnClickListener {
            override fun onClick(p0: View?){
                val mIntent : Intent = Intent(applicationContext, SubActivity::class.java)
                mIntent.putExtra("Num1", Integer.parseInt(bindingMain.edtNum1.text.toString()))
                mIntent.putExtra("Num2", Integer.parseInt(bindingMain.edtNum2.text.toString()))

                requestLauncher.launch(mIntent)
            }
        })

    }

}
// AddActivity.kt

package com.cookandroid.activityexcercise2

import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.cookandroid.activityexcercise2.databinding.ActivityAddBinding

class AddActivity : AppCompatActivity() {

    private lateinit var bindingAdd : ActivityAddBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        bindingAdd = ActivityAddBinding.inflate(layoutInflater)
        setContentView(bindingAdd.root)

        val rxIntent : Intent = getIntent()

        val rxNum1 : Int = rxIntent.getIntExtra("Num1", 0)
        val rxNum2 : Int = rxIntent.getIntExtra("Num2", 0)
        val sum : Int = rxNum1 + rxNum2

        // AddActivity를 종료시켜서 자동으로 MainActivity로 돌아감
        bindingAdd.btnReturn.setOnClickListener(object: View.OnClickListener {
            override fun onClick(p0: View?){
                val responseIntent : Intent = Intent(applicationContext, MainActivity::class.java)
                responseIntent.putExtra("Sum", sum)
                setResult(1001,responseIntent) // setResult(<result code>, txIntent)
                finish()
            }
        })
    }

}
// SubActivity.kt

package com.cookandroid.activityexcercise2

import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.cookandroid.activityexcercise2.databinding.ActivitySubBinding

class SubActivity : AppCompatActivity() {

    private lateinit var bindingSub : ActivitySubBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        bindingSub = ActivitySubBinding.inflate(layoutInflater)
        setContentView(bindingSub.root)

        val rxIntent : Intent = getIntent()

        val rxNum1 : Int = rxIntent.getIntExtra("Num1", 0)
        val rxNum2 : Int = rxIntent.getIntExtra("Num2", 0)
        val sub : Int = rxNum1 - rxNum2

        // SubActivity를 종료시켜서 자동으로 MainActivity로 돌아감
        bindingSub.btnReturn.setOnClickListener(object: View.OnClickListener {
            override fun onClick(p0: View?){
                val responseIntent : Intent = Intent(applicationContext, MainActivity::class.java)
                responseIntent.putExtra("Sub", sub)
                setResult(1002,responseIntent) // setResult(<result code>, txIntent)
                finish()
            }
        })
    }
}
// AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ActivityExcercise2">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity android:name=".AddActivity"
            android:exported="true"/>

        <activity android:name=".SubActivity"
            android:exported="true"/>

    </application>

</manifest>
// activity_main.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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/edtNum1"
        android:hint="enter first number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <EditText
        android:id="@+id/edtNum2"
        android:hint="enter second number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:id="@+id/btnAdd"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Add"/>

    <Button
        android:id="@+id/btnSub"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Sub"/>


</LinearLayout>
// activity_add.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="@android:color/holo_blue_bright"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btnReturn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="RESPOND THE SUM"/>

</LinearLayout>
// activity_sub.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:background="@android:color/holo_green_light"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btnReturn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="RESPOND THE SUB"/>

</LinearLayout>