Study Record

[안드로이드] 강제 종료 시나리오 테스트 - onSaveInstanceState() 본문

안드로이드

[안드로이드] 강제 종료 시나리오 테스트 - onSaveInstanceState()

초코초코초코 2023. 6. 29. 10:01
728x90

 

😶 앱이 강제로 종료되는 경우

사용자가 앱을 사용하다가 홈 화면으로 눌러 이동하게되면 앱이 종료되지 않고 백그라운드에 남아있게 된다.(Lifecycle 상에서 onStop() 이 호출됨) 그러다가 다시 사용자가 앱을 시작하면 새롭게 Activity 가 생성되지 않고 메모리에 남아있는 인스턴스 객체를 다시 불러오게 된다. 

 

 

하지만 안드로이드 시스템은 백그라운드에 있는 앱들보다 현재 실행중인 앱(포그라운드)의 우선순위가 높기 때문에 백그라운드에 있는 앱들이 강제로 종료될 수 있다. 강제로 종료된 후 사용자가 다시 앱을 실행하면 Activity 를 새롭게 생성하게 된다.

 

뿐만 아니라, 앱의 언어 설정을 바꿔 레이아웃의 텍스트 방향을 바꿔야 하는 경우나 가로 모드, 세로 모드 같은 방향을 바꾸는 경우에도 강제 종료시키고 재생성한다.

 

이렇게 앱이 강제로 종료되는 케이스에서는 EditText 의 text 와 같이 안드로이드 시스템이 인지할 수 있는 데이터는 삭제될 때 저장되었다가 다시 Activity 를 재생성할때 자동으로 복구된다. 하지만, 시스템이 인지하지 못하는 변수들이나 데이터는 모두 소실될 수 있다.

 

 

이 데이터의 소실을 막을 수 있는 방법이 바로 onSaveInstanceState() 메소드를 활용하는 것이다. onSaveInstanceState() 는 onStop() 함수 이후에 호출되며 Bundle 객체로 데이터를 저장할 수 있다.

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState)
    
    // 저장할 데이터 Bundle 객체
    outState.run {
        putInt(KEY_REVENUE, revenue)
    }
}

 

onSaveInstanceState() 에서 데이터를 저장한 Bundle 객체는 onCreate() 에서 Bundle 객체와 onRestoreInstanceState() 함수의 Bundle 객체로 받을 수 있다.

 

onCreate() 함수는 재생성 시 외에도 처음 생성될 때도 호출될 수 있기 때문에 Bundle 객체가 null 일 가능성이 있다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    // 저장된 데이터 불러오기
    if (savedInstanceState != null) 
        revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
    }
    ...
}

 

 

onRestoreInstanceState() 는 onStart() 함수 이후에 호출되는 특징이 있다.

override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)
    
    // 저장된 데이터 불러오기
    revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}

 

 

 

😶 강제 종료 테스트

간단하게 number 변수를 선언하고 onResume() 과 onStart() 에서 1씩 증가하는 Activity 를 예시로 들면,

class MainActivity : AppCompatActivity() {
    private final val KEY_NUMBER = "KEY_NUMBER"
    private final val LOG_KEY = "LOG_"
    private var number = 0
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        
        Log.i(LOG_KEY, "onCreate called")
        
        if(savedInstanceState != null) {
            number = savedInstanceState.getInt(KEY_NUMBER, 0)
            Log.i(LOG_KEY, "onCreate Bundle : ${number}")
        }
    }

    override fun onResume() {
        super.onResume()
        number++
        Log.i(LOG_KEY, "onResume called")
    }

    override fun onStart() {
        super.onStart()
        number++
        Log.i(LOG_KEY, "onStart called")
    }

    override fun onPause() {
        super.onPause()
        Log.i(LOG_KEY, "onPause called")
    }

    override fun onStop() {
        super.onStop()
        Log.i(LOG_KEY, "onStop called")
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        
        outState.putInt(KEY_NUMBER, number)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.i(LOG_KEY, "onDestroy called")
    }
}

onSaveInstanceState() 를 호출하여 number 변수 값을 저장했다가 onCreate 의 Bundle 객체로 다시 number 값을 가져오기 때문에 앱을 실행하고 홈버튼을 눌러 앱을 강제 종료시키고 다시 앱을 실행했을 때 Log 에 찍히는 number 값은 2가 되어야 한다.

 

 

강제 종료시키는 방법

terminal 에서 adb 명령어로 특정 앱 패키지 이름으로 백그라운드에 있는 앱을 강제 종료시킬 수 있다.

adb shell am kill [강제 종료]

// ex)
adb shell am kill com.example.test2

 

+ terminal 에서 abd 명령어가 없을 경우

https://laustudy.tistory.com/343

 

 

시나리오)

앱을 실행시킨다. > 홈 버튼을 누른다 > teminal 창에서 앱을 강제 종료시킨다. > 앱을 다시 실행한다.

 

 

terminal 에서 강제 종료시키면 onDestory() 를 호출하지 못하고 바로 앱이 죽는다. 시나리오 예상 대로 onCreate() 의 Bundle 객체에 저장된 number 변수의 값이 2로 잘 나타나있다.

728x90