Developing Myself Everyday
article thumbnail
Published 2023. 7. 23. 17:48
Android의 Intent (인텐트) Android

인텐트란?


인텐트는 안드로이드 애플리케이션에서 4대 컴포넌트 구성 요소들 간에 통신하는 데 사용되는 객체입니다. 인텐트는 Activity를 실행하는 데 가장 많이 사용되지만 다른 용도도 있습니다.

 

 

인텐트 유형에는 암시적 인텐트명시적 인텐트가 있습니다.


명시적 인텐트(Explicity Intent): 특정 컴포넌트를 명시하여 실행하는 인텐트
암시적 인텐트(Implicit Intent): 추상적이며 목적지를 명시적으로 지정하지 않고, 수신자가 필터를 통해 처리할 수 있는 인텐트

 

 

 

명시적 인텐트 예시 - Activity 이동

binding.button.setOnClickListener {
    val intent = Intent(this, SecondActivity::class.java).apply {
        putExtra("letter", "안녕하세요")
    }
    startActivity(intent)
}

 

위의 인텐트는  현재 액티비티에서 `SecondActivity`라는 액티비티로 이동하는 명시적 인텐트를 사용한 예시입니다. 

 

이동할 액티비티는 `SecondActivity::class.java`로 찾습니다. 이 때 리플렉션을 이용해서 액티비티를 찾게 됩니다.

 

그리고 putExtra 메서드를 호출하여 '문자'를 첫 번째 인수로 전달하고 "안녕하세요" 라는 String을 두 번째 인수로 전달합니다. 대상 액티비티의 인스턴스는 아직 생성되지 않았기에 존재하지 않습니다. extra는 나중에 검색할 수 있도록 이름이 지정된 숫자나 문자열과 같은 데이터 입니다.

 

마지막으로 컨텍스트 객체에서 `startActivity()` 메서드를 호출해 intent를 전달합니다.

 

이제 `SecondActivity`의 `onCreate()` 메서드에서 `setContentView` 호출 후에 인텐트에서 전달된 `letter`를 가져오는 코드를 호출합니다.

val letter = intent?.extras?.getString("letter").toString()
binding.textView.text = letter

 

위의 코드에서의 intent는 특정 액티비티의 속성이 아니라 모든 액티비티의 속성입니다.  extras 속성은 `Bundle` 유형이고 인텐트에 전달된 모든 extras에 액세스하는 방법을 제공합니다.

 

 

명시적 인텐트 예시 - 다른 애플리케이션 실행

명시적 인텐트를 사용해서 다른 애플리케이션을 실행할 수도 있습니다. 여기서는 유튜브를 한번 실행해 보겠습니다.

binding.youtubeButton.setOnClickListener {
    val intent = Intent(Intent.ACTION_MAIN).apply {
        `package` = "com.google.android.youtube"
    }
    startActivity(intent)
}

인텐트 안에 정의한 것은 `액션`입니다. 액션 해당 인텐트가 어떤 동작을 수행할지 명시하는 String입니다. 우리는 유튜브의 기본 화면을 표시할 예정이기에 `ACTION_MAIN`으로 설정했습니다.

 

우리의 만든 앱이 package가 있듯이, 우리가 사용하는 유튜브나 다른 앱도 package가 존재합니다.

package com.jun.myapplication

Intent에서 패키지를 설정하면 해당 앱을 타깃으로 할 수 있습니다.

 

 

다만 유튜브가 없는 기기의 경우에는 위의 코드론 오류가 발생할 수 있습니다.

binding.youtubeButton.setOnClickListener {
    val intent = Intent(Intent.ACTION_MAIN).apply {
        `package` = "com.google.android.youtube"
    }
    try {
        startActivity(intent)
    } catch (e: ActivityNotFoundException) {
        e.printStackTrace()
    }
}

그렇기에 위와 같이 오류를 처리해 줘야 합니다. 만약 유튜브가 없어서 오류가 발생했다면 여기서 유튜브를 설치하는 코드를 작성할 수도 있겠네요

 

 

 

 

 

암시적 인텐트

유튜브같이 명확하게 내가 어떤 앱을 실행할 것인지 확실한 경우에는 명시적 인텐트를 사용합니다. 하지만 만약 이메일을 전송하려고 한다고 생각해 보겠습니다.

 

우리는 어떤 앱을 사용해서 이메일을 전송할 것인지 정하지 못했습니다. 네이버, Gmail 등 선택지는 여러 개입니다. 그렇기에 이때 우리는 암시적 인텐트를 사용합니다.

binding.sendEmailButton.setOnClickListener {
    val intent = Intent(Intent.ACTION_SEND).apply {
        type = "text/plain"
        putExtra(Intent.EXTRA_EMAIL, arrayOf("jun@email.com"))
        putExtra(Intent.EXTRA_SUBJECT, arrayOf("메일 제목"))
        putExtra(Intent.EXTRA_TEXT, arrayOf("메일 내용"))
    }
    if (intent.resolveActivity(packageManager) != null) {
        startActivity(intent)
    }
}

위에서는 아까와 같이 액션을 정의하고 있습니다. 추가적으로 설정해야 할 것은 type입니다. type으로 인텐트가 처리하려는 데이터의 종류를 나타낼 수 있습니다.

 

그리고 resolveActivity(packageManager)를 통해 해당하는 앱이 있는지 확인합니다.

 

아마 이대로라면 아래와 같은 경고가 발생할 것입니다.

이는 구글에서 우리가 만든 앱이 manifest에서 <queries>를 작성하는 경우에만 이에 해당하는 앱을 검색할 수 있게 하기 윈 하기 때문에 생기는 경고입니다.

 

이런 경고가 발생하는 이유는 우리가 이메일을 전송할 수 있는 앱을 열고 싶어서 인텐트를 전송할 때, 이메일과 관련 없는 앱가지 우리가 보낸 인텐트를 수신하게 하고 싶지는 않기 때문입니다.

<queries>
    <intent>
        <action android:name="android.intent.action.SEND"/>
        <data android:mimeType="text/plain"/>
    </intent>
</queries>

코드블록이 위와 같이 추가한다면 경고는 사라집니다.

 

 

 

 

 

인텐트 필터

우리가 만든 앱에서 다른 앱을 실행하고 데이터를 보낼 수 있다는 것을 확인했습니다. 그렇다면 반대도 가능하지 않을까요?

 

이를 위한 것이 바로 인텐트 필터입니다.

 

인텐트 필터를 학습하기 위해 크롬에서 고양이 사진을 검색하고 해당 사진을 우리의 앱에서 수신하고 이를 띄워보는 방법을 알아보겠습니다.

 

 

크롬에서 내가 원하는 고양이 사진을 선택하고 이를 전달합니다. 다만, 위에서 알 수 있듯이, 우리의 앱은 보이지가 않습니다. 그렇기에 우리는 앱의 manifest에서 우리가 이미지를 수신할 Activity의 <intent-filter> 요소에서 수신할 작업을 정의해서 우리의 앱이 보이게 해야 합니다.

<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>
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*"/>
    </intent-filter>

    <meta-data
        android:name="android.app.lib_name"
        android:value="" />
</activity>

MainActivity에는 초기에 실행될 때 필요한 인텐트 필터가 정의되어 있습니다. 그렇기에 우리가 원하는 작업을 하기 위해서는 추가로 인텐트 필터를 정의합니다. 아까 전에 queries를 정의할 때와 비슷합니다. 

 

 

다른 앱에서 인텐트를 보내서 우리 앱이 열리게 되는 것은 우리가 원하는 바입니다.

 

그런데 만약 우리의 앱이 이미 실행되어서 백스택에 있다면 어떻게 될까요?

 

 바로 우리의 앱이 이미 켜져 있더라도 켜져 있는 인스턴스틀 사용하는 것이 아니라 새로운 인스턴스를 생성해 우리의 앱을 실행하게 됩니다. 이렇게 되면 이전에 실행했던 우리의 앱의 정보는 사라지고 새롭게 액티비티가 생성됩니다.

 

이를 해결하는 방법은 기존에 열려있던 우리의 앱의 인스턴스를 가져오기 위해 launchModesigleTop으로 변경해야 합니다.

<activity
    android:name=".MainActivity"
    android:launchMode="singleTop"
    android:exported="true">

 

이와 관련된 내용은 복잡하기에 여기서는 그냥 넘어가겠습니다. 관심이 있다면 아래의 게시글을 참고해 주세요.

 

작업 및 백 스택 이해

이 게시글은 아래의 문서를 보고 작성했습니다. 작업 및 백 스택 이해 | Android 개발자 | Android Developers 일반적으로 앱에는 여러 활동이 포함됩니다. 각 활동은 사용자가 실행할 수 있는 특정 종류

everyday-develop-myself.tistory.com

 

이제 우리의 앱을 공유하기에서 찾을 수 있습니다.

 

 

 

 

보내진 인텐트의 수신은 액티비티의 생명주기 중 하나인 onNewIntent()에서 할 수 있습니다.

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            intent?.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)
        } else {
            intent?.getParcelableExtra(Intent.EXTRA_STREAM)
        }

        binding.imageView.load(uri)
    }

 

 

 

 

 

 

Reference

 

활동 및 인텐트  |  Android Developers

암시적 인텐트와 명시적 인텐트로 탐색을 사용하도록 미완성 앱을 업데이트합니다.

developer.android.com

 

profile

Developing Myself Everyday

@배준형

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!