# Table of Contents

# 인텐트 플래그 설정 방법

인텐트 플래그를 설정하는 방법에는 두 가지가 있습니다.

  • setFlags(): 기존 플래그는 없애고 인자로 전달된 플래그를 설정합니다.
  • addFlags(): 기존 플래그를 유지하고 인자로 전달된 플래그를 추가합니다.

# 인탠트 플래그 종류

안드로이드에는 무수히 많은 인탠트 플래그가 있습니다. 이번 포스트에서는 자주 사용하는 인텐트 플래그에 대해 알아보겠습니다.

# FLAG_ACTIVITY_CLEAR_TOP

실행되는 액티비티가 이미 태스크에 존재할 경우, 그 위에 있는 액티비티들을 종료하고 실행되는 액티비티를 스택의 가장 위에 위치시킵니다.

val intent = Intent(this@ActivityD, ActivityB::class.java)
intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP)
startActivity(intent)

# FLAG_ACTIVITY_NO_ANIMATION

화면 전환 시 애니메이션을 없애줍니다.

val intent = Intent(this@ActivityA, ActivityB::class.java)
intent.setFlags(FLAG_ACTIVITY_NO_ANIMATION)
startActivity(intent)

# FLAG_ACTIVITY_SINGLE_TOP

launchMode="singleTop"과 동일합니다.

# FLAG_ACTIVITY_REORDER_TO_FRONT

실행되는 액티비티가 태스크에 이미 존재하면 태스크의 가장 위로 올려줍니다.

val intent = Intent(this@ActivityD, ActivityB::class.java)
intent.setFlags(FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivity(intent)

# FLAG_ACTIVITY_NO_HISTORY

Activity A > Activity B > Activity C 순서대로 실행한다고 가정합시다.

Activity A에서 Activity B를 실행할 때 FLAG_ACTIVITY_NO_HISTORY플래그를 설정하면 Activity B는 태스크에 추가되지 않고 화면전환만 됩니다.

val intent = Intent(this@ActivityA, ActivityB::class.java)
intent.setFlags(FLAG_ACTIVITY_NO_HISTORY)
startActivity(intent)

이제 Activity B에서 어떠한 플래그도 설정하지 않고 Activity C로 이동합시다.

val intent = Intent(this@ActivityB, ActivityC::class.java)
startActivity(intent)

Activity C에서 뒤로 가기(Back) 버튼을 누르면 Activity B가 아니라 Activity A로 이동합니다. Activity B는 태스크에 추가되지 않았기 때문입니다. 로딩 화면처럼 다시 돌아올 필요가 없는 화면에 사용합니다.

# FLAG_ACTIVITY_FORWARD_RESULT

Activity A > Activity B > Activity C로 화면 이동을 한다고 가정합시다. Activity C에서 Activity A로 돌아가면서 데이터를 반환하려면 Activity B에서 onActivityResult()를 구현하고, 이 안에서 다시 Activity A로 데이터를 반환하는 로직을 구현해야합니다.

이러한 경우 FLAG_ACTIVITY_FORWARD_RESULT을 사용하여 값을 쉽게 반환할 수 있습니다.

우선 Activity A에서 Activity B로 이동하는 코드는 다음과 같습니다.

class ActivityA : AppCompatActivity() {

    private val buttonOpenB: Button by lazy { findViewById<Button>(R.id.activity_a_button_open_b) }

    private val REQUEST_CODE = 1000

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

        buttonOpenB.setOnClickListener {
            // Open B 버튼을 눌렀을 때 Activity B 실행
            val intent = Intent(this, ActivityB::class.java)
            startActivityForResult(intent, REQUEST_CODE)
        }

        // ...
    }
}

Activity B에서 Activity C를 실행할 때는 startActivityForResult() 대신 startActivity()를 사용합니다. 또한 FLAG_ACTIVITY_FORWARD_RESULT플래그를 설정합니다.

class ActivityB : AppCompatActivity() {

    private val buttonOpenC: Button by lazy { findViewById<Button>(R.id.activity_b_button_open_c) }
    private val buttonCloseC: Button by lazy { findViewById<Button>(R.id.activity_b_button_close_c) }

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

        buttonOpenC.setOnClickListener {
            // Open C 버튼을 눌렀을 때 Activity C 실행
            val intent = Intent(this@ActivityB, ActivityC::class.java)
            // 플래그 설정
            intent.setFlags(FLAG_ACTIVITY_FORWARD_RESULT)
            startActivity(intent)
        }
    }
}

Activity C에서 Activity B로 돌아가는 로직은 다음과 같이 구현합니다.

class ActivityC : AppCompatActivity() {

    val buttonCloseC: Button by lazy { findViewById<Button>(R.id.activity_c_button_close_c) }

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

        buttonCloseC.setOnClickListener {
            // Close C 버튼을 눌렀을 때 데이터를 반환하면서 Activity C 종료
            val intent = Intent()
            intent.putExtra("name", "Paul")
            setResult(Activity.RESULT_OK, intent)
            finish()
        }

        // ...
    }
}

이제 Activity B에서 데이터를 받아 Activity A로 전달해봅시다.

Activity B에서 Activity C로 이동할 때 FLAG_ACTIVITY_FORWARD_RESULT플래그를 설정하지 않았다면, Activity B에 onActivityResult()를 구현하고 이 안에서 setResult()finish()를 호출하여 Activity A로 데이터를 반환하는 로직을 구현해야 합니다. 그러나 Activity B에서 Activity C로 이동할 때 플래그를 설정했으므로 그저 finish()만 호출하여 Activity B를 종료하면 됩니다.

class ActivityB : AppCompatActivity() {

    private val buttonOpenC: Button by lazy { findViewById<Button>(R.id.activity_b_button_open_c) }
    private val buttonCloseB: Button by lazy { findViewById<Button>(R.id.activity_b_button_back_to_a) }

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

        buttonOpenC.setOnClickListener {
            // ...
        }

        buttonCloseB.setOnClickListener {
            // Close B 버튼을 눌렀을 때 Activity B 종료
            finish()
        }
    }

    // onActivityResult()를 구현하지 않아도 됩니다.
    /* 
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        // ...
    }
    */
}

이제 Activity A에서 onActivityResult()를 구현하여 Activity C에서 온 데이터를 전달받을 수 있습니다.

class ActivityA : AppCompatActivity() {

    private val button: Button by lazy { findViewById<Button>(R.id.activity_a_button_open_b) }

    private val REQUEST_CODE = 1000

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

        button.setOnClickListener {
            // ...
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == REQUEST_CODE) {
            if (resultCode != Activity.RESULT_OK) { return }
            data?.extras?.getString("name")?.run {
                Toast.makeText(this@ActivityA, this, Toast.LENGTH_SHORT).show()
            }
        }
    }
}

# FLAG_ACTIVITY_NEW_TASK

새로운 태스크를 생성하고 그 안에서 액티비티를 실행합니다. 그러나 동일한 affinity를 가진 태스크가 존재하면 새로운 태스크를 생성하지 않고 기존의 태스크를 재활용합니다.

AndroidManifest.xml에 다음과 같이 네 개의 액티비티가 정의되어있다고 가정합시다. Activity A와 Activity B의 taskAffinity값이 동일하고, Activity C와 Activity D의 taskAffinity값이 동일합니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yologger.example">
 
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">

        <activity
            android:name=".ActivityA"
            android:taskAffinity="com.yologger.task1">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <activity android:name=".ActivityB"
            android:taskAffinity="com.yologger.task1"/>
 
        <activity android:name=".ActivityC"
            android:taskAffinity="com.yologger.task2"/>
 
        <activity android:name=".ActivityD"
            android:taskAffinity="com.yologger.task2"/>
 
    </application>
 
</manifest>

앱을 처음 실행하면 다음과 같은 상태입니다.

Activity A에서 다음과 같이 Activity B를 실행합시다.

val intent = Intent(this, ActivityB::class.java)
intent.setFlags(FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)

그럼 태스크는 아래와 같습니다. Activity A와 Activity B는 동일한 taskAffinity 속성값을 가지고 있기 때문입니다.

이제 Activity B에서 Activity C를 실행합니다.

val intent = Intent(this, ActivityC::class.java)
intent.setFlags(FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)

이제 태스크는 아래와 같습니다. Activity B와 Activity C는 다른 taskAffinity 속성값을 가지고 있기 때문입니다.

이제 Activity C에서 Activity D를 실행합니다.

val intent = Intent(this, ActivityD::class.java)
intent.setFlags(FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)

이제 태스크는 아래와 같습니다. Activity C와 Activity D는 동일한 taskAffinity속성값을 가지고 있기 때문입니다.

# FLAG_ACTIVITY_MULTIPLE_TASK

이 플래그는 FLAG_ACTIVITY_NEW_TASK와 같이 사용하지 않으면 아무련 효과가 없습니다. 액티비티를 실행하면 무조건 새로운 태스크를 생성하고 그 안의 루트 액티비티로 지정하여 실행합니다. 만약 동일한 taskAffinity속성값을 같는 태스크가 존재하면, 기존의 태스크를 제거하고 새로운 태스크를 생성한 후 루트 액티비티로 지정합니다.

바로 위 예제와 동일하게 AndroidManifest.xml에 네 개의 액티비티가 정의되어있습니다.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yologger.example">
 
    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
        <activity
            android:name=".ActivityA"
            android:taskAffinity="com.yologger.task1">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <activity android:name=".ActivityB"
            android:taskAffinity="com.yologger.task1"/>
 
        <activity android:name=".ActivityC"
            android:taskAffinity="com.yologger.task2"/>
 
        <activity android:name=".ActivityD"
            android:taskAffinity="com.yologger.task2"/>
 
    </application>
 
</manifest>

앱을 처음 실행하면 다음과 같은 상태입니다.

이제 FLAG_ACTIVITY_NEW_TASKFLAG_ACTIVITY_MULTIPLE_TASK를 모두 설정하여 Activity B를 실행합시다.

val intent = Intent(this, ActivityB::class.java)
intent.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK)
startActivity(intent)

이제 앱은 다음과 같은 상태가 됩니다.

FLAG_ACTIVITY_MULTIPLE_TASK플래그는 태스크를 무조건 생성합니다. 그런데 Activity A와 Activity B의 taskAffinity속성값이 동일합니다. 따라서 기존 태스크를 제거하고 새로운 태스크를 생성한 후 Activity B를 루트 액티비티로 지정합니다.

이제 다음과 같이 Activity B에서 Activity C를 실행합시다.

val intent = Intent(this, ActivityC::class.java)
intent.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK)
startActivity(intent)

Activity B와 Activity C는 taskAffinity 속성값이 다릅니다. 따라서 태스크는 두 개가 되며, 앱은 다음과 같은 상태가 됩니다.

마지막으로 Activity C에서 Activity D를 실행합시다.

val intent = Intent(this, ActivityD::class.java)
intent.setFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK)
startActivity(intent)

이제 앱은 다음과 같은 상태가 됩니다.