[1]
며칠전, 나는 계속해서 보아온 안드로이드의 그래픽이 동작하는 방법에 대한 많은 부정확한 정보들을 바로잡으려는 글을 썼다. 그 글로 인해 많은 건전한 토의가 오고갔지만, 불행히도 일부 사람은 안드로이드가 동장하는 방식에 대해 새로운, 소설같은 그리고 상당부분 기술적으로 부정확한 정보를 가지고 비난을 계속하고 있다.
[2]
이 새로운 비난의 요점은 몇몇 안드로이드의 UI 디자인에 대한 근본적인 원칙들에 대한 것이었고, 왜 그것이 틀렸는지에 대한 것이었다. 나는 안드로이드 UI가 왜 지금의 방식으로 디자인되었으며, 실제로 어떻게 동작하는가에 대한 진짜 배경지식을 전달함으로써 사람들로 하여금 이 논의를 잘 이해하도록 돕고 싶었다.
[3]
그중 하나의 이슈는, 안드로이드가 백그라운드의 작업이 유저인터페이스를 인터럽트하는 정도를 줄이기 위하여 쓰레드 우선순위(thread priorities)를 사용하지 않는다는 것이었다.
*인터럽트는 번역하면 방해하다는 정도가 되겠지만 프로그래밍에서는 실행되는 프로세스를 중지시키고 자신이 실행된다는 의미가 있으므로 그냥 썼습니다. 같은 맥락에서 쓰레드도 해석하지 않았는데, 쓰레드 우선권이라 쓰지 좀 우습네요;;;;
이것은 완전 틀린 얘기다. 실제 안드로이드는 우선순위를 사용한다. 해당 내용은 SDK의 다음 링크에서도 정의되어있다.
http://developer.android.com/reference/android/os/Process.html#THREAD_PRIORITY_AUDIO.
[4]
이것들중 가장 중요한 것은 백그라운드 우선순위와 기본 우선순위다. 사용자 환경(UI) 쓰레드들은 일반적으로 기본 우선순위로 실행된다. 백그라운드 쓰레드들은 백그라운드 우선순위에 의해 실행된다. 백그라운드에 있는 어플리케이션 프로세스들은 그것들의 쓰레드들에게 백그라운드 우선순위를 강제하도록 되어있다.
[5]
안드로이드의 백그라운드 우선순위는 굉장히 흥미롭다. 그것은 cgroups이라는 리눅스의 기능을 사용하는데, 이것은 모든 백그라운드 쓰레드들을 하나의 특별한 스케줄링 그룹으로 넣는다. 이 스케줄링 그룹은 내부에 있는 쓰레드들을 모두 합쳐서 CPU의 10%이상을 사용할 수 없게 되어있다. 그러니까, 백그라운드에 10개의 프로세스가 있다고 가정하고, 모든 프로세스가 동시에 실행되려고 하고 있다고해도, 이 프로세스들은 모두 합쳐서 UI 쓰레드들이 필요로 하는 시간의 10% 이상을 사용할 수 없다는 것이다.
(*foreground는 background의 반대 의미로, 사용자와 상호작용하는 것을 의미합니다. 여기선 UI 쓰레드들이 동작하는 방식을 표현하고 있습니다)
이 정도면 백그라운드 쓰레드들이 UI 쓰레드들을 방해하지 않고 실행되기에 충분하다.
6
당신은 아마도 foregound 우선권 또한 정의 됐다고 알고 있을 겁니다. 이것은 현재 안드로이드에서 쓰이지 않습니다. 그것은 원래 구현에 있었습니다. 그러나 우리는 리눅스 스케쥴러가 실제 순서에 기초된 쓰레드에게 충분한 특혜를 주지 않는다는 것을 발견했습니다. 그래서 우리는 android 1.6부터 cgroups로 변경했습니다. 나는 또한 안드로이드는 iOS 처럼 랜더링 쓰레드를 쓰지 않기 때문에 안드로이드 기초 설계는 기본적으로 잘못 됐고 구식이라는 많은 주장들을 봤습니다.
(렌더링(Rendering)은 컴퓨터 프로그램을 사용하여 모델로부터 영상을 만들어내는 과정을 말한다.)
7
iOS 동작 방식이 확실히 몇 가지 이점이 있습니다. 그러나 이 관점은(this view 이런 불만들이 보는 시선?) 너무 유용한 하나의 특정 기능에 집중되어 있습니다. 그리고 이러한 관점은 그들이(iOS와 android의) 동작하는 방식의 실제 유사성에 대해 얼버무리고 넘어갑니다. => 이 부분 잘 모르겠어요. 올바르게 해석한게 맞나요?
8
안드로이드는 원래 설계 목표는 iOS의 목표와는 매우 많은 차이점을 가졌습니다. 안드로이드의 중요 목표는 오픈 어플리케이션 플랫폼에서 제공하고 어플리케이션 샌드박스들을 사용하는 것과 (sandbox -보호된 영역에서 프로그램을 동작 시키는 것) 어플리케이션이 요구하는 동작을 중앙 권한 인증에 의존하지 않는 더 안전한 환경을 만드는 것이었습니다.
이것을 성취하기 위해서 리눅스 프로세스 독립상태와 사용자 ID들을 사용합니다. 각각의 어플리케이션이 통제되지 않거나 안전하지 않은 방법들로 시스템이나 다른 어플리케이션에 접근할 수 있는 것을 예방할 수 있습니다.
9
이것은 iOS의 기초 설계 제약들과는 매우 다릅니다. iOS는 써드파티 어플리케이션에게는 아무 것도 허락하지 않았습니다.
* third party(제 삼자 : 단말 제조사 측에서 만든 어플이 아닌..제삼자 어플)
10
이런 보안을 달성하는 데 있어 중요한 부분은 각각의 UI 요소들이 화면을 안전한 방법으로 공유하도록 하는 것 입니다.
(수정: 누군가 나에게 지적했는데, iOS가 실제로는 multiple windows와 multiple GL contexts를 사용한다고 했습니다.
제가 얻은 교훈이 있다면, 내가 직접 확인해보지 않은 것에 대해 말하지 말자는 것 :)
그럼에도 불구하고 Android에 대한 나의 진술은 여전히 유효합니다. 반면에 내가 아래에 언급한바와 같이 우린 단지 꽤 최근까지 multiple GL contexts를 수행할 수 있는 하드웨어와 드라이버들을 보유하지 않았을 뿐입니다.) 안드로이드에는 iOS 처럼 multiple GL contexts를 수행할 수 있는 하드웨어와 드라이버들을 갖고 있지 않기 때문에 윈도우들이(윈도우라는 개념? 동작 행동?) 있습니다. 그렇기에 안드로이드에는 윈도우들이 있는 것입니다. 상태바와 알림창은 안드로이드시스템에 의해 그려지고 소유된 윈도우들입니다. 이것들은 어플리케이션 윈도우와는 분리되어 있습니다. 그래서 어플리케이션들은 상태바에 손댈 수 없는데 예를 들어서 sms messages들이 거기에 표시 됐을 때 text를 긁어내는 것 같은 행동은 할 수 없습니다.
비슷한 예로 소프트키보드는 각각의 어플이 소유하고 있는 분리된 윈도우이고 소프트키보드와 어플리케이션은 잘 정의되고 컨트롤된(통제된) 인터페이스를 통해서만 서로에게 상호작용을 할 수 있습니다. (그래서 안드로이드는 서드 파티의 입력 메서드를 안전하게 지원해줄 수 있습니다.)
11
안드로이드의 또다른 목표는 어플리케이션 간의 밀접한 협업을 지원하는 것이었다. 그렇게 함으로써, 공유 API를 구현하여 이를 사용하는 어플리케이션을 만들고, 이 어플리케이션을 기본 안드로이드 어플리케이션 흐름에 통합시키는 것이 쉬워졌다. 이를 위한 부분으로, 안드로이드 어플리케이션들은 전통적으로 "Activities"라는 이름의 부분으로 나누어져있다. 이 Activity는 어플리케이션의 특정 UI 부분을 제어한다.
* http://developer.android.com/reference/android/app/Activity.html
예를 들어, 연락처(contacts lists)는 하나의 activity이고, 자세히보기 화면과 편집화면도 각각 하나의 activity이다.
연락처의 여러 UI화면 사이를 이동하는 것은 이처럼 activity사이의 전환이 이루어진다는 것을 의미한다. 그리고 각각의 activity들은 자신들만의 독립적인 window를 가진다.
*여기서 말하는 window는 android의 class 입니다. 이 activity라는 녀석이 window를 만들어서 이 window에 UI를 올릴 수 있도록 해줍니다. window는 화면의 전체 혹은 일부를 자치하면서 사용자와 상호작용하는 UI를 담고 있습니다.
http://developer.android.com/reference/android/view/Window.html
12
이제 뭔가 흥미로운 것이 보이지 않는가? 애니메이션 효과를 내는기본 안드로이드 UI가 있는 곳이라면 거의 대부분 이 window들이 움직이고 있다. 연락처를 실행할 때의 애니메이션은 홈스크린 window와 연락처리스트 window간의 애니메이션이다. 연락처 자세히보기를 하면 연락처리스트 window와 연락처자세히보기 window간에 애니메이션이 실행된다.
화면하단에서 슬라이드하여 올라오는 소프트 키보드를 보여주는 것은 키보드 window의 애니메이션이다. 공유하기를 눌러서 나타나는 알림창은 알림창을 보여주는 window의 애니메이션이다.
13
화면에 보이는 window는 실제 surface라고 불리는 녀석이다.
*http://developer.android.com/reference/android/view/Surface.html
이것은 공유메모리에 독립적으로 존재하며, window는 이 위에 UI를 그린다. 그리고 여러 window들은 "surface flinger"라는 이름의 독립된 시스템 서비스에 의해 스크린으로 통합된다. (이 통합작업은 독립된 쓰레드에서 normal 보다 높은 우선순위로 실행된다.)
* 여러 window들이 surface를 공유하며 이 위애 UI를 그리는 것을 의미합니다. 레이어를 합치는 개념과도 비슷한 것 같습니다.)
뭔가 익숙한 설명 아닌가?
사실 이것은 iOS가 여러 화면들을 하나의 독립적인 쓰레드로 통합하는 것과 비슷하다. 단지 좀 덜 세부적이고, 훨씬 안전한 수준이라는 점이 다를뿐. 그리고 최초부터 안드로이드는 window composition을 하드웨어 가속으로 지원해왔다.
[14]
UI에서 이루어지는 또다른 중요하고 흥미로운 상호작용중에 하나는 바로 이용자의 손가락의 움직임을 추적한다는 것입니다. -- 스크롤하는 것,목록을 fling하는 것(??), 앨범을 슥슥 넘기는 것(swipe?) 등등
이러한 상호작용을 하려면 창 안에있는 내용을 업데이트해줘야 합니다. 그래서 매번 움직임이 있을 때마다 그 창을 다시 렌더링하는 작업이 필요하게 됩니다. 하지만 이렇게 주요 쓰레드를 render off(??)할 수 있게 된다고 해서 얻는 것이 많지는 않습니다. 이런 작업은 "UI의 이 부분을 X에서 Y로 옮긴 후 완료되면 알려줘라" 같은 단순한 작업이 아닙니다.
--- 모든 움직임은 화면을 터치한 손가락에 대한 이벤트에 근거합니다.(??). 이 이벤트들은 어플리케이션이 메인 쓰레드에서 처리해주어야 하죠.
(내용을 도무지 못 따라가겠네요 ㅜㅜ 몬말인지가 잘.. 프로그래밍하시는 분들은 몬 말인지 이해하시나욤 --a)
15
UI중에서 움직이는 부분 안에 있는 모든 내용을 매번 다시 그려주지 않아도 되면, 성능면에서 도움이 됩니다. 이것은 안드로이드가 1.0버전 이전부터 도입해온 기법이기도 합니다. ListView(목록 보여주는..) 같은 UI 엘리먼트들은 그안에 들어있는 내용을 스크롤해야 하는데, setDrawingCacheEnabled라는 것을 호출해서 그 안의 내용을 cache안에다가 render해둔다음에, 움직일 때마다 bitmap만 다시 그려주면 됩니다.
16
전통적으로 안드로이드에서는, view(뿌려주는 엘리먼트??)만이 drawing cache를 갖고 있는데요, 이 drawing cache는 스크롤을 할 때라든지, 손가락의 움직임을 추적할 때 등, 일시적인 상태에서만 쓸 수 있는 상태가 됩니다. 일시적인 상태에서만 .enabled되게 하는 이유는 뭐냐면요, drawing cache를 쓰면 부하가 많이 걸리기 때문입니다. 비트맵 용으로 추가적인 메모리가 필요하게 되죠. 그리고 cached된 view안에 있는 내용을 다시 그려야 하는 상황이 되면, 메모리가 더 많이 필요하게 됩니다. 왜냐하면 cached된 비트맵을 도로 창에다가 그리는데 또 추가적인 작업이 필요하기 때문이죠.
17
그래서, 모든 것을 고려하면, 안드로이드 1.0에서 각각의 뷰(view)가 texture에 그려지게 하는 것, 그리고 이 texture가 또 다른 쓰레드에서 창에서 혼합되게 하는 방식으로는 얻는 것은 없으면서 비용만 높은 방법입니다.
엔지니어하는데 들어가는 시간도 비용이라고 할 수 있죠. -- 개발자의 시간은 layout-based view hierarchy(여러가지 화면 사이즈에서 잘 보이도록 하는..)나 "remote view(알림이나 위젯용)"같은 것 관련 작업에 쓰는 편이 훨씬 좋습니다. 알림이나 위젯 등은 (안드로이드)플랫폼이 발전하면서 큰 이득을 봤죠.
18
사실, 최근까지만 해도 윈도우 안에서 하드웨어로 가속해서 그리는 것은 불가능했다. 안드로이드의 화면이 여러 개의 윈도우를 가지도록 설계되었기 때문에, 하드웨어로 가속해서 각각의 윈도우를 그린다는 것은, GPU와 드라이버가 동시에 서로 다른 프로세스에서 다수의 활성화된 GL 컨텍스트를 지원해야 함을 의미한다. 당시에는 하드웨어가 이러한 기능을 지원하지 않았고, 그걸 위해 필요한 추가적인 메모리가 없었다는 것을 무시하기까지 했다. 심지어 요즘에도 이러한 기술들은 초기 단계에 있다. -- 대부분의 모바일 GPU는 GL 컨텍스트 스위칭 비용이 꽤 비싸다.
19
나는 이 기사가 사람들이 안드로이드의 동작을 더 잘 이해하는데 도움이 되길 바란다. 그리고 나의 관점을 한번만 더 명확히 해 보자면, 내가 이걸 쓰는 이유가 사람들이 안드로이드를 싫어하는 것에 대해 변명하고자 하는게 아니라, 안드로이드의 동작에 대해 터무니없이 잘못된 설명을 하거나 심지어 그 주제와 관련해서 마치 그들이 대단한 권위자인양 하는걸 더 이상 보기 싫어서이다.
20
물론 아직까지도, 1.0부터 많은 것들이 개선되어온 것과 같이, 안드로이드는 개선해야 할 것을이 많다. 더 긴급한 이슈들이 강조되고 하드웨어의 처리 능력이 개선됨에 따라서, 우리는 안드로이드가 더 좋아지도록 끊임없이 노력할 것이다.
21
마지막 한마디. 나는 브렌트 로얄-고든의 흥미로운 코멘트를 봤는데, 그건 iOS 리스트에서 60 fps 스크롤링을 위해 개발자가 해야 할 몇 가지에 관한 것이었다: "60 fps까지 올리려면 꽤 어려워. 쎌의 뷰 구조를 더 간략화하거나, 컨텐츠 일부를 추가하는걸 지연시키거나, 더 비싼 텍스트 렌더링 API를 필요로하는 텍스트 포매팅을 없애거나, 심지어는 모든 셀로부터 서브뷰를 떼어 내고 모든걸 손으로 그리거나."
22
나는 iOS 전문가가 아니라서, 이걸 진실로 받아들이기로 했다. 이러한 것들은 우리가 안드로이드 앱 개발자들에게 제공한 권고사항들과 정확하게 일치한다. 그리고 이 문장을 기반으로 한다면, 리스트를 60 fps로 스크롤 하는데 있어서 안드로이드에는 뭔가 본질적으로 결함이 있다거나, iOS보다 뭔가 더 나쁘다거나 하는 등의 징후는 보지 못했다.