Study/Android

인텐트

hegunhee 2021. 12. 19. 23:06

Do it! 깡쌤의 안드로이드 앱 프로그래밍 with 코틀린(강성윤) 책을 정리하면서 적은 글입니다.

상당히 오랜만에 글을 쓰네요 다름이 아니라 안드로이드 4대 구성요소를 다시한번 정리해보려고 합니다.

 

Intent란?


안드로이드 앱은 모두 4개의 컴포넌트로 개발하는데 이때 핵심 클래스가 바로 Intent입니다.

인텐트는 '컴포넌트를 실행하려고 시스템에 전달하는 메시지' 라고 정의할 수 있습니다.

인텐트는 컴포넌트를 실행하는 정보이며 이 정보가 담긴 인텐트 객체를 시스템에 전달하면 컴포넌트가 실행됩니다.

즉 컴포넌트는 개별적으로 작동하며 그 사이를 중재하는 역할을 하는것이 인텐트 입니다.

Activity -> Activity

Activity -> Service , Activity -> BroadCast Receiver

 

액티비티 화면을 전환하거나 다른 컴포넌트를 사용할때 단순히 객체를 생성하듯이 사용할 수 없습니다.

왜냐하면 컴포넌트 클래스는 시스템이 직접 실행하는 클래스이고 생명주기가 따로 존재하기때문에 Intent를 사용해야합니다.

예를들어서 MainActivity클래스에서 DetailActivity클래스를 실행하려면 시스템에 인텐트를 전달해 줘야 합니다.

그러면 시스템에서 인텐트의 정보를 분석해서 그에 맞는 컴포넌트를 실행해 줍니다.

 

이러한 인텐트의 중재 역할은 같은 앱의 컴포넌트뿐만 아니라 외부 앱의 컴포넌트와 연동할 때도 마찬가지입니다.

물론 외부 앱의 컴포넌트와 연동할때는 규칙이 있습니다.

 

액티비티는 매니페스트 파일에 등록해야 합니다. 액티비티 클래스 하나당 <activity> 태그 하나로 등록해야 하며, 이때 액티비티의 클래스 이름을 지정하는 name 속성은 생략할 수 없습니다.

        <activity android:name=".ServiceActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

액티비티 뿐만 아니라 서비스, 브로드캐스트 리시버, 콘텐츠 프로바이더도 매니페스토 파일에 등록해야 합니다.

이처럼 안드로이드 컴포넌트를 매니페스토 파일에 등록해야 하는 이유는 시스템에 컴포넌트를 알려야 하기 때문입니다.

시스템은 런타임 때 매니페스트 파일에 정보를 참조하여 앱을 실행합니다. 만약 어떤 컴포넌트를 개발해 놓고 매니페스트 파일에 등록하지 않으면 시스템은 해당 컴포넌트를 알 수 없습니다. 따라서 인텐트가 해당 컴포넌트를 실행할 수 없습니다.

DetailActivity()  / X
val intent : Intent = Intent(this, DetailActivity::class.java)
startActivity(intent)   /O

위의 코드처럼 코틀린에서 객체를 생성하듯 하면 안되고 intent에 보여주고자 하는 액티비티의 정보를 담고

startActivity에 intent를 넣어줍니다.

위의 DetailActivity::class.java는 클래스 타입 레퍼런스 정보이며 런타임때 해당 클래스의 정보를 불러오는 것입니다.

 

 

인텐트 엑스트라 데이터


만약 MainActivity에서 인텐트를 이용해 DetailActivity를 실행할 때 데이터를 전달해야 한다면 어떻게 해야할까?

인텐트에 컴포넌트 실행을 요청할때 데이터를 함께 전달하려면 엑스트라 데이터를 이용해야 합니다.

엑스트라 데이터는 인텐트에 담는 부가 정보라 할 수 있습니다.

인텐트에 엑스트라 데이터를 추가하는 함수는 putExtra()입니다.

putExtra() 함수의 첫 번째 매개변수는 데이터의 식별자이며 두 번째 매개변수가 전달할 데이터입니다.

즉 key-value 형식으로 value를 보내며 key값으로 데이터를 불러올 수 있습니다.

putExtra() 함수는 각 타입의 데이터를 담을 수 있도록 오버로딩으로 선언되어 있습니다.

val intent : Intent = Intent(this, DetailActivity::class.java) // 여기서 this는 Context
intent.putExtra("data1","hello")
intent.putExtra("data2",10)
startActivity(intent)

여기서 보낸 데이터를 받아오려면 intent 객체를 받고 그 인텐트 객체의 getIntExtra()함수로 데이터를 가져오면 됩니다.

데이터를 가져오는 함수 또한 타입별로 여러 개 제공합니다.

getIntExtra(String name,int defaultValue) // 여기서 name에 데이터를 보낼때 사용했던 key값을 사용하면 받을 수 있습니다.

여기서 필자만의 사용법을 알려주자면 key값이 오타가 나서 데이터를 못받는다거나 에러가 발생할 수 있는데 이 값을 companion object의 상수값으로 지정하면 오타가 발생하지 않습니다. 설령 변수명을 잘못쓰더라도 컴파일 에러가 발생합니다.

    companion object{
        const val STR_DATA = "data1"
    }
    intent.putExtra(STR_DATA,10)
    
    // 받는쪽
    intent.getIntExtra(STR_DATA,0)
val intent = intent
val data1 = intent.getStringExtra("data1")
val data2 = intent.getIntExtra("data2",0)

액티비티 화면 되돌리기


액티비티는 화면을 구성하는 컴포넌트입니다.

따라서 한 액티비티에서 다른 액티비티를 인텐트로 실행하면 화면이 전환됩니다. 

화면이 전환되었을 때 의도에 따라 화면을 되돌리거나 되돌리지 않을 수도 있습니다.

이런 상황을 고려해 액티비티를 시작하는 함수는 2개 입니다.

startActivity(Intent intent)

startActivityForResult(Intent intent, int requestCode)

startActivity() 함수는 화면을 되돌릴 필요가 없을 때 사용하며, startActivityForResult() 함수는 결과를 포함해 화면을 돌릴때 사용합니다. 혹은 화면이 되돌아왔을때 어떤 작업을 해야된다거나, 어떤 데이터를 받아야 할때 사용합니다.

startActivityForResult(intent, 10)

startActivityForResult() 함수로 액티비티를 시작할 때 두 번째 매개변수는 개발자가 정하는 요청코드(requestCode)이며 인텐트를 식별하는 값입니다.

이 값은 startActivityForResult() 함수로 결과를 돌려받은 후 별도로 처리될 때 필요합니다.

 

사용자가 뒤로가기 버튼을 누르지 않고 자동으로 화면을 되돌릴 때 finish() 함수를 이용합니다. finish() 함수는 현재 화면에 보이는 액티비티를 종료해 달라고 시스템에 요청합니다.

intent.putExtra("resultData","world") // finish해서 돌아가는쪽으로 데이터를 보냄
setResult(RESULT_OK,intent)
finish()

finish() 함수를 호출하기 전에 결과 데이터를 인텐트 객체에 담을 수 있으며 이때 자신을 실행한 인텐트 객체에 엑스트라 데이터로 담으면 됩니다.

setResult() 함수의 매개변수로 들어가는 RESULT_OK는 성공적으로 일을 마쳤다 라는것이며 requestCode는 전의 화면으로 돌아갈때 어디서 돌아오는 것인지 알려주는 코드입니다.

RESULT_OK, RESULT_CANCELD 등을 지정하며 이것은 resultCode 라고 합니다.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if(requestCode == 10 && resultCode == Activity.RESULT_OK){
        val result = data?.getStringExtra("resultData")
    }
}

startActivityForResult 함수를 호출해서 다른 액티비티를 실행하고 돌아올경우 onActivityResult 함수를 호출합니다.

여기서 requestCode가 어떤것인지 그리고 resultCode가 RESULT_OK인지를 검사한 후 데이터를 불러오고 처리를 해줍니다.

  • requestCode : 인텐트를 시작한 곳에서 인텐트를 구분하려고 설정한 요청 코드입니다.
  • resultCode : 인텐트로 실행된 곳에서 돌려받은 결과 코드입니다.
  • data : 인텐트 객체입니다. 이 객체에 결과 데이터가 있습니다.

인텐트 필터


지금까지 살펴본 내용을 보면 인텐트를 시스템에 전달할 때 실행할 대상 컴포넌트 정보를 다음처럼 지정했습니다.

val intent = Intent(this,DetailActivity::class.java)

이 코드에서 실행할 대상 컴포넌트는 DetailActivity::class.java입니다. 이 정보는 클래스 타입 레퍼런스 입니다. 같은 앱의 컴포넌트라면 이처럼 클래스 타입 레퍼런스로 설정할 수 있지만, 외부 앱의 컴포넌트는 이렇게 실행할 수 없습니다.

 

그래서 인텐트는 실행할 컴포넌트 정보를 어떻게 설정하는지에 따라 다음과 같이 2개로 나뉩니다.

  • 명시적 인텐트 : 클래스 타입 레퍼런스 정보를 활용한 인텐트입니다.
  • 암시적 인텐트 : 인텐트 

앞의 코드처럼 클래스 타입 레퍼런스를 이용하는 것을 명시적 인텐트 라고하며 내부 앱의 컴포넌트를 요청하는 인텐트 객체를 만들 때 사용합니다. 그리고 외부 앱의 컴포넌트는 클래스 타입 레퍼런스를 활용할 수 없으므로 암시적 인텐트를 이용합니다.

암시적 인텐트는 매니페스토 파일에 선언된 인텐트 필터를 이용합니다.

<activity android:name=".OneActivity" />
<activity android:name=".TwoActivity" />
	<intent-filter>
    	<action android:name="ACTION_EDIT" />
    </intent-filter>
</activity>

앱 내부에서만 이용하는 컴포넌트라면 android:name 속성만 선언하면 됩니다. 그런데 어떤 컴포넌트를 외부에서도 인텐트로 실행할 수 있어야 한다면 해당 컴포넌트가 있는 앱의 매니페스토 파일에 암시적으로 실행할 수 있게 <intent-filter>를 설정해 줘야 합니다.

<intent-filter>는 <activity>, <service>, <receiver>등 컴포넌트 등록 태그에 작성할 수 있습니다. <intent-filter>태그는 컴포넌트를 암시적으로 실행할 때만 추가하여 반드시 사용할 필요는 없습니다.

어디선가 이 컴포넌트를 실행하려면 시스템에 등록된 정보에 맞게 인텐트를 설정합니다.

그리고 동적으로도 할당할 수 있는데 이건 브로드캐스트 리시버 부분에서 자세히 설명하겠습니다.

 

인텐트 필터 하위에는 <action>, <category>, <data> 태그를 이용한 정보를 설정할 수 있습니다. 어떤 정보를 설정할 것인지는 개발자의 선택입니다. <action>만 선언할 수도 있고 <data>를 함께 선언할 수도 있습니다.

  • <action> : 컴포넌트의 기능을 나타내는 문자열입니다.
  • <category> : 컴포넌트가 포함되는 범주를 나타내는 문자열입니다.
  • <data> : 컴포넌트에 필요한 데이터 정보입니다.

<action> 태그의 android:name에 설정하는 값은 개발자가 임의로 지정하는 문자열이며 앱에서 유일하지 않아도 됩니다. 그런데 컴포넌트의 기능을 나타낼 것을 권장하고 있습니다.

 

<category> 태그의 android:name에 설정하는 문자열은 컴포넌트가 어느 범주에 포함되어야 하는지를 의미합니다. 개발자가 임의로 지정할 수도 있지만 대부분 플랫폼 API에서 제공하는 문자열을 제공합니다.

ex) 인터넷, 지도, 전화

예를들어 android.intent.category.LAUNCHER는 런처가 실행하는 컴포넌트라는 의미이고, android.intent.category.BROWSER는 브라우저가 실행하는 컴포넌트라는 의미입니다.

 

<data> 태그는 컴포넌트에서 어떤 성격의 데이터를 처리하는지를 나타냅니다.

<data>는 URL형식으로 표현하며 <action>이나 <category>처럼 문자열 하나로 선언하지 않고 android:scheme, android:host, android:port, android:mimeType 등의 속성을 이용합니다. 이러한 속성을 모두 선언할 필요는 없으며 필요한 만큼만 선언하면 됩니다. 

scheme 속성은 URL의 프로토콜명으로, 데이터의 성격을 한정 지을 때 사용합니다. host는 URL의 도메인이며 port는 URL의 포트입니다. 그리고 mimeType은 데이터 타입으로 text/plain이나 image/* 등을 선언할 수 있습니다.

 

        <activity android:name=".ServiceActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

앱을 실행했을때 가장 먼저 실행하는것을 지정한것입니다.

사용자가 앱을 실행하면 외부 앱에서 실행하는 것이므로 매니페스트 파일에 인텐트 필터를 등록해야합니다.

런처 앱은 시스템에서 LAUNCHER 범주의 정보를 가져와 앱 아이콘을 나열합니다. 따라서 앱의 첫 화면을 나타내는 액티비티에는 <action> 태그의 name 속성값을 android.intent.action.MAIN으로 선언하고, <category> 태그의 name 속성값은 android.intent.category.LAUNCHER로 선언한 인텐트 필터를 설정해야 합니다.

 

앱의 첫 화면인 MainActivity의 인텐트 필터로 설명했지만 다른 컴포넌트도 외부 앱과 연동하려면 인텐트 필터를 등록해야 합니다. 만약 TwoActivity를 다음처럼 등록했다면 인텐트 필터에 선언된 정보와 맞게 액티비티를 시작해야 합니다.

<activity android:name=".TwoActivity">
	<intent-filter>
    	<action android:name="ACTION_EDIT"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="http"/>
    </intent-filter>
</activity>

다음은 인텐트의 action과 data 프로퍼티에 실행 대상인 컴포넌트 정보를 지정하는 코드입니다. 이때 컴포넌트 정보는 매니페스트에 선언한 정보를 이용합니다. data값은 URL문자열인데 안드로이드에서 URL 문자열은 Uri객체로 표현합니다.

val intent = Intent()
intent.action = "ACTION_EDIT"
intent.data = "Uri.parse("http://www.google.com")
startActivity(intent)

앞의 코드는 action, data 정보를 인텐트의 각 프로퍼티에 설정한 것이며 다음처럼 매개변수로 지정해줘도 됩니다.

val intent = Intent("ACTION_EDIT",Uri.parse("http://www.google.com")
startActivity(intent)

'Study > Android' 카테고리의 다른 글

서비스 컴포넌트  (0) 2021.12.24
액티비티 생명주기  (0) 2021.12.20
브로드캐스트 리시버 BroadCast Receiver  (1) 2021.10.30
액티비티의 life cycle  (0) 2021.10.29
안드로이드 4대 컴포넌트(구성요소)  (0) 2021.10.28