Skip to main content

[번역] A journey on the Android Main Thread - Part 1

본 문서는 square engineering blog 에 기재된 A journey on the Android Main Thread - Part 1 기사를 번역한 것 입니다.

coding horrer 에 왜 우리는 소스 읽는 법 을 배워야 하는가 에 대한 기사 가 있습니다.
안드로이드의 가장 큰 특징 중 하나는 오픈소스 생태계 라는 점 입니다.

PSVM (public static void main)

public class BigBang {
 
public static void main(String... args) {
   
// The Java universe starts here.
 
}
}

모든 자바 프로그램은 public static void main() 메소드를 호출하면서 시작합니다.
이는 자바 데스크탑 프로그램, JEE 서블릿 컨테이너, 안드로이드 애플리케이션 이 모두 동일 합니다.

안드로이드 시스템은 부팅 단계에서 ZygoyteInit 이라 불리는 리눅스 프로세스를 실행합니다. 이 프로세스는 달빅VM 으로, 쓰레드에 안드로이드 SDK 의 대부분의 클래스 를 로드 하고 대기합니다.

새로운 안드로이드 애플리케이션을 시작할 때, 안드로이드 시스템은 ZygoteInit 프로세스를 포크 하게 됩니다. 포크된 자식 프로세스의 쓰레드는 대기를 해제하고, ActivityThread.main() 메소드를 호출합니다.



위키피디아 에 정의된 zygote 란 수정란을 의미합니다.

Loopers

계속 진행하기 앞서, 우리는 Looper (이하 루퍼) 클래스를 살펴 볼 필요가 있습니다.
루퍼를 사용하는 것은 하나의 쓰레드가 메시지들을 연속해서 실행하도록 하는 좋은 방법 입니다.

각각의 루퍼는 메시지 객체의 큐를 지니고 있습니다. (이를 메시지 큐 MessageQueue 라고 합니다.)

루퍼는 큐에있는 각각의 메시지를 실행하고, 큐가 비었을 때에는 정지(차단)하는 loop() 메소드를 지니고 있습니다.

Looper.loop() 메소드는 아래와 유사합니다.

void loop() {
 
while(true) {
   Message message
= queue.next(); // blocks if empty.
   dispatchMessage
(message);
   message
.recycle();
 
}
}

각각의 루퍼는 하나의 쓰레드에 연결되어 있습니다. 새로운 루퍼를 생성하고 현제 쓰레드에 연결하기 위해서는 여러분은 반드시 Looper.prepare() 메소드를 호출해야 합니다.

루퍼들은 Looper 클래스안에 스테틱으로 정의된 ThreadLocal 에 저장 됩니다.
여러분은 Looper.myLooper() 메소드를 호출함으로 써 현제 쓰레드에 연결된 루퍼 를 검색할 수 있습니다.

HandlerThread 클래스는 여러분들을 위해 이 모든것을 대신해 줍니다.

HandlerThread thread = new HandlerThread("SquareHandlerThread");
thread
.start(); // starts the thread.
Looper looper
= thread.getLooper();

코드는 다음과 같습니다.

class HandlerThread extends Thread {
 Looper looper
;
 
public void run() {
   Looper
.prepare(); // 루퍼를 생성하고 ThreadLocal 에 저장 합니다.
   looper
= Looper.myLooper(); // Retrieve the looper instance from the ThreadLocal, for later use.
   Looper
.loop(); // Loop forever.
 
}
}

Handlers

핸들러는 루퍼와 매우 잘 어울립니다.

핸들러는 두가지 목적을 지니고 있습니다:

  • 다른 쓰레드로 부터 루퍼의 메세지 큐에 메세지를 전달 합니다.
  • 그 루퍼와 연결된 쓰레드에서 루퍼의 대기열의 메시지를 처리합니다.

// 각각의 핸들러는 하나의 루퍼와 연결되어 있습니다.
Handler handler
= new Handler(looper) {
 
public void handleMessage(Message message) {
   
// Handle the message on the thread associated to the given looper.
   
if (message.what == DO_SOMETHING) {
     
// do something
   
}
 
}
};
// Create a new message associated to that handler.
Message message
= handler.obtainMessage(DO_SOMETHING);
// Add the message to the looper queue.
// Can be called from any thread.
handler
.sendMessage(message);

여러분은 하나의 루퍼에 여러 핸들러를 연결할 수 있습니다. 루퍼는 message.target 에 메시지를 전달 합니다.

핸들러를 사용하는 인기있고 간편한 방법은 Runnable 을 전달하는 것 입니다.

// Create a message containing a reference to the runnable and add it to the looper queue
handler
.post(new Runnable() {
 
public void run() {
   
// Runs on the thread associated to the looper associated to that handler.
 
}
});

또한 핸들러는 루퍼를 제공하지 않고도 생성이 가능합니다.

// DON'T DO THIS
Handler handler
= new Handler();

인자가 없는 핸들러 생성자는 Looper.myLooper() 를 호출하게 되고, 현재 쓰레드와 연결된 루퍼를 검색하게 됩니다.
이는 여러분인 핸들러로 연결하고 싶은 쓰레드일 수도 있고 아닐수도 있습니다.

대개, 여러분은 단지 메인 쓰레드에 전달하는 핸들러를 생성하고 싶으실 것 입니다.

Handler handler = new Handler(Looper.getMainLooper());

Back to PSVM

다시 ActivityThread.main() 을 살펴봅시다. 기본적으로 수행하는 것은 아래와 같습니다.

public class ActivityThread {
 
public static void main(String... args) {
   Looper
.prepare();

   
//이제 여러분은 Looper.getMainLooper() 를 호출함으로써 언제든지 메인 루퍼를 검색 할 수 있습니다.
   Looper
.setMainLooper(Looper.myLooper());

   
// 루퍼에 첫번째 메시지를 전달합니다.
   
// { ... }

   Looper
.loop();
 
}
}

이제 여러분은 쓰레드가 메인 쓰레드에서 호출되는 이유에 대해 이해하셨을 것 입니다.

Note : 예상하신 바와 같이, 메인 쓰레드가 첫번째로 할 동작 중 하나는 Application 을 생성하고 Application.onCreate() 를 호출 하는 것 입니다.

다음 시간에는 안드로이드 생명주기와 메인 쓰레드 간의 관계에 대해 살펴 보면서, 어떻게 복잡한 버그를 만들어 내는지도 살펴 볼 것 입니다.

Comments

Popular posts from this blog

[Tip] Android Studio, Intellij 에서 Source Code Pro font 적용

예전에 코딩할 때 좋은 폰트 에 Source Code Pro 가 1등으로 선정된 것을 본 적이 있다. 그전까진 나눔고딕코딩 을 자주 썼는데, Source Code Pro 를 쓰면서 가독성이 좀더 향상된 것을 느낄 수 있어 요즘 주력 폰트로 사용 하고 있다. 그런데 Mac에서는(윈도우는 어떨지 모르겠지만...) Eclipse 나 Xcode 에서는  문제없는 이 폰트가 유독 Intellij 혹은 Android Studio 에서는 폰트 설정 시, 이탤릭체가 깨지는 문제가 발생하고 있다. 폰트 설치 후, Intellij 에 적용하면 아래와 같이 이텔릭 채 부분에서 깨지기 시작한다. 이같은 문제는 이미 Source Code Pro Github issue 에 등록되어 있는데, issue 에서 가이드 하는 수정방법은 다음과 같다. 먼저 Source Code Pro Font 를 다운받고, 설치하자. Source Code Pro Font 다운로드   그 후 아래 폴더로 이동하자. /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/fonts 위 경로에 Source Code Pro 의 TTF 에 포함되어 있던 폰트들을 모두 복사하여 붙여넣는다. 그리고 Intellij (혹은 Android Studio) 를 재부팅 후, 다시한번 폰트를 설정하러 들어가면 아래와같이 폰트들이 나타나는것을 확인 할 수 있다. 개인적으로 Medium 을 선호하기 때문에 Medium 을 선택. 그러면 아래처럼 정상적으로 출력됨을 확인할 수 있다.

Google Press Event 7/24/13

Sundar Pichai 와 함께한 아침. Google 은 3가지 새로운 제품에 대한 발표를 하였습니다. 1. 2nd Nexus7 첫번째는 이미 소문이 무성했던 Nexus7 입니다. 성능이 대폭 향상되었습니다. Nexus 7 발매와 더불어 google app 들이 업데이트 되었고, Google Play Game 과의 연계성도 더욱 향상되었습니다. 4G LTE 는 미국에만 제한적으로 사용될 것으로 보입니다. 가격은 다음과 같습니다. - 16GB WIFI : $229 - 32GB WIFI : $269 - 32GB LTE : $349 조만간 발매될 국가에 한국도 포함되어 있습니다. (만세!) Google Play 에서 판매 예정이라고 합니다. 개인적으로 고화질 디스플레이 와 OpenGL|ES 3.0 이 지원되면서 게이밍 기능이 향상된 점을 강조하는 것이 눈에 띄였습니다. 아마도 멀티미디어 적인 기능향상으로 인한 자신감이 아닐까요. 2. Android 4.3 두번째로 Android 4.3 에 대한 소식입니다. 새로운 기능들이 많이 추가되었습니다. 4.3 APIs  는 바로 다운 받을 수 있습니다. http://developer.android.com/about/versions/jelly-bean.html 개인적으로 눈에 띄는 APIs 는 ActionBar 가 드디어 하위버전을 지원하기 시작했다는 것 입니다. new v7 appcompat library 에 추가 된 사항으로 Android 2.1(API level 7) 까지 지원합니다. 이를 지원하기위해  ActionBarActivity class 가 새로이 추가 되었습니다. 3. Chromecast 위 두 소식은 이미 들은 이야기가 많았지만 chromecast  는 좀 생소했습니다. 쉽게 생각해서 Apple 의 AirPlay 와 흡사하다고 보시면 될 것 같습니다. ...

[리뷰] 스위프트 프로그래밍

스위프트 3 가 발표된 이후로 국내에도 스위프트 3 내용을 다루는 책들이 하나둘씩 출간되고 있습니다. 그리고 지금부터 이야기할 <스위프트 프로그래밍> 역시 그중 한 권입니다. 이 책은 기존에 있던 책들과는 달리 iPhone이나 Mac App 개발에 필요한 내용을 다루지는 않습니다. 개인적으로는 이러한 점 때문에 스위프트란 언어 자체에 좀 더 집중할 수 있었습니다. 책은 크게 다섯 개의 파트로 구성되어 있으며 각 파트별 목차는 다음과 같습니다. Part Ⅰ 스위프트 기초 1. 스위프트 2. 스위프트 처음 시작하기 3. 데이터 타입 기본 4. 데이터 타입 고급 5. 연산자 6. 흐름 제어 7. 함수 8. 옵셔널 Part Ⅱ 객체지향 프로그래밍과 스위프트 9. 구조체와 클래스 10. 프로퍼티와 메서드 11. 인스턴스 생성 및 소멸 12. 접근제어 Part Ⅲ 함수형 프로그래밍과 스위프트 13. 클로저 14. 옵셔널 체이닝과 빠른종료 15. 맵, 필터, 리듀스 16. 모나드 Part Ⅳ 확장 17. 서브스크립트 18. 상속 19. 타입캐스팅 20. 프로토콜 21. 익스텐션 22. 제네릭 23. 프로토콜 지향 프로그래밍 Part Ⅴ 스위프트 고급 24. 타입 중첩 25. 패턴 26. where 절 27. ARC 28. 오류처리 Part1, 2  까지는 스위프트 언어에 대한 기본적이고 전반적인 내용을 설명합니다. 스위프트를 처음 배우는 독자뿐만 아니라 스위프트를 조금 공부해 본 독자에게도 유용한 내용이 많았습니다. 특히, 스위프트 언어를 사용하는 데 있어 지켰으면 하는 규칙을 반복적으로 설명함으로써 자연스럽게 그러한 규칙이 손에 익히도록 한 점이 좋았습니다. 다만 중간중간 난이도가 갑자기 높아지는 부분이 있어 초심자에게는 책 앞부분에서 조금 힘들 수도 있겠다는 생각이 들었습니다. Part3  는 아마도 이 책에서 가장 ...