IT_Programming/Android_Java

[펌_Android] 어플 종료에 대한 실험 + @

JJun ™ 2012. 3. 19. 18:59



 출처

 : http://linkedlist.tistory.com/entry/Android%EC%96%B4%ED%94%8C%EC%9D%84-%EC%99%84%EC%A0%84-%EC%A2%85%EB%A3%8C%EC%8B%9C%EC%BC%9C%EB%B4%85%EC%8B%9C%EB%8B%A4-%EC%95%BD%EA%B0%84%EC%9D%98-%EC%8B%A4%ED%97%98




개발을하다 액티비티(Activity)를 종료하고 이전 액티비티(Activity)로 전환시킬 때..
혹은 액티비티(Activity)에서 홈화면으로 전환할때. 
문득 finish()로만 종료해도 안전한건가..? 다른 방법도 많은데...? 더 안전한 방법은 없을까?
라고 말이죠.. 실제로 "안전"하다는 기준은 개발자마다 다르겠지만.. 여튼 저는 finish()를 쓰곤 합니다.

종료하도록 유도하는 명령은 3개정도라고 할 수 있겠습니다.(물론 다른것도 많겠지만 대표적으로 3개 정도입니다.)


finish();
moveTaskToBack(true);
android.os.Process.killProcess(android.os.Precess.myPid());




이 중 어떤것을 쓰면 좋을까?! 라는 생각은 시간낭비입니다. 그냥 아무거나 쓰세요~~
하지만 각 명령들은 약간씩 차이가 있다는것!

그 전에 이 글을 쓰도록 유도해준 글이 있습니다.
(이거슨 즉 출처를 밝힌다는 의도! 그리고 글쓴님들의 말씀을 약간 인용했다는 사실도 밝혀드립니다. 문제가 되면 삭제하겠습니다.)

안드로이드펍(포스코님) : http://www.androidpub.com/index.php?mid=android_dev_info&page=9&document_srl=1552061 

아이군의 블로그(아이님) : http://theeye.pe.kr/entry/how-to-go-background-using-moveTaskToBack-or-Intent-on-android-sdk 

위의 글과 내용이 거의 흡사할 것입니다. 하지만 저는 + @ 로 실험을 한가지 더 해보았지요~

다시 본론으로 넘어가서 각 메쏘드들은 제각각의 의미가 있었습니다.
그 중 

- moveTaskToBack(boolean nonRoot)

  인자로 boolean을 받고 결과값을 boolean형으로 반환합니다. 이 메서드는 현재의 액티비티가 속해있는 테스크를 백그라운드로 즉시 이동시킵니다. 
  정상적으로 이동이 되었다면 true가 반환이 되고 이동에 실패할경우 false가 반환됩니다. 하나의 인자를 받는데 true를 입력할 경우

  어떠한 경우라도 상관없이 백그라운드로 이동을 시킵니다. 하지만 false일 경우 현재의 액티비티가 루트(root)일 경우에만 백그라운드로 이동시킵니다.

  "액티비티가 root 다" 라는 것은 가장 첫번째(바닥)의 엑티비티(Activity)임을 뜻합니다.

- finish()

  (정확하게 조사해보고 알려드리는 것이 아니고 직접 사용해본 결과를 말씀드립니다.)
  finish()를 호출했을 때 현재 액티비티(Activity)만 종료하는것 같습니다. 즉, 위에서 말씀드린 root 액티비티에서 finish()를 호출 할 경우

  더이상 표시할 액티비티가 없으므로 어플리케이션을 종료하고 어플을 실행하기 전 화면으로 전환되는것 같습니다.

- android.os.Process.killProcess(android.os.Process.myPid())
   

   제가 조사해본 바로는 이 메소드로 어플종료를 유도했을 시 어플이 완전히 죽지 않는다겁니다.
   물론
실행되고 있는 어플이 액티비티가 한 개 라면 정상적으로 종료됩니다. 하지만 액티비티가 두 개 이상이고 root 액티비티가 아닌
   다른 액티비티에서 해당 메소드를 호출
하여 종료하면 프로세스 자체는 죽지 않습니다. (어떻게 보면 finish()와 비슷하기도 합니다..)
   (다른 서적에 명확한 정의가 있겠지만 저는 실험을 통해 알게된 사실을 말씀드립니다)



"포스코"님의 실험은 각 메소드를 조합해서 써보는 것이었습니다.
아래를 보시면 "포스코"님이 작성하신글을 토대로 적었습니다.




1번  moveTaskToBack(true);
2번  finish();
3번  android.os.Process.killProcess(android.os.Process.myPid());

1~3번까지 번호를 붙였습니다. 목표는 완전한 프로세스 종료입니다.
3가지를 조합하여 코드를 작성했을 경우 ANR 에러가 뜨는 경우가 생긴다고 합니다.
그래서 미리 Thread 문제라고 생각했지만 현재 상황과는 관련이 없었습니다.
Thread.setDaemon(true) 이 문제가 있다고 했지만 역시 관련이 없는 문제였습니다.

그래서 프로세스 종료 메소드를 조합해봤습니다.

첫번째    case ->   2번 + 3번         = 정상
두번째    case ->   2번                 = 정상
세번째    case ->   3번                 = 정상
네번째    case ->   1번 + 3번         = 오류
다섯번째 case ->   1번 + 2번 + 3번 = 오류
여섯번째 case ->   1번 + 2번         = 정상

[결론 추측]

만약 finish()로 어플이 종료 되었을 때 사용자들은 프로그램이 완전히 종료되지 않은것으로 오해합니다.
따라서 moveToBack(true)를 주의해서 사용해야할 것입니다.




여기까지 "포스코"님의 내용을 조금 수정하여 적었습니다.
하지만 제가 생각할때는 조금 석연치 않은 실험내용이었습니다. (제 생각이 잘못되었을수도.....)
어떤 상황에서 실험을 했는지를 모르겠다는거죠.. 예를들어 현재 액티비티가 1개인 경우2개 이상 쌓여있는 경우를 두고 실험했다면
결과가 다를수도 있지 않을까..라는 것이 제 생각입니다.(물론 저도 실험방법 절차 등이 체계적이지 않아서 모자란 부분이 많습니다.)


거기에다 +@로 메소드를 호출하여 종료할 때 어떤 메소드가 onDestroy()를 호출하고 종료하는 것인지도 알아보고 싶어졌습니다.

앞으로의 내용은 제가 직접 실험해보고 포스팅하는 것임을 말씀드립니다.
(물론 정확하지 않을 수 있고 명확하지 않을 수 있다는 것에 유념해주시면 감사하겠습니다.)


크게 2가지를 실험해볼겁니다.

1. "어떤 메소드가 onDestroy() 를 호출할까? 액티비티 개수에 따라서 결과는?" 
2. "포스코 님의 실험을 그대로 따라해보되, 액티비티의 개수를 늘려가며 결과를 도출해볼까?"

입니다.

첫 번째 실험은 개인적으로 알아보고 싶은 부분이었습니다.
(어디서 듣기론 3개 중에 onDestory()를 호출하지 않고 종료시키는 메소드가 있다고 들었어요)
두 번째 실험은 "포스코"님의 실험을 조금 더 다양한 상황에서 입증해보기 위한 실험입니다.

실험도구는 이렇습니다.

- 개발환경
   Eclipse SDK 3.7.1
   jdk1.7.0_02
   Android 2.2

- AVD Spec
  Target     : Android 2.2 - API Level 8
  CPU/ABI  : ARM(armeabi)
  SD Card   : Size 64 MiB
  Skin        : WQVGA400
  Hardware : Abstracted LCD density (Value 120)
                   Max VM application  heap size (Value 24) 

본 환경으로 실험에 임했습니다. 참고해주시고..


실험 첫 번째 CASE ( onDestroy() 호출 )


첫 번째 _ 세부실험 1

조건 : 액티비티가 1개일 경우

실험과정   
    1. 기본 프로젝트 > 메인 Activity > 
onCreate(...)에서 종료 메소드 호출

    2. Override 된 onDestroy() 메소드 안에 Log.e() 를 통해 결과 확인
    3. 상황에 따라 알게된 결과 보고
 

실험결과  
  1) finish()

      onDestroy()를 마지막에 호출하고 종료하는 것을 확인했습니다.
      하지만 어플리케이션 관리에 들어가보니 완전히 종료되지 않았습니다.

  2) moveTaskToBack(true)
      홈화면으로 이동하는 것을 확인하였고,
      첫 번째 실행 시 onDestroy()를 호출하지않았습니다.
      Uninstall 하지않고 어플을 다시 실행해본 결과 종료되지 않고
      Hello World! 화면이 나왔으며 뒤로가기 버튼을 누르지 않는 이상 
      Hello World! 화면을 유지하는 것으로 나타났습니다.
      어플을 Uninstall 하지 않고 다시 Eclipse에서 Run 했을 경우
      
      [2012-03-15 10:20:37 - TEST] ActivityManager: Warning: Activity not started,
      its current task has been brought to the front

      이와같은 경고가 Console에서 생겼지만 어플리케이션은 실행이 되는데,
      실행화면은 HelloWorld 화면이 나왔습니다.
 
  3) android.os.Process.killProcess(android.os.Process.myPid());     

       이 명령도 onDestroy()를 호출하지 않고 바로 종료했습니다.
       메소드 이름에도 나와있듯이 프로세스 자체를 그냥 바로 죽이는것 같습니다.
       Log에도 ActivityManager 에서 Process [package Name] (pid 520) has died
       라는 로그가 출력되었습니다.(제가 적은 Log가 아닙니다)
       프로세스를 죽였으니 어플리케이션도 완전히 종료됨을 알 수 있었습니다.
       (현재 작동중인 어플리케이션 목록에서 해당 어플을 찾을 수 없었습니다)

[결론 도출 및 추측]

1. onDestroy()를 호출하며 종료하는 메소드는 finish() 뿐이었습니다.
2. 프로세스 자체를 죽여 종료하고 싶을 때나 단순히 현재 표시되어있는 액티비티를
    종료할 때는 android.os.Precess.killProcess(android.os.Process.myPid()); 와
    finish()를 이용해야 함을 알 수 있었습니다.
3. 액티비티가 1개 일 경우 moveTaskToBack(true)를 사용하기는 좀 더 생각해봐야
    할것 같습니다. 

첫 번째 _ 세부실험 2

조건 : 액티비티가 2개일 경우

실험과정   
    1. 기본 프로젝트 > 메인 Activity > 
onCreate(...)에서 두 번째 Activity 호출 > 두 번째 Activity에서 종료메소드 호출 

    2. 첫 번째 및 두 번째 Activity에서 Override 된 onDestroy() 메소드 안에
        Log.e() 를 통해 결과 확인
    3. 상황에 따라 알게된 결과 보고
 

실험결과  
   

    1) finish()
        가장 처음 호출된(첫 번째 액티비티)로 전환됨을 알 수 있었습니다.
        그러니까, 첫 번째 액티비티로 돌아가게 된겁니다. 당연히 프로세스는 죽지 않았고
        두 번째 액티비티에서 onDestroy()를 호출하며 전환되었습니다.



     2) moveTaskToBack(true)
       
홈화면으로 이동했습니다. 마치 어플리케이션이 완전히 종료된 것처럼 보입니다.
        하지만 무조건 홈으로 이동하는건 아니었습니다. 홈화면에서 실행해보고, 설정을 눌러 어플리케이션의 목록을 보여주는 화면에서
        실행해본 결과, 실행하기 이전의
화면으로 전환했습니다. (아이폰은 기본적으로 이런기능이 없는듯..)
        그리고 프로세스는 여전히 살아있습니다. onDestroy()를 호출하지 않았습니다.
        이번에도 한번 Uninstall 하지 않고 다시 설치하여 실행해보았습니다.
        같은 종류의 Warning 이 나왔습니다.        



       [2012-03-15 11:51:41 - TEST] ActivityManager: Warning: Activity not started, its
        current task has been brought to the front
 


        현재 보여지는 Task(즉, 액티비티) 는 이미 존재하기 때문에 Activity가 실행되지 않았다는 말인것 같습니다...
        (고수님들 이에대한 조언을 구합니다)


     3) android.os.Process.killProcess(android.os.Process.myPid()); 
        화면의 이동이 있어보이지만 이내 곧 다시 같은화면이 출력됩니다. 
        계속 같은화면이 나오지만 같은 액티비티가 계속 생성되는 것은 아니었습니다.
        그리고 onDestroy()를 호출하지 않습니다.
        이 부분에 대한 로그를 자세히 살펴보고 분석할 수 있는 실력이 되어야겠군요....
        왜 이런현상이 되는지를 모르니........

[결론 도출 및 추측]

1. onDestroy()를 호출하는 메소드는 finish() 뿐이었습니다.
2. 아무래도 moveTaskToBack() 메소드는 혼자 쓰면 어플리케이션에서 액티비티 전환시 불안정하게 작동할 것 같습니다.
3. 특히 killPrecess는 액티비티가 2개 이상일 경우. 상황에 따라 종료시켜주는 메소드를 추가해서 코딩해야할것 같습니다.


일단 첫 번째 실험이 끝이 났습니다. 이 중 finish() 만이 onDestroy()를 호출해준다는 것을 알게 되었습니다. 개인적인 제 생각으론..
moveTaskToBak() 과 finish() ,  killProcess 와 finish() 이렇게 써 주는것이 좋을것 같다 라는 생각을 해봅니다..(물론 상황에 따라 다르겠지만)
아, 그리고 액티비티를 2개까지만 했는데 3개 부터는 하지 않았습니다.
(기회가 된다면 3개 이상도 해봐야지요...ㅎㅎ)

이번엔 "포스코" 님이 실험하신 내용을 그대로 다시 실험하되 액티비티 개수에 따라 결과가 달라지는지를 알아보도록 하겠습니다.


①  finish()
②  moveTaskToBack()
③  android.os.Process.killProcess(android.os.Process.myPid()); 

조합순서

1. ②  +                  = 정상

2. ②                        = 정상     이미 위 실험을 통해 말씀드렸습니다.
3.                         = 정상     이미 위 실험을 통해 말씀드렸습니다.
4.   +                  = 오류
5.   +    +          = 오류
6    +                  = 정상 

실험 두 번째 CASE ( 종료 메소드의 조합 )


두 번째 _ 세부실험 1

조건 : 액티비티가 1개일 경우

실험과정   
    1. 기본 프로젝트 > 메인 Activity > 
onCreate(...)에서 종료 메소드 조합

    2. 상황에 따라 알게된 결과 보고
 

실험결과  
  1) 
  +    (moveTaskToBack()
                                  + android.os.Process.killProcess(android.os.Process.myPid());)
        어플리케이션이 종료되었고 프로세스도 죽었습니다.
        이는 설명하자면 어플 실행하기 바로 전 액티비티로 돌아간 후 실행했던 어플의 프로세스를 죽이는것 같습니다.
         ③ +   순서로 실험했는데 같은 결과가 나왔습니다.
        정상이 맞습니다. 


  2)    +      (finish()
                                   + android.os.Process.killProcess(android.os.Process.myPid());)
        어플리케이션이 종료되었고 프로세스도 죽었습니다.
        허나 이상한건.. 오류가 나지 않았다는 것.
        한번에 오류가 났다면 좋을텐데.. 아주 단순한 액티비티라서 그런지 오류없이 잘 종료되었습니다.
        "포스코"님 말처럼 어쩌다가 오류가 나는 것일수도....
        (ANR은 보통 스레드에서 생긴다는데.. 스레드쪽을 다시 한번 보심이.......?;;;)

  3)    +    +   (finish() + moveTaskToBack() 
                                   + android.os.Process.killProcess(android.os.Process.myPid());)
        위의 조합과 결과가 같습니다. 오류가 나지 않습니다...
        1 2 3 으로 해볼 수 있는 모든 조합
        [1, 2, 3] , [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]
        (호출 순서는 상관없어보이지만 안해보고는 모르기 때문에 해보는 겁니다)
        이 모든 조합도 모두 정상적으로 어플이 완전히 종료되었습니다.

       
  4)    +    (finish() + moveTaskToBack())
         유일하게 액티비티의 onDestroy()를 호출하는 조합입니다.
        그리고 어플 프로세스는 죽지 않고 액티비티만 destroy 된것 같습니다.
        이것 역시 오류가 없었습니다.



[결론 도출 및 추측]

1. onDestroy()를 호출하는 종료 메소드 조합은 ① + ② 입니다.
2. 유일하게 프로세스를 죽이지 않는 조합도  ① + ② 었습니다.
3. 오류가 날것 이라는 조합들이 정상적으로 동작하는 것을 알게되었습니다.
    물론 아주 간단한 구조라서 오류가 없을지 모르지만 이 조합자체가 오류를 유발시키는건 아닌것 같습니다. 



두 번째 _ 세부실험 2


조건 : 액티비티가 2개일 경우

실험과정   
    1. 기본 프로젝트 > 메인 Activity > 
onCreate(...)에서 두 번째 Activity 호출 > 두 번째 Activity의 onCreate(...)에서 종료 메소드 조합

    2. 상황에 따라 알게된 결과 보고
 

실험결과  
  1) 
   +    (moveTaskToBack() 
                                  + android.os.Process.killProcess(android.os.Process.myPid());)
        응용프로그램 Running 쪽을 보면 목록에 없는데 어플리케이션의 상세정보를 보면 Force Stop (강제종료) 버튼이 활성화 되어있습니다.
        이는 프로세스가 완전히 죽지 않았다는 얘기가 됩니다.
        물론 어플은 종료되고 프로세스 목록엔 없지만 뭔가 남아있습니다..-_-;;
        (원래 한 번 실행한 어플은 프로세스는 죽어도 "강제종료"가 활성화 되어있는걸지도 모르겠습니다..ㅎㅎ)

  2)    +      (finish() + android.os.Process.killProcess(android.os.Process.myPid());)
        (맨처음 실험한 내용과 같습니다)
        화면의 이동이 있어보이지만 이내 곧 다시 같은화면이 출력됩니다. 
        계속 같은화면이 나오지만 같은 액티비티가 계속 생성되는 것은 아니었습니다.
        그리고 onDestroy()를 호출하지 않습니다.
        이 부분에 대한 로그를 자세히 살펴보고 분석할 수 있는 실력이 되어야겠군요....
        왜 이런현상이 되는지를 모르니........ 
     
  
  3)    +    +   (finish() + moveTaskToBack() + android.os.Process.killProcess(android.os.Process.myPid());)
       1) 번의 실험결과와 같았습니다.

       
  4)    +    (finish() + moveTaskToBack())
        두 번째 액티비티(최근 화면에 보인)가 onDestroy()를 호출합니다.
        그리고 프로세스가 살아있습니다.
        화면만 사라진 것일뿐... 별다른 오류가 없었습니다.



[결론 도출 및 추측]

1. 실험결과 오류가 발견되지 않았지만 사용하기엔 조금 찝찝한 조합이 있었습니다 : (2)조합
2. 경우에따라, 상황에따라 작동하는 조합 혹은 단일메소드를 사용해야할 것 같습니다.



이로써 모든 실험이 종료되었습니다. 이 모든 실험을 통틀어 제가 정말 부족하다는것을 알게되었습니다..
(원래부터 알았지만.. 뭔가 더 절실하게 느껴졌다랄까요..)


이 실험은 어떤것이 더 좋냐 안좋냐를 가려내기보단 결과를 보고 "어떤상황에 어떤 메소드를 쓰면 좋겠다.." 라는 생각을 키워주기 위함이고
제 공부를 한 것입니다.
그래서 지나가다 제 글을 보신분 중에 제가 작성한 내용이 부적절하거나 부족한 부분이 있으면 댓글을 남겨주십시오.
비평도 좋습니다. 


여기까지 읽어주셔서 감사합니다.
그리고 여러분에게 도움이 되는 글이었으면 좋겠습니다.

그리고 추가적인 사항은~ "아이"님의 블로그에서 퍼온 intent를 활용하여 홈스크린으로 전환하는 것입니다. (어플의 완전한 종료가 아닙니다!)

Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
startActivity(intent);
이 코드를 쓰시면 된다고 합니다. 자료를 포스팅해주신 "아이"님 매번 감사드려요~

From. 박스튜