본문 바로가기

Android

액티비티(Activity)

액티비티란?

어떤 활동에 따른 Application의 Component로서 일반적으로 윈도우를 가진 상태로 화면에 표시되는 것입니다.

UI가 없는 액티비티도 있지만 기본적으로 한 액티비티가 한 화면에 표시됩니다.

 

액티비티를 만들기 위해서는 액티비티를 상속한 클래스를 만들어야 하는데, android.support.v7.app.AppCompatActivity를 상속해서 하면 됩니다.

 

액티비티를 상속함으로써 Material Design의 가이드라인에 따른 AppCompat 라이브러리를 활용할 수 있습니다.

 

AppCompatActivity를 상속할 수 없을 때에는 AppCompatDelegate를 이용하면 됩니다.

https://developer.android.com/reference/androidx/appcompat/app/AppCompatDelegate

 

AppCompatDelegate  |  Android Developers

 

developer.android.com

 

액티비티 수명주기

1. onCreate(): 액티비티가 생성되거나 상태가 변경될 경우 호출되는 콜백함수(초기화 처리와 뷰 생성)

2. onStart(): 통신이나 센서 처리를 시작

3. onRestart(): 보통은 아무것도 하지 않아도 됨

4. onResume(): 최전면에 표시되며, 필요한 애니메이션 실행 등의 화면 갱신을 처리할때 쓰인다.

5. onPause(): 일시 정지 상태로, 애니메이션 등 화면 갱신 처리를 정지 또는 일시정지할때 필요 없는 리소스를 해제하거나 필요한 데이터를 영속화하기 위함

6. onStop(): 통신이나 센서 처리를 정지

7. onDestroy(): 필요 없는 리소스를 해제하거나 액티비티 참조를 모두 정리하는 경우

 

onCreate()에서 뷰를 만들면 리소스가 할당되면서 작업이 실행되는데, onDestroy()에서 이를 해제할 수 있습니다.

그렇지만 해당 클래스를 다른 클래스에서 참조하고 있을 경우, onDestroy()로 폐기하더라도 메모리에 남아 결국

누수가 발생할 수 있습니다.

이러한 메모리 누수를 해결하기 위해서는 LeakCanary라는 Square사에서 개발한 라이브러리를 이용하면 됩니다.

해당 라이브러리는 메모리 누수를 감지하여 어떤 인스턴스에서 원인이 되는지 알려줍니다.

이 라이브러리는 기본 설정만으로 Fragment의 누수를 감지하진 않습니다.

따라서 개발자가 직접 구현해야 하는데, 이 때 사용하는 클래스가 RefWatcher입니다. 

이는 가비지 콜렉션으로 해제됐는지 감시하는 기능을 가지고 있어 Fragment에서의 메모리 누수도 확인할 수 있습니다.

 

이외에 메모리 누수를 애초에 방지하기 위해서 최적화 작업을 하는 것이 중요합니다.

불필요한 메서드 호출을 피하는 것이 좋으며 객체 풀과 객체를 재사용하는 것이 좋습니다.

가령, 메서드 호출을 반복문에서 지속해서 부른다면 메모리 발생이 크게 생기겠죠?

자바에서는 원시적인 타입 호출 외에는 모두 객체로 정의되기에, 메모리는 힙에 확보되어 가비지 콜렉션의 대상이 됩니다.  따라서 객체는 재사용하는 것이 좋습니다.

 

메모리 효율이 높은 라이브러리를 사용하는 것도 방법입니다.

키-값 형식의 데이터 구조에서는 HashMap을 많이 쓰긴 합니다.  하지만 HashMap은 알고리즘 특성상 미리

어떤 일정한 크기의 배열을 확보합니다.

그러므로 속도는 빠를지언정, 메모리상 비효율적이라 할 수 있습니다.

따라서 양이 많지 않은 데이터일 경우 ArrayMap이나 SimpleArrayMap을 이용하는 것이 좋습니다.

https://developer.android.com/reference/android/util/ArrayMap

 

ArrayMap  |  Android Developers

 

developer.android.com

 

오토 박싱을 피하는 것도 메모리 효율을 높이는 방법입니다.

원시형 타입 선언인 int나 long 등은 힙이 아니라 스택 영역에 확보됩니다.

그러므로 이용이 끝나면 가비지 콜렉터를 기다리지 않고 바로 해제됩니다.

또한 인스턴스를 생성하지 않기 때문에 메모리도 절약됩니다.

하지만 HashMap<Integer, String> myMap = new HashMap<>(); 이러한 해시맵에서

Key, Value로 선언하게 된다면 해시맵을 생성할 때 Integer 인스턴스가 생성되기 때문에 결과적으로

메모리가 소비됩니다.

 

따라서 SparseArray라는 클래스를 사용하게 되면 int와 임의의 객체의 키-값을 가지기 때문에 불필요한 오토박싱을

피할 수 있습니다.

 

SparseArray<String> myMap = new SparseArray<>();

myMap.put(1, "value"); 

 

 


디바이스 설정에서 세로 > 가로 or 가로 > 세로로 바뀌면 액티비티가 폐기되고 다시 생성되므로 액티비티의 상태가 지워지게 됩니다.

이러한 문제를 해결하기 위해서 onSaveInstanceState/onRestoreInstanceState라는 콜백 메서드를 사용해야 합니다.

이는 일시적으로 데이터를 저장하고 복귀할 때 저장한 데이터를 가져오도록 하는 메서드입니다.

onSaveInstanceState에 값을 저장한 다음에 onRestoreInstanceState에서 상태를 복귀시키는 처리를 하면 됩니다.

 

[예제]

 

private String mText;

private EditText mEditText;

 

@Override

protected void onSaveInstanceState(Bundle outState){

   super.onSaveInstanceState(outState);

   mText = mEditText.getText().toString();

   outState.putString("edt_text1", mText);

}

 

@Override

protected void onRestoreInstanceState(Bundle savedInstanceState){

   super.onRestoreInstanceState(savedInstanceState);

   mText = savedInstanceState.getString("edt_text1");

}

 

onSavedInstanceState() 메서드로 인수를 전달할 때 Bundle형 인스턴스에 저장하고 데이터를 보냅니다.

onRestoreInstanceState() 메서드에서 저장된 데이터를 가지고와서 복귀 처리를 합니다.

Bundle로 설정할 수 있는 자료형은 int, float 등 자바의 기본형과 문자열, 리스트와 Parcelable형을 구현할 수 있습니다.

 


액티비티 백스택의 중요성

 

새로운 액티비티가 시작되면 실행 중인 액티비티는 백스택에 들어가게 됩니다.

또한 시작된 액티비티는 태스크라는 그룹에 속합니다.

안드로이드 OS 버전에 따라 동작 방법이 미묘하게 다르기 때문에 모두 이해하긴 어렵습니다.

따라서 이것만 이해하시면 됩니다.

  • 같은 앱에서 시작된 액티비티는 같은 백스택에 쌓인다.
  • taskAffinity의 속성에 따라 소속되는 태스크가 달라진다.
  • launchMode에 따라 액티비티 생성의 여부, 새로운 태스크에 속하는 등 액티비티의 시작이 달라진다.

백스택에 쌓인 액티비티는 [뒤로가기] 키 등으로  액티비티를 종료하면 위에서부터 차례로 꺼내지게 됩니다.

taskAffinity는 태스크 친화성이라는 뜻이지만, 대체로 태스크 이름으로 바꿔 읽는 것이 이해하기 편합니다.

taskAffinity가 지정되지 않은 경우에는 자기 앱의 패키지 이름이 태스크 이름이 됩니다.

별도로 설정하지 않는다면 그 앱의 taskAffinity (태스크 이름)는 모두 같아질 것입니다.

 

launchMode는 총 4가지가 있으며 주로 사용하는 것은 standard, singleTop, singleTask입니다.

  • standrad: 매번 액티비티의 인스턴스를 새로 생성한다. (default)
  • singleTop: 같은 액티비티가 최상위에서 실행 중이면 액티비티를 생성하지 않고, 대신 최상위 인스턴스의 onNewIntent()를 호출한다.
  • singleTask: 1개의 태스크에 인스턴스가 존재한다.  이미 같은 액티비티가 실행 중이면 액티비티를 생성하지 않는다.
  • singleInstance: 1개의 태스크에 1개의 인스턴스만 존재한다.  다른 액티비티를 태스크에 포함하지 않는다.  이미 같은 액티비티가 실행 중이면 액티비티를 생성하지 않는다.

백스택 확인

$ adb shell dumpsys activity activities

singleTask, singleInstance로 설정한 경우, startActivityForResult()를 호출하기 때문에 다른 앱과 연계할 수 없습니다.

이때는 곧바로 Activity.RESULT_CANCELED가 반환되어 취소로 다루게 됩니다.

 

 

반응형

'Android' 카테고리의 다른 글

Firebase Messaging 관리  (2) 2023.01.17
Kotlin 문법2 - Class  (0) 2023.01.17
Kotlin 문법1 - 변수/Scope  (0) 2023.01.17
Service & Thread  (0) 2018.03.13
구글 스토어에 등록하는 방법  (0) 2018.02.23