728x90

어플 기능을 추가하면서 메모리 누수 문제로 고생을 좀 했다.

그냥 무심코 넘긴 Activity 생명주기가 얼마나 중요한지에 대해 새삼 알게되었다.


Activity 생명주기

안드로이드 생명주기(수명주기)란 Activity 의 상태정보가 변하는 것이다.

안드로이드 응용 프로그램은 PC용과 달리 화면이 작으므로 동시에 여러개의 Activity(화면)이 나올 수 없다.

화면 하나만 활성화된 상태이고, 나머지는 모두 비활성화된 상태로 남게 된다.


- 응용 프로그램이 시작되면 onCreate(), onStart(), onResume()가 차례로 수행되고, MainActivity 화면이 나온다.


  홈 버튼을 눌러서 Activity를 백그라운드로 보내버리거나, NewActivity가 실행되어 화면 전환이 될 때

  onPause()  --> onStop() 이 두 메소드가 연달아 호출되며 백그라운드 스택으로 내려간다.

 

- NewActivity 를 요청하면 오른쪽 방향의 onPause(), onStop()이 수행되고, MainActivity는 중지되며 NewActivity 화면이 나온다.

- New Activity 의 사용을 종료하면 onRestart(), onStart(), onResume()가 수행되고 다시 MainActivity 화면이 나온다.


- MainActivity 를 끝내면 아래 방향의 onPause(), onStop(), onDestroy()가 차례로 수행되고 응응 프로그램이 종료된다.


상태 메소드

 설명

 onCreate()

 - Activity 가 처음 만들어졌을 때 호출됨

 - 화면에 보이는 뷰들의 일반적인 상태를 설정하는 부분

 - 이전 상태가 저장되어 있는 경우에는 번들 객체를 참조하여 이전 상태 회복 가능

 - 이 메소드 다음에는 항상 onStart() 메소드가 호출됨

 onStart()

 - Activity가 화면에 보이기 바로 전에 호출됨

 - Activity가 화면상에 보이면 이 메소드 다음에 onResume() 메소드가 호출됨

 - Activity가 화면에서 가려지게 되면 이 메소드 다음에 onStop() 메소드가 호출됨

 onResume()

 - Activity가 사용자와 상호작용하기 바로 전에 호출됨

 onRestart()

 - Activity가 중지된 이후에 호출되는 메소드로 다시 시작하기 바로 전에 호출됨

 - 이 메소드 다음에는 항상 onStart() 메소드가 호출됨

 onPause()

 - 또다른 Activity를 시작하려고 할 때 호출됨

 - 저장되지 않은 데이터를 저장소에 저장하거나 애니메이션 중인 작업을 중지하는 등의

   기능을 수행하는 메소드임

 - 이 메소드가 리턴하기 전에는 다음 Activity가 시작될 수 없으므로 이 작업은 매우 빨리

   수행된 후 리턴되어야 함.

 - Activity가 이 상태에 들어가면 시스템은 Activity를 강제 종료할 수 있음

 onStop()

 - Activity가 사용자에게 더 이상 보이지 않을 때 소멸됨
 -
Activity가 소멸되거나 또다른 Activity가 화면을 가릴 때 호출됨
 -
Activity가 이 상태에 들어가면 시스템은 Activity를 강제 종료할 수 있음

 onDestroy()

 - Activity가 소멸되어 없어지기 전에 호출됨

 - 이 메소드는 Activity가 받는 마지막 호출이 됨

 - Activity가 애플리케이션에 의해 종료(finish)되거나 시스템이 강제로 종료시키는 경우에

   호출될 수 있음.

 - 위의 두가지 경우를 구분할 때 isFinishing() 메소드를 이용함

 - Activity가 이 상태에 들어가면 시스템은 Activity를 강제 종료할 수 있음.



Activity stack 처리하는 방법

Activity를 그냥 intent생성해서 만들면 계속해서 새로 Activity를 start하고 스택으로 쌓이는 문제가 발생한다.
예를 들어서 A, B, C Activity를 A->B->C->A->C->B로 이동하고 뒤로가기를 누르면 다시 화면이 B->C->A->C->B->A 순으로 왔던 순서 반대로 나타난다.
문제점 검토
1. 뜨면 안될 화면도 뜨게 된다.(접근해선 안되는 이전화면으로 돌아가버린다)
2. 그냥 다시 뒤로가면 되는 화면인데 새로운 화면을 만들어서 그 위에 쌓아나간다.(이전 화면의 Activity를 새로 생성해서 위에 쌓는다)

해결방법
1. 접근해선 안되는 이전화면으로 돌아가버리는 문제
   해당 Activity 의 JAVA 소스 파일에서 intent를 생성한 바로 다음에
   intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); //기존에 쌓여있던 스택을 모두 없앤다.
   intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // task를 새로 생성한다
   를 추가해준다.

2. 이전 화면의 Activity로 돌아가야하는 문제
   intent.addFlags(intent.FLAG_ACTIVITY_CLEAR_TOP); // 현재 Activity 없애고 이전 화면을 새로운 화면으로 지정


3. 어플 실행도중 전화가 왔을 때의 처리

    onResume() 와 onPause() 에서 관련 코드 추가

    @Override
    public void onResume() {
        super.onResume();  // Always call the superclass method first
        if(mediaPlayer != null && mediaPlayer.isPlaying() == false){
            mediaPlayer.start();
        }
        // 전화통화가 끝나면 멈춤상태 음악이 다시 플레이된다.
    }

    @Override
    public void onPause() {
        super.onPause();  // Always call the superclass method first
        // 홈버튼을 누르면 백그라운드로 들어가면서 재생되는 음악이 멈춘다.
        // 전화를 받으면 재생되던 음악이 자동으로 멈춘다.
        if(mediaPlayer != null && mediaPlayer.isPlaying() == true){
            mediaPlayer.pause();
        }
    } 



※ 참고로 Activity 생성시에 사용되는 Intent Flag 정리는 아래 블로그에 잘 정리되어 있다.

    http://theeye.pe.kr/archives/1298


    http://androidhuman.com/260 Activity 생애주기 샘플코드 및 설명



아래 사항은 여기에 추가하는 것이 적절한 거 같아서 콘솔 앱을 만들면서 적용한 걸 적어둔다. (Updated 2019.5.17)


서비스에서 Activity를 띄울 때는 인텐트에 플래그를 주어야 하며, 메인 Activity가 이미 메모리에 만들어져 있는 경우에는 메인 Activity의 onNewIntent() 메서드로 데이터가 전달된다.


@Override
protected void onNewIntent(Intent intent) {
    // If the activity has already been created/instantiated, the event will arrive through the 'onNewIntent()' method
    super.onNewIntent(intent);
    connectUsb();
    LayoutMode();


Intent intent = new Intent(Setting.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);

FLAG_ACTIVITY_SINGLE_TOP 은 Activity를 재사용 하기 위해서, 필요없는 Activity를 제거하기 위해서 FLAG_ACTIVITY_CLEAR_TOP 을 같이 조합하였다.
이런경우 MainActivity 에서는 onNewIntent()를 override 해서 intent를 받아야 한다.
호출되는 순서는 onCreate() 대신에 onNewIntent()가 호출되고 다음으로 onResume() 이 호출된다.

처음 startActivity 호출시
1. onCreate  → 2. onStart
3. onResume

해당 Activity가 실행된 상태에서  FLAG를 추가하고 다시 startActivity 호출시
1. onNewIntent
2. onResume

블로그 이미지

Link2Me

,