[안드로이드] 안드로이드 액티비티(Activity)
액티비티는 안드로이드 어플리케이션을 구성하는 가장 기본적인 빌딩블록입니다. 보통의 경우 한 화면을 차지하면서 뷰(View)로 구성된 유저 인터페이스를 화면에 표시하고 사용자의 입력을 처리하는 역할을 합니다.보통의 어플리케이션은 여러 다른 화면을 가지고 있고, 각각의 화면은 다른 액티비티로 구현되어있습니다. 그러므로 화면의 전환이 이루어지게되면 새로운 액티비티가 실행되어 처리하게 됩니다. 어떤 경우는 액티비티 간에 데이터를 서로 주고 받을 수도 있습니다. 새로운 화면이 생성되며 기존의 화면은 스택에 놓여집니다. 각각의 액티비티는 스택을 통해 관리되며 현재 액티비티를 종료하면 그 이전 화면의 액티비티로 돌아가게 됩니다.
그럼 우선적으로 액티비티가 API로 살펴 보도록 하겠습니다. 3
http://developer.android.com/reference/android/app/Activity.html
java.lang.Object | ||||
↳ | android.content.Context | |||
↳ | android.content.ContextWrapper | |||
↳ | android.view.ContextThemeWrapper | |||
↳ | android.app.Activity |
위와 같은 상속 계보도를 가지며 API에서 엄청나게 많은 내용들을 설명하고 있습니다.
영어인 관계로 자세한 설명은 스스로 해결하시기를 바라구요. 중요한 몇가지 부터 살펴 보도록 하겠습니다.
1. 생명주기(Activity Lifecycle)
Activity에는 다음과 같은 생명주기 메소드들이 존재 합니다.
protected void onCreate(Bundle savedInstanceState);
protected void onStart();
protected void onRestart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestroy();
}
아래의 강의는 타 사이트로 부터 스크랩한 자료입니다. 정리가 잘되어 있으니 많은 도움이 되실 겁니다.
1. Activity의 4가지 주요 상태 (4 essential states of Activity)
Active/Running 상태
Activity A가 화면의 가장 앞(foreground)에 있어 사용자가 직접 볼 수 있고, 포커스를 가지고 있는 상태.
다시 말하자면 입/출력이 가능한 상태.
Pause 상태
Active상태의 activity A가 화면의 foreground를 새로 점유한 activity N에게 포커스를 잃었지만 아직은 A의 일부가 보이는 상태 (foreground를 획득한 activity N이 화면 전체를 사용하지 않거나, 반투명하게 구현 되어 있을 때).
Stop 상태
Active/Pause상태의 activity A가 full-screen크기의 activity N에게 화면 foreground를 선점 당한 상태.
Killed (Dead) 상태
Activity A가 생성되지도 않았거나 생성 후 소멸된 상태.
2. Activity의 상태 변이와 callback 메소드
Activity의 상태변이도
위에서 설명한 4가지 Activity상태간 변이 시에 코드의 흐름을 나타낸 그림이다. (클릭하면 크게 보임)
참고. onRestoreInstanceState(Bundle)와 onSaveInstanceState(Bundle)메소드들은 life cycle 에 직접적으로 관여하는 메소드는 아니며 상황에 따라 호출이 생략될 수 있다. 다음 단락: Activity의 강제 종료와 복구에서 자세히 설명.
위의 그림에서 파란색 실선은 정상적인 Actvity의 흐름이며 붉은색 점선은 비정상적인 Activity의 흐름이다.
위 그림을 이용하면 수 많은 Activity의 상태변이 case들에서 어떤 callback들이 어떤 차례로 호출되는지 쉽게 볼 수 있다.
예를 들어, Activity가 처음 실행돼서 사용자에게 사용되다 정상 종료 된다면 다음과 같은 callback이 차례로 호출된다.
또, Running상태의 Activity A가 다른 Activity B에게 완전히 가렸다 A가 다시 foreground로 와서 사용자에게 사용되는 경우는 다음과 같은 callback이 차례로 호출된다.
마지막 예로, 화면의 일부가 가린 pause 상태의 Activity A가 system 자원(메모리)의 부족으로 system에 의해 강제로 종료 당했다가 system 자원의 여유가 생겨 다시 복구 될 때는 다음과 같은 callback이 차례로 호출된다.
Life Cycle 관련 Callback 메소드 분석
Activity의 상태변이 시 호출되는 callback들을 각각 자세히 설명하면 다음과 같다. (클릭하면 크게 보임)
한가지 중요한 것은 위의 callback을 오버라이딩 할 때는 다음과 같이 super클래스의 원래 callback을 먼저 호출하여야 한다.
...
3. Activity의 강제 종료와 복구
System에 의한 Activity의 강제 종료
스마트폰 플랫폼은 PC와 달리 제한된 리소스(대표적으로RAM)을 가지고 있기 때문에 여러 가지 작업을 동시에 진행할 때 리소스 부족현상에 직면할 수 있다.
이는 심각한 문제를 유발 할 수도 있는데, 한가지 예를 들어 보자. 예를 들어 사용자가 이용하고 있는 게임 어플리케이션의 Activity A가 foreground서 사용자와 interact하고 있다고 치자. 이때 전화가 걸려오면 시스템은 incoming call Activity B를 foreground에 띄워 사용자에게 전화가 왔음을 알려야 한다. 하지만 만약에 A가 시스템의 리소스를 거의 전부 점유하고 있는 상황이라면 B Activity를 띄우는 것이 불가능 할 수 있다.
스마트폰에서 게임을 하는 것 보다 전화를 받는 Task가 훨씬 중요한 것이 일반적임으로, 이 경우 디바이스의 효율적인 운영이 이뤄진다고 볼 수 없고 이는 곧 user의 불편함으로 이어진다.
그래서 안드로이드에서는 여러 개의 Activity를 운영 중 시스템 리소스가 부족하면 특정 상태에 있는 Activity를 강제로 종료 할 수 있게 디자인 되어있다. Activity의 상태에 따른 강제 종료 우선 순위는 다음과 같다.
Running 상태: 절대 강제 종료 되지 않음
Pause 상태: 강제 종료 2순위
Stop 상태: 강제 종료 1 순위
전 단락의 life cycle 관련 callback 테이블을 보면 강제 종료 칼럼에 yes라고 표기된 onPause(), onStop(), onDestroy()가 있는데 이들 callback이 호출 된 뒤에는 Activity가 언제든지 강제 종료 될 수 있다는 뜻이다.
한가지 중요한 것은 onPause() callback이 실행 중에는 Activity는 아직 Running 상태이고 onPause()가 리턴 하자마자 Activity는 Pause상태가 되기 때문에 onPause() callback은 항상 return이 보장되는 반면 나머지 2 개의 callback, onStop(), onDestroy()이 실행 될 때는 Activity는 각각 Pause상태, Stop상태이기 때문에 return이 보장되지 않는다.
이런 이유(onPause() 이후에는 Activity가 강제 종료 당할 수 있음)로 onPause() callback에서 사용자가 진행하던 작업을 저장 하는 등의 강제 종료에 대비한 작업을 해주어야 한다.
강제 종료된 Activity의 복구
강제 종료된 Activity는 가동하는데 충분한 resource가 확보되면 (예를 들면 Activity A를 강제 종료 시켰던 Activity B의 종료 등) 자동으로 복구되지만, 한가지 작업이 이루어 지지 않는다면 사용자에게 심각한 불편을 초래할 수 있다. 다음과 같은 시나리오를 가정해 보자.
위와 같은 불편을 막기 위해, 안드로이드의 Activity 클래스는 onSaveInstanceState(Bundle) 메소드와 onRestoreInstanceState(Bundle) callback 메소드를 제공한다.
우선 위의 Activity 상태변이도에서 본것과 같이,
onSaveInstanceState(Bundle)은 onPause()전에 호출되며 파라미터로 전달 받는 Bundle 인스턴스에 현재 activity의 상태를 저장하게 된다. 저장된 Bundle인스턴스는 시스템이 Activity를 자동으로 복원할 때 호출되는 onCreate(Bundle) -> onStart() -> onRestroreInstanceState(Bundle) -> onResume() callback 중 onCreate(…)와 onRestoreInstanceState(…) callback에 모두 파라미터로 전달 됨으로 양쪽 어디서건 사용해서 강제종료 되기 전의 상태로 Activity를 복구 시키면 된다.
한가지 참고할 사상은 onSaveInstanceState(Bundle)과 onRestroreInstanceState(Bundle)은 life cycle과 직접적으로 관련이 있는 callback이 아님으로 Activity의 상태 변화 시 항상 호출된다는 보장이 없다. 논리적으로 꼭 필요한 상황에서만 호출됨으로 Activity상태가 바뀔 때 예외 없이 호출 되어야 하는 루틴은 life cycle 관련 callback (onResume(), onPause() 등)에서 구현 하여야 한다.
예를 들면, 사용자가 Activity 를 디바이스의 BACK key 로 직접 종료하거나 Activity.finish() 메소드를 사용해 정상 종료되는 경우에는 Activity의 현 상태를 복구할 필요가 없기 때문에 onSaveInstanceState(Bundle) callback의 호출은 생략된다.
4. Activity 생명주기 예제 (Activity Life Cycle Demo)
어떠한 상황에서 Activity가 상태변이를 일으키는지 눈으로 직접 확인하기 위해 예제를 작성했지만, Activity의 강제 종료를 시뮬레이션 하기 위해서는 실제 메모리가 부족한 상황을 만들어야 하는데 이 부분은 추후 업데이트를 할 예정이다.
예제는 2개의 버튼을 가지고 있는 Main Activity 한 개와 sub Activity 2개로 이루어졌다.
Sub Activity중 하나는 Pause 상태를 재현하기 위한 반투명 배경의 Activity이며 (Pause 상태의 조건은 running 중인 activity가 반투명 Activity난 non-full screen Activity에 의해 일부가 가렸을 때 임을 상기), 두 번째 Activity는 Main Activity 의 Stop상태를 재현하기 위한 full screen창이다.
상태변이를 확인하기 위해서는 life cycle관련 callback이 호출될 때 마다 Logcat에 Log를 남기는 방법을 사용했다.
Running 상태 재현
Activity의 생성시를 보면 다음과 같은 callback들이 호출되는 것을 볼 수 있다.
Logcat Log 내용
Running 상태 -> Pause상태 -> Running 상태 재현
Running상태인 Main Activity를 Pause상태로 전환 시키기 위해 반투명 sub activity를 foreground로 불러오면 다음과 같은 순서로 callback들이 호출된다.
Logcat Log내용
눈 여겨 볼만한 점은 onSaveInstanceState(Bundle)의 호출이다. Pause상태의 main activity는 언제 강제 종료 당할지 모르기 때문에 onSaveInstanceState(Bundle)이 call 되고 있다.
다시 Main Activity로 돌아오기 위해 반투명 sub Activity를 종료하면 (finish() 호출) 다음과 같은 callback이 차례로 호출된다.
Logcat Log내용
Sub Activity를 우선 종료 후 Main Activity를 foreground로 되돌리는 것이 아니라 Sub Activity를 Pause상태로 만들고 Main Activity가 Sub Activity를 가리면 Sub Activity가 Stop상태로 전환 되고 소멸 된다.
Running 상태 -> Stop상태 -> Running 상태 재현
Running 상태인 Main Activity를 Stop상태로 전환 시키기 위해 full-screen size의 Activity를 main activity 앞으로 불러온다.
Logcat Log 내용
여기서도 Main Activity가 Pause상태에 들어가고 Sub Activity가 foreground를 점유하면서 Main Activity를 완전히 가리면 Main Activity가 Stop() 상태로 전환되는 것을 볼 수 있다.
Full screen sub Activity를 닫으면 (Activity.finish() 메소드 호출) main activity가 running 상태로 복귀 하는 것을 볼 수 있다.
Logcat Log 내용
Stop상태였던 Main Activity가 Running 상태로 전환되면서 onRestart()가 호출되는 것을 볼 수 있다.
또, Sub Activity는 Activity.finish() 메소드를 사용해 정상적으로 종료되는 상황이라 onPause()호출 전에 onSaveInstanceState(Bundle) 메소드가 생략된 것도 볼 수 있다.
출처 : http://tigerwoods.tistory.com/30
덧글]
개발하시다가 온몸이 찌뿌둥하시면 아래 동영상을 따라 스트레칭을 한번하세요.
1. 목디스크 예방을 위한 목운동 ☞ http://jwandroid.tistory.com/192
2. 손목터널증후군 손목스트레칭으로 예방합시다. ☞ http://jwandroid.tistory.com/193
3. 개발자 여러분 허리를 세우세요 - 척추체조 1번 ☞ http://jwandroid.tistory.com/194
4. 개발자 여러분 허리를 세우세요 - 척추체조 2번 ☞ http://jwandroid.tistory.com/195
5. 개발자 여러분 허리를 세우세요 - 척추체조 3번 ☞ http://jwandroid.tistory.com/196
6. 개발자 여러분 허리를 세우세요 - 척추체조 4번 ☞ http://jwandroid.tistory.com/197
'프로그래밍 > 안드로이드' 카테고리의 다른 글
[안드로이드 팁] 안드로이드 ListView 이벤트 추가 팁 (0) | 2012.04.30 |
---|---|
[안드로이드/Android] 안드로이드 개발환경 구축 (0) | 2012.04.30 |
[안드로이드/Android] 안드로이드 기본 개요 (0) | 2012.04.30 |
[안드로이드/Android] 안드로이드 인텐트 - 1 (0) | 2012.04.30 |
[안드로이드/Android] 안드로이드 Hello Android 작성 (0) | 2012.04.30 |
[안드로이드/Android] 안드로이드 기본 구성 요소 (0) | 2012.04.30 |
[안드로이드 팁] ADB "Unable to open sync connection!" 에러 메시지 (0) | 2012.04.26 |
UncaughtExceptionHandler에 대한 정리 (0) | 2012.04.26 |
[Android] ADB 이용해서 패킷 덤프 뜨는 방법 [스크랩 자료] (0) | 2012.02.01 |