JVM은 동시에 하나의 어플리케이션이 여러개의 스레드를 사용하여 작업할 수 있도록 해준다.
각각의 스레드는 우선순위를 가지고 있고 우선순위가 높은 녀석 순으로 작업이 실행된다. 각 스레드들은 데몬으로서 표시될 수도 있고 아닐 수도 있다.
어떤 스레드가 또 다른 스레드에 의해서 생성된다면 생성한 스레드의 우선순위를 따른다. 또 생성 스레드가 데몬 스레드일 때만 자식 스레드가 데몬 스레드이다.
JVM이 최초에 실행되었을 때는 대개 non-daemon의 싱글 스레드만 존재한다. (일반적으로 psvm 을 호출한다)
JVM은 다음과 같은 경우가 발생할 때 까지 스레드를 유지한다.
Runtime
class의 exit
method 가 호출되고 security manager
가 종료동작(exit operation)
을 허용했을 때 run
메소드 너머에서 전달된 exception
에 의해 죽었을 때 사용자가 앱을 실행시키면 안드로이드는 메인 스레드에서 실행되는 리눅스 프로세스를 생성한다.
메인 스레드는 UI Thread
라고도 알려져있다. 메인 스레드는 화면에 표시되는 모든 것들을 책임진다.
이 녀석이 어떻게 동작하는지 이해하면 앱의 퍼포먼스 향상에 큰 도움이 된다.
메인 스레드의 디자인은 아주 간단하다.
앱이 종료될 때 까지 스레드 작업 큐에 존재하는 작업들을 thread-safe
하게 처리한다.
두가지 방법이 있다.
Thread
class를 상속받는 클래스로 선언하기(이런 클래스를 subclass
라고 부르는듯하다)
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
반드시 run
메소드를 오버라이딩해야한다.
PrimeThread p = new PrimeThread(143);
p.start();
외부에서 이렇게 스레드를 생성하고 start
메소드를 호출하면 된다.
위의 코드에서 Thread
를 extend
하는 부분만 변경되었다.
class PrimeThread extends Thread {
long minPrime;
PrimeThread(long minPrime) {
this.minPrime = minPrime;
}
public void run() {
// compute primes larger than minPrime
. . .
}
}
구현체도 run
메소드를 오버라이딩해야한다.
PrimeRun p = new PrimeRun(143);
new Thread(p).start();
외부에서 이렇게 클래스를 생성하고 스레드 클래스에 넘겨준 후 start
메소드를 호출하면 된다.
Handler
는 스레드의 MessageQueue
와 관련된 Message
와 Runnable
객체를 전송하거나 다룰 수 있게 해준다.
각 Handler
인스턴스는 싱글 스레드와 그 스레드의 MessageQueue
와 연관이 있다.
새로운 Handler
를 생성하면 이녀석은 Looper
에 바인딩된다. 이를 통해 Looper
스레드 위에서 Looper
의 MessageQueue
에 Message
와 Runnable
을 전달하고 실행할 수 있게한다.
Handler는 대표적으로 두 상황에서 사용한다.
Message
와 Runnable
이 원하는 시점(미래)에 실행되도록 스케쥴링할 때Main Thread
와 다른 큐에서 수행할 작업을 대기열에 추가할 때핸들러 생성은 이전의 포스팅을 참고하자
핸들러를 생성 한후 post
메소드를 활용해서 부려먹자
serviceHandler.post(() -> {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
createNotificationChannel();
createLocationService();
createDatebase();
createForegroundService();
} else {
startForeground(2, new Notification());
}
});
이런식으로 작성했다.
스레드를 분리했다고 생각하는 코드에 다음 코드 작성해보자.
if (Looper.myLooper().equals(Looper.getMainLooper()) {
// Toast or Log 등 디버깅코드
}
만약 분리가 되었다면 디버깅 코드가 실행되면 안된다.