본문 바로가기

개발 코딩 정보 공유/안드로이드 자바 코틀린

HandlerThread 의 활용

 

안드로이드 지식 공유

 

 

HandlerThread 의 활용

 

 

우리가 전편에서 구경했던? Handler 는 매우 쉽고 간편하게 비동기적 처리를 담당해주는 고마운 녀석이 었습니다. 그러나 백그라운드 작업에서 핸들러를 설정하려면 여러가지로 셋팅해줘야 할 부분들이 생기죠. 쓰레드의 동작 까지 신경써야 합니다. 그리하여 우리는 HandlerThread 라는 녀석과 만나게 됩니다. 

 

https://developer.android.com/reference/android/os/HandlerThread

 

이 녀석은 쉽게말해 우리가 쓰레드를 생성하고 그안에 핸들러 셋팅까지 했던 것을 자동으로 해주는 고마운 친구 입니다. 즉 Thread Looper MessageQueue 의 복잡함을 자동으로 관리해준다는 말이죠. 동작방법은 너무도 쉽습니다.

 

HandlerThread handlerThread = new HandlerThread("HandlerThread-1");

handlerThread.start();

 

어디서 많이 보던 start 아닙니까? ㅎㅎ 맞습니다. 우리가 쓰레드 동작시 사용하던 그 방식과 유사합니다. 핸들러쓰레드의 경우도 마찬가지로 핸들러사용시 따랐던 기준을 그대로 따릅니다. 우리가 해주지 않을뿐 내부적으로 looper 와 MessageQueue를 가지고 있고 이는 순차적으로 처리되지요. 골치 아픈 동기화따위 신경쓸 필요가 없습니다ㅎㅎ

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
private class TestHandlerThread extends HandlerThread{
 
        private static final int READ = 1;
        private static final int WRITE = 2;
 
        private Handler mHandler;
 
        @Override
        protected void onLooperPrepared() {
            super.onLooperPrepared();
            mHandler = new Handler(getLooper()){
                @Override
                public void handleMessage(Message msg) {
                    switch(msg.what){
                        case READ:
                            //내용을 읽습니다...
 
                            break;
                        case WRITE:
                            //내용을 작성합니다...
 
                            break;
 
                    }
 
                }
            };
        }
 
        public void read(){
            mHandler.sendEmptyMessage(READ);
        }
 
        public void write(int i){
            mHandler.sendMessage(Message.obtain(mHandler, WRITE, i));
        }
 
 }
cs

 

 

 

이너클래스를 생성하고 그 안에 HandlerThread 를 상속합니다. 그리고 반드시 onLooperPreared() 메서드를 오버라이드 하여 구현해 주셔야 합니다. 여기서는 Handler 를 생성하고 getLooper()로 바인딩 해준뒤 메세지를 처리하고 있네요. 메세지는 아래와 같은 경우 작업이 진행되겠죠.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
...
private Button btnRead;
private Button btnWrite;
private int mCount = 0;
 
 
   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_thread);
 
        mTextValue = findViewById(R.id.text06);
        btnRead = findViewById(R.id.btnRead);
        btnRead.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                onButtonClickRead(view);
            }
        });
        btnWrite = findViewById(R.id.btnWrite);
        btnWrite.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View view) {
                onButtonClickWrite(view);
            }
        });
 
        mThread = new TestHandlerThread();
        mThread.start();
 
    }
 
    /**
     * ui스레드에서 더미값을 기록한다
     * @param v
     */
    public void onButtonClickWrite(View v){
        mThread.write(mCount++);
    }
 
    /**
     * ui스레드에서 읽기를 시작한다
     * @param v
     */
    public void onButtonClickRead(View v){
        mThread.read();
    }
cs

 

 

버튼을 두개 만들어 읽고 쓰는 작업을 시켰습니다. public 메서드를 통해 핸들러쓰레드의 만들어두었던 public 메서드 들을 통해 핸들러 에게 정확히 전달 되겠죠? 그러면 그 핸들러는 해당 작업을 진행할 겁니다. 이제 우리는 쓰레드를 만들고 할 필요 없이 유연한 비동기 로직을 만들었습니다. 

 

아 한가지 빼먹은 부분은 이 핸들러 쓰레드를 다 사용하고 나서 뒷처리 입니다. 핸들러쓰레드의 경우는 루퍼의 종료를 통해 쓰레드의임무도 다하게 됩니다. 핸들러는 quit()를 통해 루퍼의 종료를 선언 할 수 있죠.

 

 

1
2
3
4
5
6
7
8
/**
     * 액티비티와 함께 백그라운드 스레드가 종료됨을 보장한다.
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mThread.quit();
//mThread.quitSafely()
    }
cs

 

이런식으로 액티비티와 생명주기를 같이 할 수도 있습니다. 모든 작업이 끝났다면 말이죠. 핸들러를 통해 마지막 종료 태스크를 전달 할 수도 있겠죠? Looper.myLooper()를 통해 핸들러 자신과 연결된 루퍼를 가져옵니다. 그리곤 종료 명령을 때려버립니다.ㅎㅎ

 

 

 

1
2
3
4
5
6
7
handler.post(new Runnable(){
    
    @override
    public void run(){
        Looper.myLooper().quit();
    }
});
cs

 

 

 

이렇게 말이죠. 한번 종료된 핸들러쓰레드와 친구들은 한번 종료되면 다시 사용할 수 없습니다. 다시 시작할때는 분명 Exception 친구를 만나게 될 것입니다. ^^ 

 

잘 사용하면 너무 편한 핸들러 , 핸들러 쓰레드에 대해 알아보았습니다. 비동기 처리를 간단하게 구현할 수 있지만 사실은 개발하는 입장에서 신경써야 할게 상당히 많습니다. 한마디로 노간단... 다음 시간에는 좀 더 활용도가 높은 AsynTask 에 대해 알아보겠습니다. 그럼 이만.