Activity
클래스는 안드로이드 앱의 핵심적인 구성 요소이다. Activity
들이 어떻게 실행되고 조합되느냐가 안드로이드 애플리케이션의 기본 구성이 된다.
안드로이드 시스템은 main()
메서드를 통해 앱들을 실행하는 프로그래밍 패러다임과는 다른 방식으로 작동한다. 안드로이드 시스템은 Activity
의 생명주기 단계에 대응하는 콜백 함수들을 호출함으로써 코드를 실행시킨다.
모바일 앱 환경은 사용자와의 상호작용이 항상 같은 위치에서 시작되지 않는다. 이 점이 데스크톱 환경과 다른 점이다.
대신 사용자는 비결정적으로 앱을 탐색한다. 예를 들어 홈 화면에서 이메일 앱을 실행시키면, 다양한 리스트의 이메일을 볼 수 있다. 반대로 소셜 미디어 앱을 통해 이메일 앱을 열면, 대상에게 이메일을 보내는 화면으로 바로 이동할 수도 있다.
Activity
클래스는 이러한 패러다임을 가능케 한다. 하나의 앱이 다른 앱을 실행시킬 때, 다른 앱 전체를 실행시키는 것이 아닌, 앱의 액티비티를 실행시킨다. 즉, Activity
는 앱과 사용자간의 상호작용을 위한 진입점 역할을 한다. 이를 위해 Activity
클래스의 하위 클래스를 구현해야 한다.
Activity
는 앱이 UI를 그리는 창을 제공한다. 보통 이 창이 화면 전체를 채우지만, 보다 작은 사이즈로 구성하고 다른 창 위에 띄울 수도 있다. 일반적으로 하나의 액티비티가 앱의 한 화면을 담당한다. 예를 들어 하나의 Activity
가 설정 화면을 구현하고, 또 다른 하나의 Activity
가 사진 선택 화면을 구현한다.
대부분의 앱은 다양한 화면을 포함하기에, 다양한 Activity
로 구성되어 있다. 일반적으로 하나의 Activity
가 앱을 실행할 때 가장 먼저 표시되는 화면인 MainActivity
로 지정된다. 이후 각각의 Activity
는 다른 기능을 수행하기 위해 다른 Activity
를 실행시킬 수 있다. 예를 들어 간단한 이메일 앱에서 MainActivity
는 받은 메일들을 보여주는 화면일 것이다. 이후 MainActivity
는 메일 작성, 메일 자세히 보기 등의 작업을 수행하기 위해 다른 Activity
를 실행시킬 것이다.
응집력 있는 UX를 제공하기 위해 다양한 Activity
들이 협력하지만, 각각의 액티비티는 서로 느슨하게 결합되어있다. 일반적인 앱에는 Activity
간 최소한의 의존성만 존재한다. 실제로 브라우저 앱이 소셜 미디어 앱의 공유하기 Activity
를 실행시키듯, Activity
는 다른 앱의 액티비티를 실행시키기도 한다.
앱에서 Activity
를 사용하려면 관련 정보를 앱의 매니페스트에 등록하고, Activity
의 수명을 적절히 관리해야 한다. 이제부터 그 방법에 대해 설명한다.
앱에서 Activity
를 사용하려면 해당 Activity
와 몇 가지 속성들을 매니페스트에 선언해야 한다.
Activity
를 선언하기 위해, 매니페스트 파일을 열고 <activity>
요소를 <application>
요소의 자식으로 추가한다.
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
<activity>
요소에 필요한 유일한 속성은 Activity의 클래스명을 지정하는 android:name
이다. 그 외에도 label, icon, UI 테마 등의 Activity
속성을 지정할 수 있다. 이에 대한 추가 정보는 문서에서 확인할 수 있다.
중요: 앱을 출시하고 나면, 특정 Activity
의 이름을 바꾸면 안 된다. 만약 바꿀 경우, 앱 숏컷 등 특정 기능이 동작하지 않을 수 있다. 출시 후 변경되면 안 되는 것들에 대한 자세한 정보는 Things That Cannot Change 문서를 통해 확인할 수 있다.
Intent filter는 안드로이드 플랫폼의 매우 강력한 기능이다. Intent filter를 통해 명시적인 요청 뿐 아니라, 암시적인 요청을 통해 액티비티를 실행시킬 수 있다. 명시적인 요청은 시스템에게 “Gmail 앱의 SendEmailActivity
를 실행시켜”라고 말하는 것이다. 반대로 암시적인 요청은 시스템에게 “메일을 전송할 수 있는 화면을 보여주는 Activity
를 실행시켜”라고 말하는 것이다. 시스템 UI가 사용자에게 특정 작업을 위해 어떤 앱을 사용할지 묻는다면, intent filter가 사용된 것이다.
<activity>
요소의 <intent-filter>
속성을 선언함으로서 해당 기능을 사용할 수 있다. <intent-filter>
는 <action>
과 필요에 따라 <category>
, <data>
요소로 이뤄진다. 이러한 요소들을 결합해 Activity
가 응답할 수 있는 인텐트의 유형을 지정한다. 아래 코드는 텍스트 데이터를 전송하고 다른 Activity
로부터 요청을 수신할 수 있는 Activity
를 선언하는 방법이다.
<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
위 예시에서 <action>
은 해당 Activity
가 데이터를 전송한다는 것을 명시한다. <category>
를 DEFAULT
로 선언하면 Activity
가 실행 요청을 수신할 수 있도록 한다. <data>
는 이 Activity
가 보낼 수 있는 데이터의 타입을 명시한다. 다음 코드는 위 Activity
를 호출하는 방법을 보여준다.
val sendIntent = Intent().apply {
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, textMessage)
}
startActivity(sendIntent)
외부에서 다른 앱이 해당 Activity
를 실행시키지 못하게 독립적으로 구성하고 싶다면 intent filter를 사용할 필요가 없다. 다른 앱이 이용 불가능하도록 하려는 Activity
는 intent filter 없이 명시적인 Intent
를 통해서만 실행되어야 한다. 어떻게 Activity
가 Intent
에 응답하는지 확인하려면 Intents and Intent Filters 문서를 참고해라.
매니페스트의 <activity>
태그를 통해 어떤 앱들이 특정 Activity
를 실행할 수 있는지 제어할 수 있다. 만약 부모 Activity
와 자식 Activity
가 매니페스트에 같은 권한을 갖고 있지 않다면, 부모 Activity
는 자식 Activity
를 실행시킬 수 없다. 만약 부모 Activity
에 <uses-permission>
요소를 선언한다면, 각 자식 Activity
들은 일치하는 <uses-permission>
요소를 가져야 한다.
당신의 앱이 SociaApp이라는 이름의 가상 앱을 통해 소셜 미디어에 게시글을 공유하려고 한다고 가정하자. SocialApp은 스스로 자신을 호출하려는 앱이 가져야 하는 권한을 정의해야 한다.
<manifest>
<activity android:name="...."
android:permission=”com.google.socialapp.permission.SHARE_POST”
/>
이제 SocialApp을 실행하려면 당신의 앱은 SocialApp의 메니페스트에 설정된 권한과 일치해야 한다.
<manifest>
<uses-permission android:name="com.google.socialapp.permission.SHARE_POST" />
</manifest>
권한과 보안에 대한 추가 정보는 Security and Permissions 문서에서 확인할 수 있다.
Activity
는 자신의 수명 동안 여러 상태를 거치게 된다. 일련의 콜백을 통해 상태 간 전환을 다룰 수 있다. 다음 섹션에서 이러한 콜백들을 소개한다.
시스템이 Activity
를 생성할 때 호출되는 콜백 함수로, 반드시 구현해야 하는 함수이다. 해당 함수의 구현은 View
를 생성하고 데이터를 리스트에 연결하는 등, Activity
에 필요한 구성 요소들을 초기화해야 한다. 앱의 UI를 위해 setContentView()
를 반드시 호출해야 하는 곳이기도 하다.
onCreate()
함수가 수행되고 나면, 다음 콜백 함수는 항상 onStart()
가 된다.
onCreate()
함수가 끝나면 Activity
는 Started 상태가 되고 사용자에게 보이기 시작한다. onStart()
는 Activity
가 포그라운드에 나와 상호작용할 수 있도록 최종적으로 준비하는 코드를 포함한다.
onResume()
이 콜백 함수는 Activity
가 사용자와 상호작용하기 직전에 시스템에 의해 호출된다. 이 시점에서 Activity
는 Activity Stack의 최상위가 되고, 유저의 입력을 수집한다. 앱의 핵심 기능 대부분은 onResume()
메서드에 구현되어 있다.
onPause()
콜백 함수는 항상 onResume()
다음에 호출된다.
Activity
가 포커스를 잃고 Paused 상태로 돌입하면 시스템은 onPause()
를 호출한다. 유저가 뒤로가기나 최근 사용한 앱 버튼을 눌렀을 때 이 상태가 된다. 시스템이 Activity
의 onPause()
함수를 호출한다는 것은 엄밀히 말하면 Activity
가 부분적으로 표시된다는 의미이다. 하지만 대부분의 경우 사용자가 Activity
를 종료하고 Activity
가 Stopped 또는 Resumed 상태가 될 것이라는 것을 의미한다.
Paused 상태의 Activity
에서도 UI가 업데이트될 것이라 예상된다면 UI 업데이트를 지속할 수 있다. 예를 들어, 네비게이션 지도 화면이나 미디어를 보여주는 Activity
에서 포커스를 잃었더라도 UI가 계속 업데이트될 수 있다.
onPause()
함수에서 앱 또는 사용자 데이터를 저장하거나, 네트워크 통신을 호출하거나, 데이터베이스 트랜잭션을 실행해서는 안 된다. 데이터 저장에 관해서는 Saving and restoring activity state 문서를 참고하라.
onPause()
의 다음 콜백은 Activitiy
가 Paused 상태가 되고 난 후의 상황에 따라 onStop()
또는 onResume()
이 된다.
Activity
가 더 이상 사용자에게 보이지 않을 때, 시스템은 onStop()
을 호출한다. Activity
가 파괴되고 새 Activity
가 시작되거나, 기존 Activity
가 Resumed 상태가 되면서 Activity
를 덮어버릴 때 호출될 수 있다. 이 모든 경우에 Stopped 상태가 된 Activity
는 더 이상 보이지 않게 된다.
시스템이 호출하는 다음 콜백은 onRestart
또는 onDestroy
이다. Activity
가 다시 사용자와 상호작용한다면 onRestart()
가 되고, 완전히 제거된다면 onDestroy()
가 된다.
Stopped 상태의 Activity
가 새로 시작하려고 할 때, 시스템은 onRestart()
를 호출한다. onRestart()
는 앱이 Stopped 상태로 돌입했을 때의 Activity
상태를 복원한다.
이 콜백은 항상 onStart()
이후에 수행된다.
Activity
가 파괴되기 전에 시스템은 onDestroy()
를 호출한다.
이 콜백은 Activity
가 수신하는 마지막 콜백이다. onDestroy()
는 일반적으로 Activity
또는 프로세스가 파괴될 때, 사용되던 모든 자원이 해제됨을 보장하기 위해 구현된다.
Activity 생명 주기와 콜백을 더 자세히 다루려면 The Activity Lifecycle 문서를 참고하라.
https://developer.android.com/guide/components/activities/intro-activities