Thread in Android

노력을 즐기는 사람·2020년 11월 15일
0
post-thumbnail

Thread in Android

JVM은 동시에 하나의 어플리케이션이 여러개의 스레드를 사용하여 작업할 수 있도록 해준다.
각각의 스레드는 우선순위를 가지고 있고 우선순위가 높은 녀석 순으로 작업이 실행된다. 각 스레드들은 데몬으로서 표시될 수도 있고 아닐 수도 있다.
어떤 스레드가 또 다른 스레드에 의해서 생성된다면 생성한 스레드의 우선순위를 따른다. 또 생성 스레드가 데몬 스레드일 때만 자식 스레드가 데몬 스레드이다.

JVM이 최초에 실행되었을 때는 대개 non-daemon의 싱글 스레드만 존재한다. (일반적으로 psvm 을 호출한다)

JVM은 다음과 같은 경우가 발생할 때 까지 스레드를 유지한다.

  • Runtime class의 exit method 가 호출되고 security manager가 종료동작(exit operation) 을 허용했을 때
  • 데몬 스레드가 아닌 모든 스레드가 run() 메소드 호출에 의해 종료되거나 run 메소드 너머에서 전달된 exception 에 의해 죽었을 때

Main Thread

사용자가 앱을 실행시키면 안드로이드는 메인 스레드에서 실행되는 리눅스 프로세스를 생성한다.

메인 스레드는 UI Thread라고도 알려져있다. 메인 스레드는 화면에 표시되는 모든 것들을 책임진다.

이 녀석이 어떻게 동작하는지 이해하면 앱의 퍼포먼스 향상에 큰 도움이 된다.

Internals

메인 스레드의 디자인은 아주 간단하다.
앱이 종료될 때 까지 스레드 작업 큐에 존재하는 작업들을 thread-safe하게 처리한다.

Thread 생성방법

두가지 방법이 있다.

Thread 상속 받는 클래스 선언하기

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 메소드를 호출하면 된다.

Runnable 인터페이스 구현체 만들기

위의 코드에서 Threadextend하는 부분만 변경되었다.

     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

Handler는 스레드의 MessageQueue 와 관련된 MessageRunnable 객체를 전송하거나 다룰 수 있게 해준다.
Handler 인스턴스는 싱글 스레드와 그 스레드의 MessageQueue와 연관이 있다.
새로운 Handler를 생성하면 이녀석은 Looper에 바인딩된다. 이를 통해 Looper 스레드 위에서 LooperMessageQueueMessageRunnable을 전달하고 실행할 수 있게한다.

Handler는 대표적으로 두 상황에서 사용한다.

  • MessageRunnable이 원하는 시점(미래)에 실행되도록 스케쥴링할 때
  • 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 등 디버깅코드
}

만약 분리가 되었다면 디버깅 코드가 실행되면 안된다.

참고문서

Handler
Better performance through threading
Thread
Looper

profile
노력하는 자는 즐기는 자를 이길 수 없다

0개의 댓글