Programming/Android2013. 10. 1. 23:19

액티비티(Activities)

참조: http://developer.android.com/guide/components/activities.html


액티비티 명세(Declaring the activity in the manifest)

기본적으로 manifest파일에 <application> 태그 안에 <activity> 태그로 생성된다. attribute 로 android:name만 설정하면 되지만 한번 앱이 실행되면 이름을 바꿀수 없다.


인텐드 필터의 사용(Using intent filters)

<activity>는 <intent-filter> 태그를 사용하여 명시될 수 있다. 이는 다른 앱의 컴포넌트가 이 액티비티를 활성화할 수 있는 선언을 해 준다.


만약 다른 앱에서 이 액티비티를 실행시키기를 원하지 않으면 메인을 설정하는 <intent-filter>를 제외한 나머지 필터를 설정하지 않으면 된다.

- 인턴트 필터에 대한 더 많은 정보는 다음을 참조: http://developer.android.com/guide/components/intents-filters.html


액티비티의 시작(Starting an Activity)

다른 액티비티에서 startActivity() 와 Intent 객체를 인자로 원하는 액티비티를 실행시킬 수 있다. 그리고 인텐트는 정확하게 실행시키고싶은 액티비티를 설정할 수도있고 원하는 타입의 액티비티를 설정할 수도 있다(이때 시스템은 다른 액에서 적당한 액티비티를 선택한다). 그리고 인텐트는 적은양의 데이터도 옮길 수 있다.


그러니까 

명시적 인텐트가 있고(인텐트 객체 생성시 인자로 실행시킬 액티비티 클래스 설정)

암시적 인텐트가 있다(인텐트 객체 생성시 인자로 원하는 동작 타입과 그게 맞는 데이터만 넘김).

암시적 인텐트에 가치가 있는데 이는 다른 앱에서 원하는 액티비티를 불러낼 수 있고 만약 이런 앱이 많다면 사용자가 고를 수 있다. 예를 들어 이메일을 보내는 다른 앱의 액티비티를 불러내 보낼 당사자의 이메일 정보를 같이 이메일 앱으로 보낼 수 있다.


결과를 위한 액티비티의 시작(Starting an activity for a result)

startActivity()메소드 대신에 startActivityForResult()라는 메소드(인자: Intent intent, int requestCode)를 실행시킬 수 있다. 이 경우 다음 실행된 액티비티가 실행이 맞췄을 때 onActivityResult() 콜백 메소드로 인자 int requestCode, int resultCode, Intent data를 받을 수 있다. resultCode로 응답의 유무를 알 수 있고 requestCode로 startActivityForResult()메소드와 시퀀스를 맞추고 intent에서 데이터를 추출할 수있다.


액티비티 끄기(Shutting Down an Activity)

하나의 액티비티를 소멸하기 위해 finish() 메소드를 사용할 수 있고 finishActivity() 메소드를 사용하여 각각의 액티비티를 소멸할 수 있다. 하지만 굳이 액티비티를 액티비티 라이프싸이클에 의해 소멸시킬 필요가 없기 때문에 만약 필요하면 위의 메소드를 좀더 찾아 보길.


액티비티 라이프싸이클(Managing the Activity Lifecycle)

이전 포스팅에서 액티비티 라이프 싸이클에 대해 충분히 설명이 되었고 하나 추가하자면 onCreate(), on Start() … onDestroy()까지 모든 오버라이드하는 메소드는 super.onCreate() 같이 상위 클래스의 메소드를 첫줄에 추가해 줘야한다.



그리고 시스템이 스스로 액티비티를 죽일 수 있는 순간은 onPause(), onStop(), onDestroy() 가 호출 된 이후에만이다. 그래서 onPause()메소드가 실행되는 시점부터 자동으로 액티비티가 죽을 수 있으니 모든 중요 데이터는 onPause() 단계에서 중간 저장하고 가라 하지만 또 너무 많이 저장하면 매번 실행되기 때문에 시스템을 느리게 할 수 있는 요지가있기 때문에 선별하여 저장하는게 좋다.


액티비티 상태 저장(Saving activity state)

번들로 액티비티 상태를 저장하는 것은 이 부분역시 이전 포스팅에서 설명했다. 저장하는 onSaveInstanceState()와 복원하는 onRestoreInstanceState()는 클래스를 오버라이드한 후 바로 슈퍼클래스의 각각의 메소드를 호출해 주어야 한다. (만약 이런 상태 저장 자체를 원하지 않는다면 android:saveEnable 애트리뷰트에 false로 저장하거나 setSaveEnabled() 메소드를 호출하여 설정할 수 있다.) 그리고 간단하게 이걸 확인하는 방법은 기기를 로테이션 시켜서 가로모두로 바꾼하 돌아와보면 그 결과를 쉽게 알 수 있다.



컨피그레이션 변경 핸들링(Handling configuration changes)

스크린 로테이션, 키보드 보이기 나 언어 설정 등의 기기 컴피그레이션이 런타임동안 변할수 있다. 이러한 변경이 이루어질 때 시스템은 액티비티를 재생성한다(onDestory()->onCreate()). 그래서 가장 좋은 핸들링 방법은 위의 액티비티 상태를 저장하고 복귀하는 것이다. 


- 컴피그레이션 변경에 대한 더 많은 정보는 다음을 참조: http://developer.android.com/guide/topics/resources/runtime-changes.html


코디네이팅 액티비티(Coordinating activities)

액티비티A가 다른 액티비티B를 불러낼때 

1. 액티비티 A의 onPause() 메소드가 실행되고

2. 액티비티 B의 on Create(), onStart(), onResume()의 세게의 메소드가 순서대로 실행된다.

3. 그리고 마지막으로 액티비티 B가 화면에 보이면 액티비티 A의 onStop() 메소드 실행된다.


그러니까 액티비티 B가 실행되기 전 마지막 액티비티 A의 실행은 onPause() 때문에 액티비티 A에서 데이터 베이스 저장 후 액티비티 B에서 그 데이터를 쓰려면 액티비티 A의 onPause()에서 저장 시켜야 한다는 것이다 onStop() 에서가 아니라.

Posted by Brian B. Lee
Programming/Android2013. 9. 30. 22:04

Managing the Activity Lifecycle (4/4)


액티비티 다시 생성(Recreating an Activity)

참조: http://developer.android.com/training/basics/activity-lifecycle/recreating.html


백버튼이 눌려지거나 finish() 메소드가 호출되는 액티비티가 소멸되는 몇몇 시나리오가 있다. 또 시스템에서 화면 전면의 액티비티가 많은 메모리를 필요로 하거나 하는 경우 액티비티를 소멸시킬 수 있다.


하지만 이렇게 시스템이 액티비티 객체를 소멸시켰을 때 사용자가 백버튼을 이용하여 다시 그 액티비티로 복기하려 할 때 소멸된 액티비티를 다시 불러올 수 있다. 이는 그 액티비티를 인스턴스화 하고 번들 객체에 key-value 짝으로 저장되어 있는 데이터를 가져온다. 이렇게 저장된 데이터를 인스턴스 상태(instance state)라고 한다.


기본적으로 시스템은 번들 인스턴스로 액티비티에 레이아웃에있는 View 객체 같은 것을 저장한다. 그래서 액티비티 인스턴스가 소멸되고 재생성될때도 그전의 상황을 재현할 수 있다. 하지만 이런 다시 재생성될때 같이 돌아오고 싶은 더 많은 정보들이 있다.(멤버 변수 같은)


액티비티 상태 저장(Save Your Activity State)

만약 더 많은 정보를 저장하려면 onSaveInstanceState() 콜백 메소드를 오버라이드 해야한다. 이때 Bundle 객체를 인자로 받고 이곳에 저장하고 싶은것을 Key-Value로 저장하면 된다. 이렇게 예상 밖에 액티비티가 소멸됐다가 나중에 다시 재생성될때 onRestoreInstanceState()와 onCreate() 두 메소드에 인자로 Bunde 객체를 보내 이곳에서 위에서 저장한 정보를 불러올 수 있다.

한마디로 무조건 일어버리면 안돼는 정보는 Bundle에 저장해라.(물론 View의 스크롤 포지션이나 에디트 텍스트의 문장 같은 자동으로 저장되는 것이 있다.)


액티비티 상태 다시 불러오기(Restore Your Activity State)

위에서 Bundle객체를 가지고 오는 법이 onCreate()와 onRestoreInstanceState() 두가지 방법이 있다고 했는데 onCreate() 의 경우 Bundle객체가 null일 수 있기 때문에 if 문으로 null 체크를 해주어야 하지만 onRestoreInstanceState()의 경우 번들 객체가 있을 때만 메소드를 콜백하기 때문에 null 체크를 해줄 필요가 없다.

Posted by Brian B. Lee
Programming/Android2013. 9. 30. 21:03

Managing the Activity Lifecycle (3/4)


액티비티의 멈춤과 재시작(Stopping and Restarting an Activity)

참조: http://developer.android.com/training/basics/activity-lifecycle/stopping.html


- 사용자가 앱 사용중 화면 전면에서 사라진후 다시 홈 스크린에서 이 앱을 실행시키거나 대체된 앱에서 다시 본 앱으로 돌아왔을 때, 앱티비티가 다시 실행된다.

- 액티비티에서 다른 액티비티를 실행시켰을때 그 전 액티비티는 멈춰진다. 만약 사용자가 뒤로가기 버튼을 눌러도 첫번째 액티비티는 다시 시작된다.

- 앱을 사용할때 전화를 받았을 때도 액티비티는 멈춰진다.


액티비티는 멈춤과 재시작을 위해 onStop()과 onRestart() 메소드를 제공한다. 이는 Paused 상태와 다르게 화면에서 액티비티가 완전히 사라진다.


Note. 시스템이 스톱될때 시스템 메모리안에 액티비티 인스턴스를 저장하기 때문에 onStop(), onRestart() 심지어 onStart() 메소드를 구현할 필요가 없을 수 있다. 그래서 심플한 관계를 가지고 있는 액티비티간의 동작은 onPause() 메소드를 이용한 시스템 리소스를 컨트롤 하는 작업만이 필요할 수 도있다.


액티비티 정지(Stop Your Activity)

스톱이 되었을때 더이상 사용자에게 보이지 않기 때문에 대부분의 리소스를 릴리즈 해야한다. 만약 시스템 메모리 되찾기가 필요하면 시스템은 이 인스턴스를 재거되거나 심지어 필요에따라 아무 알람없이 onDestory() 콜백을 호출하여 종료시킬 수 있다. 그래서 메모리 누수를 막기위해 onStop() 메소드에서 리소스를 릴리즈 시켜줘야 한다.


액티비티가 스탑될 때, 액티비티 객체는 메모리에 상주 데이터로 남게 되고 액티비티 재개에서 리콜되어 진다. 그래서 거의 대부분의 경우 재초기화의 필요성이 없다.


액티비티의 시작과 재시작(Start/Restart Your Activity)

액티비티가 화면에 전면에 다시 나타날때 onRestrt()메소드가 실행된다. 그리고 바로 시스템은 onStart() 메소드를 호출하고 이 메소드는 액티비티가 화면에 보일때 마다 이루어진다. 하지만 onRestart() 메소드는 Stopped 상황에서 resume 될때만 호출된다.


보통 onRestart() 메소드는 사용되지 않고 특정 상황에서 리소스를 릴리즈 하기위해서만 사용된다. 그리고 보통은 onStart()와 onStop()를 한쌍으로 본다.


결국 정리하면 

onStart()는 

1. 처음 앱이 시작할 때 

2. 액티비티가 stopped 됐다 Resumed 로 돌아갈때

이렇게 두가지 경우에서 실행되고


onResume()는 

1. 처음 앱이 시작할 때 

2. Pused 됐다 돌아올 때 

3. stopped 됐다 돌아갈 때

와 같이 액티비티가 화면의 전면에 나타날때는 모두 실행된다.


onRestart()는 

1. Stopped에서 Resumed로 갈때 한번만 실행된다.


onPause()는

1. Pauee() 만 될때 (전화가 오거나 알람이 울어 포커스를 잃었을 때)

2. Stopped 상태가 되는 과정에 실행

3. Destroyed 상태가 될는 과정에 실행

그러니까 Resumed 상황에서 변경되면 모두 발생


onStop()는

1. Stopped 상태로 변할 때

2. Destroyed 상태로 변할 때


onDestroy()

1. Destroyed 상태로 변할 때 한번


상태가 변하면서 거쳐가는 과정이 있기 때문에 중간 중간 메모리 관리하면 된다. 그리고 결국 액티브 인스턴스가 Destroyed 상태로 변하기 전까지 메모리로 보관대기 때문에 크게 객체를 따로 보관할 필요는 없지만 객체에서 생성한 스레드 같은 것은 잘 관리 해주어야 그렇지 않으면 시스템에서 예고 없이 앱을 종료시킬 수 있다.


다음에는 Managing The Activity Lifecycle 의 마지막으로 위와 같이 잘 작동하다 시스템이 맘대로 지가 메모리가 모잘라 Stopeed 상태인 내 앱을 죽여버렸을 때 어떻게 데이터를 유지할 지 보겠다.

Posted by Brian B. Lee
Programming/Android2013. 9. 29. 22:08

Managing the Activity Lifecycle (2/4)


액티비티의 멈춤과 재개(Pausing and Resuming an Activity)

참조: http://developer.android.com/training/basics/activity-lifecycle/pausing.html#Resume


앱이 실행되는 동안 다른 비주얼 컨포넌트로 가리워 질 수 있다 이때  Puse 동작이 이루어 진다. 이때 반투명 액티비티가 실행되거나 부분적으로 액티비티가 가리워 진다. 


하지만 모두 가려졌을 때 이것은 stoped 상태가 된다.


처음 액티비티가 Paused 상태가 되면 onPause() 메서드를 실행시킨다. 이때 모든 실행은 더이상 진행할 수 없게 되고 사용자 진행을 위해 어느 정보들은  저장된다. 그리고 다시 사용자가 이 액티비티로 돌아올 때 onResume() 메소드가 실행 된다.


액티비티 중지 (Pause Your Activity)

- 애니메이션을 중지하거나 CPU를 소모하는 동작들을 중지

- 오직 사용자가 변화를 저장되어지길 기대하는경우에만 저장되지 않은 변화를 관리한다. (이메일 드레프트 같은)

- 사용자가 필요로 하지 않은 브로드케스트 리씨버나 센서(GPS같은) 사용같은 베터리 라이프에 영향을 미치는 시스템 자원들을 릴리즈한다.


보통 onPause() 메소드를 사용하여 연구 보관 저장장치에 사용자 변화(개인 정보가 입력되었을 때)를 저장하지 않는다. 하지만 특정 정보(이메일 드레프트 같은)의 경우 사용자 기대에 의해 저장되어 질 수 있다. 물론 CPU에 부화를 주는 데이터베이스 쓰기 작업 같은 경우 다음 액티비티의 트렌젝션 화면이 느려질 수 있다 이런경우를 피해야겠다.


Note: 액티비티가 멈출때 액티비티 인스턴스의 메모리 정보는 지켜지면 재개될때 리콜된다.  그래서 다시 컨포넌트를 재초기화 할 필요는 없다.



액티비티 재개(Resume Your Activity)

액티비티가 Pause 되었다 다시 재개되면 onResume() 메소드를 호출 한다.


처음 액티비티가 생성될때를 포함해 액티비티가 전면에 실행 될때 마다 이 메소드는 호출 된다는 점을 인지해야한다. 그래서 onPause(), onResume() 메소드 호출 시 상황에 맞는 초기화를 잘 해주어야 한다. 


onResume()과 onPause() 메소드는 한 쌍이기 때문에 onPause()에서 camera.release() 되면 onResume()의 경우 initializeCamera()해주는 것과 같이 관리 되어야한다. 

Posted by Brian B. Lee
Programming/Android2013. 9. 29. 21:17

Managing the Activity Lifecycle (1/4)


액티비티의 시작(Starting an Activity)

참조: http://developer.android.com/training/basics/activity-lifecycle/starting.html


main() 메서드로 시작하는 다른 프로그램과 달리 안드로이드는 Activity의 인스턴스의 콜백으로 시스템이 시작한다. 

참고: 콜백? vs 호출?

콜백함수와 함수 호출의 차이는 제어권에 있다

호출자가 -> 피호출자 호출(일반적인 함수 흐름)

피호출자 -> 호출자 호출(콜백의 함수 흐름)

즉, 안드로이드에서 콜백함수를 실행시켜줘야 앱이 동작한다는 거다.


라이프사이클 콜백의 이해(Understand the Lifecycle Callbacks)



그림 1. Lifecycle Callback Status


액티비티의 복잡성에따라 모든 인스턴스를 라이프사이클을 정의해 줄 필요는 없지만 사용자의 기대치에는 부합해야한다. 라이프 사이클의 메서드를 정의함으로써 다음과 같은 올바른 행동을 정의할 수 있을것이다.

- 앱을 사용할 때 전화를 받거나 다른 앱으로 변경시 액의 문제를 방지할 수 있다.

- 사용자가 앱을 상요하지 않을 때, 시스템 자원을 소비를 피할 수 있다.

- 앱에서 빠져나가거나 앱의 마지막 상황으로 돌아갈 때, 사용자의 진행사항을 일지 않을 수 있다. 

- 화면의 변경에서 사용자의 진행사항이나 문제를 피할 수 있다.


그림 1.에서 다른 많은 상황들이 있지만 정적인 상황은 결국 세가지 밖에 없다.

1. 재점유된(Resumed): 액티비티가 화면 전경에 보이고 사용자가 사용할 수 있다. (running이라고도 한다)

2. 중지된(Paused): 액티비티가 다른 액티비티로부터 숨김을 당했을 때이다 - 화면의 전경에 있는 다른 앱이 반투명이거나 전체화면을 모두 점유하지 못할 때이다. 이 상황에서는 사용자의 입력을 받을 수 없고 어느 코드도 실행될 수 없다.

3. 멈춘(Stopped): 액티비티가 완전히 가려졌거나 화면에 전혀 보이지 않을 때이다. 백그라운에서 돌아가게 되고 이 멈춤 중에는 액티비티의 인스턴스와 모든 멤버 변수같은 상황 정보 보유되나 코드를 실행시킬 수는 없다.


앱 런처 액티비티의 명세(Specify Your App's Launcher Activity)

홈 스크린에서 앱 아이콘을 실행하면 런처(or 메인)로 앱티비티로 지정된 액티비티의 onCreate() 메소드가 실행시된다.(메니페스트 파일에 명시)


새로운 인스턴스 생성(Create a New Instance)

액티비티가 실행되면 새로운 Activity 인스턴스의 onCreate() 메소드를 실행시키고 사용자 인터페이스(XML 레이아웃 파일)를 정의하거나 맴버 변수 정의 혹은 UI의 환경설정 등을 할 수 있다.

onCreate() 메소드가 실행되고 바로 onStart() 메소드가 실행되어 화면이 보이게 되지만 또 바로 onResume()로를 호출되어 Resumed 상황이 되기 때문에 다른 상황 변화에 대해서 Started 상황이 컨트롤 할 필요는 없다.


액티비티 소멸(Destroy the Activity)

대부분의 경우 onDestroy() 메소드를 사용할 일이 없지만(자동으로 메모리 관리) 만약 액티비티가 onCreate() 메소드에서 백그라운드 스레드를 만들거나 롱런 자원을 가지고 있다면 이 자원들이 메모리 누수를 일이킬 수 있음으로 onDestroy() 메소드를 통해 이 자원들을 닫아줘야 한다.


그림 1에서 와 같이 onDestory() 메서드 호출 전 onPause(), onStop()가 먼저 이루어진다. 하지만 예외가 하나 이다. onCreate() 메소드에서 finish() 메서드를 콜했을 때이다. 일시적 결정권자로써 액티비티가 다른 액티비티를 실행시켰을 때 finish()를 호출할 수 있는데 이는 중간 과정 없이 onDestory() 메서드를 호출 한다.

Posted by Brian B. Lee