안드로이드의 가장 큰 특징 중 하나는 오픈소스 생태계 라는 점 입니다.
PSVM (public static void main)
public class BigBang {
public static void main(String... args) {
// The Java universe starts here.
}
}
public static void main(String... args) {
// The Java universe starts here.
}
}
모든 자바 프로그램은 public static void main() 메소드를 호출하면서 시작합니다.
이는 자바 데스크탑 프로그램, JEE 서블릿 컨테이너, 안드로이드 애플리케이션 이 모두 동일 합니다.
안드로이드 시스템은 부팅 단계에서 ZygoyteInit 이라 불리는 리눅스 프로세스를 실행합니다. 이 프로세스는 달빅VM 으로, 쓰레드에 안드로이드 SDK 의 대부분의 클래스 를 로드 하고 대기합니다.
새로운 안드로이드 애플리케이션을 시작할 때, 안드로이드 시스템은 ZygoteInit 프로세스를 포크 하게 됩니다. 포크된 자식 프로세스의 쓰레드는 대기를 해제하고, ActivityThread.main() 메소드를 호출합니다.
Loopers
루퍼를 사용하는 것은 하나의 쓰레드가 메시지들을 연속해서 실행하도록 하는 좋은 방법 입니다.
루퍼는 큐에있는 각각의 메시지를 실행하고, 큐가 비었을 때에는 정지(차단)하는 loop() 메소드를 지니고 있습니다.
Looper.loop() 메소드는 아래와 유사합니다.
void loop() {
while(true) {
Message message = queue.next(); // blocks if empty.
dispatchMessage(message);
message.recycle();
}
}
while(true) {
Message message = queue.next(); // blocks if empty.
dispatchMessage(message);
message.recycle();
}
}
각각의 루퍼는 하나의 쓰레드에 연결되어 있습니다. 새로운 루퍼를 생성하고 현제 쓰레드에 연결하기 위해서는 여러분은 반드시 Looper.prepare() 메소드를 호출해야 합니다.
여러분은 Looper.myLooper() 메소드를 호출함으로 써 현제 쓰레드에 연결된 루퍼 를 검색할 수 있습니다.
HandlerThread 클래스는 여러분들을 위해 이 모든것을 대신해 줍니다.
HandlerThread thread = new HandlerThread("SquareHandlerThread");
thread.start(); // starts the thread.
Looper looper = thread.getLooper();
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.
}
}
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);
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.
}
});
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();
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();
}
}
public static void main(String... args) {
Looper.prepare();
//이제 여러분은 Looper.getMainLooper() 를 호출함으로써 언제든지 메인 루퍼를 검색 할 수 있습니다.
Looper.setMainLooper(Looper.myLooper());
// 루퍼에 첫번째 메시지를 전달합니다.
// { ... }
Looper.loop();
}
}
이제 여러분은 쓰레드가 메인 쓰레드에서 호출되는 이유에 대해 이해하셨을 것 입니다.
다음 시간에는 안드로이드 생명주기와 메인 쓰레드 간의 관계에 대해 살펴 보면서, 어떻게 복잡한 버그를 만들어 내는지도 살펴 볼 것 입니다.
Comments
Post a Comment