Android/Concept

Android about Context (안드로이드 Context에 대하여)

베어헌터 2019. 8. 7. 18:13

 이번에는 안드로이드 프로그래밍 중에 많이 접하게 되는 Context에 관하여 알아봅시다.

 

바로 요놈

 

 

일단 사전적 의미는 '문맥, 맥락'입니다. 음... 뭔가 감이 안옵니다.

 

위에 구글이 정의 해 놓은 설명의 첫 줄을 보면, 어플리케이션 환경에 대한 글로벌한 정보에 대한 인터페이스 랍니다. 즉, 대강 해석하면 앱에 대한 여러 정보들을 갖고 있는 놈이라고 볼 수 있습니다.

 

 

 

 이건 예전에 다른 글에서 봤던 것인데, Context가 영어권에선 바로 요것을 뜻한다고 합니다.

속성!!


 또는 운영체제나, 시스템 프로그래밍을 배우신 분들에게 쉽게 설명 해 드리자면 레지스터들을 생각 해 볼 수 있습니다. rsp, rax, rbx 등의 값들을 Context라고 하고, 프로세스 내에서 값 전환이 일어날 때 Context Switch를 한다고 하죠.


 저렇게 설명하니 바로 감이 잡혔습니다. 파일 같은거 속성 들어가면 용량, 이름, 생성 날짜 등 여러가지 정보들이 뜨죠. 즉, 어플리케이션에 대한 여러가지 기본 정보 라고 볼 수 있겠습니다.

 

 

 

 

 

 

 그럼 어떤 정보들이 있을까요? 대략적으로


  1. 우리가 자주 쓰는, 어플리케이션에 대한 기본 정보(Activity Context)

  2. 조금 생소한, 어플리케이션 프로세스?에 대한 정보(Application Context)

가 있습니다.

 

 Application Context는 말 그대로 어플리케이션 자체의 Context입니다. 한 앱 당 딱 1개 존재합니다. 하지만 Activity Context여러 개가 있습니다. 앞선 글에 안드로이드의 Component는 4개가 있다고 했죠? 각각마다 Context가 조금씩 달라집니다. 그리고 Application Context와는 달리 Activity가 생길 때 마다 Context도 그에 맞춰 새롭게 생겨나죠!

 

 

 

Activity Context

 먼저 1번에 대해 자세히 설명하겠습니다. 우리가 자주 context를 어디서 접할까요? 보통 Intent의 인자로 전달하고, startActivity()와 같은 메서드로 액티비티 화면 전환을 하죠.

val intent = Intent(context, MenuTournamentActivity::class.java)
startActivity(intent)

 여기서 Intent의 첫 번째 인자인 context는 this로 대체 할 수 있습니다. 그럼 this는? 현재 Activity를 가리킵니다. 즉, Activity Context를 뜻 하고 있는겁니다!

(하지만 Intent의 인자로의 Context는 Application Context나 Activity Context나 상관 없습니다! 설명은 밑에!)

 

마찬가지로 startActivity()도 앞에 this가 생략 된 겁니다. 그래서 context.startActivity()가 원문인거죠!

 

이 처럼 Activity Context는 주로 Activity안에서 가장 많이 쓰입니다. 그에 따라 이 Context는 Activity의 생명주기를 따릅니다. onCreate()하면 생기고, onDestroy()하면 사라지는.... 고런 Context입니다.

 

 또한 다른 구성요소인 Service, Broadcast Receiver, Content Provider 들도 Context를 갖고 있고, 조금 다릅니다.

(자세한 건 이해가 잘 안가더군요...!)


 참고로 Context를 가져오는 방법에는 getContext(), getApplicationContext()가 있습니다. 두 개의 차이점은 위에 두 종류입니다!

 그래서 위에 this로써 얻어온 Context는 getContext()로 얻어온 거겠죠?


 그럼 Context를 위에 처럼 대체 왜 넘겨줄까요? 넘어 갔을 때, 바로 전에 호출한 Activity에 대한 정보가 필요하기 때문입니다. 그래야 앱의 스택에 쌓이고, 취소 키 눌렀을 때 전 화면으로 돌아가거나 하겠죠?

 

 또한 TextView, ImageView 등 여러 View 객체들은 Activity 안에서 사용되죠? 그래서 View의 Context는 기본적으로 묶여있는 Activity의 Context를 참조합니다.

 

 

 

Q. 어? 근데 전 context를 getPackageName()이나 getResource() 같은 걸로 많이 봤는데요?

 

 네. 패키지 이름이나 xml, img와 같은 리소스 등을 불러오려면 앱에 대한 정보가 필요하죠? 그래서 Context를 통하는 겁니다.

 하지만 위에 설명한 Activity Context로는 잘 쓰이지 않습니다. 주로 Application Context로 씁니다. 그 이유는 바로

 

 

 

Application Context

 어플리케이션 수준의 Context를 참조하는 것이 더 효율적이기 때문입니다. 위에 설명 했듯이, 요놈은 한 앱당 1개만 존재해요. Application이 포괄적인 범위고, Activity들이 그 내부에 상세한 사항들이죠.

 

 여기서 좀 더 자세히 설명드리자면, Application Context나 Activity Context나 둘 다 Context 클래스를 상속 받습니다. 다만 상속 받아서 내용이나 생명주기가 조금 다를 뿐이죠.

 하지만 그렇기에 공통으로 갖고 있는 내용이 있습니다. 위에서 Intent 설명 할 때 두 종류 아무거나 Context를 넣어도 된다고 했죠? 바로 이런 이유입니다.

 

 어쨌든, '그래서 Application Context만이 갖고 있는 특!별한 정보는 뭔데??' 라고 의문이 드실겁니다. 이것은 안드로이드의 슬픈 구조에 이유가 있습니다....

 


 보통 우리가 프로그램(어플리케이션)을 돌리면, 그에 맞는 프로세스가 돌아가고, 그 프로세스 정보는 OS 시스템이 관리하죠. 하지만 안드로이드는 그렇게 하지 않습니다. 프로세스와 어플리케이션을 따로 관리합니다!!

 

 실생활 예가 있습니다. 분명 모바일 게임하다가 취소 버튼 두 번 눌러서 앱을 종료시켰는데, 홈키 왼쪽 버튼을 눌러서 보니까 앱이 살아있습니다! 물론 다시 클릭하면 앱이 새롭게 시작되죠. 이거 왜 그럴까요? 바로 프로세스는 꺼졌지만 어플리케이션이 남아있기 때문입니다!!

 

 그럼 '시스템이 어플리케이션 관리 안하냐?!' 하시면 그건 ActivityManagerService 라는 놈이 관리한다고 합니다.

 

'얘는 또 왜 나눠서 관리하는데?!'

 

 그 이유는 모바일 이라는 환경의 특성 때문입니다. 안그래도 성능 구린 모바일 기기... 잘 돌아가기 위해서 개발자들은 최적화를 굉장히 많이했죠...(앞선 글의 JVM의 ART 런타임 방식 등)

 

 

 2가지 정도로 예를 들어 볼 수 있는데, 앱에서 아~주 중요한 파일을 웹에서 다운로드 받고 있다고 생각 해 봅시다. 만약 사용자가 강제 종료 하면 큰 문제가 발생합니다! 그래서 앱은 꺼졌지만, 백그라운드에서 필요한 데이터는 웹을 통하여 받습니다.

 다른 예로는 아~주 중요한 업무를 해야하는데 메모리가 부족합니다. 그래서 다른 앱을 잠시 강제종료 시켰습니다. 하지만 사용자는 강제종료 시킨 앱으로 다시 돌아와서 사용을 하려고 합니다! 그러기 위해서는 강제종료 하기 전의 여러 정보들이 필요하죠. 전에 사용한 곳 부터 보여줘야 하니까!

 

 

 여튼 이런저런 이유 때문에 안드로이드는 어플리케이션과 프로세스를 나눠 놓고, 아무리 앱을 끄더라도 항상 돌아가는 듯한 느낌을 주도록 하고 있습니다.

 

 그래서 결론은, ActivityManagerService라는 놈은 어플리케이션과 프로세스간의 관계? 같은 정보들을 갖고 있다는 소립니다.


 굉장히 삥~ 돌아왔지만...! 결국 ActivityManagerService에서 어플리케이션에 대해 필요한 고유 정보를 Application Context가 갖고 있다는 것 입니다! 그래서 한 앱당 1개만 고유하게 존재한다는 거죠!

 

 

 

 

Conclusion

 Application Context는 가장 상위의 포괄적인 정보들을 갖고있고, Activity Context는 지엽적인 정보들을 갖고 있습니다.

 

 따라서 상황에 맞게 Context들을 쓰는 것이 최적화에 있어서 좋죠. 딱 해당 Activity 생명주기에서만 쓰는 놈이냐, 아니면 Global한 생명주기에 쓰이는 놈이냐 등...

 

 하지만 솔직히 경우를 잘 몰라서 IDE가 '여기에 다른 Context 넣어!' 라고 에러를 띄워줘야 압니다...


 한 가지 크게 와 닿는 경우가 있었는데요, 바로 다른 Activity나 백그라운드(AsyncTask) 같은 곳에 Context를 넘겨 줄 때, 무조건적으로 Application Context를 넘겨야 한다는 겁니다. 왜냐하면 현재 Activity Context를 다른 Activity에 넘겨서 사용했다간, 전에 있던 Activity가 죽어서 참조 해야 하는 Context가 사라져 버릴 수도 있는겁니다!

 게다가 이렇게 꼬여버리면, GC(Garbage Collector)가 해당 Context를 메모리 상에서 처리하질 못하게 됩니다...메모리 누수(Memory Leak)가 발생하죠.


 그래서 View나 Activity Context 등 Activity에 종속 된 데이터를 다른 곳에서 참조하려면 WeakReference 형태로 참조합니다. 이에 대해서는 다른 글에!!