본문 바로가기

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

Handler 에 대한 이해

 

안드로이드 지식 공유

 

 

 

 

Handler 에 대한 이해

 

 

안드로이드를 개발하면서 Handler 에 대해서 과연 알고 사용하는 것인가? 에 대한 의문이 들었습니다. 편해서 혹은 그냥 아무생각 없이 핸들러를 너무도 막 사용하기 때문이죠. 그게 틀렸다는게 아니고 알고 쓰면 더 좋겠다!! 라는 생각에서 정리해보려 합니다^^

 

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

 

Handler 는 안드로이드 API 1 레벨 부터 사용되어진 매우 익숙한 존재입니다. 비동기적으로 처리해야할 로직을 아주 간단하게 그리고 편리하게 처리할 수 있죠. 사실 내부에 어떤 방식으로 동작하는지에 대해서는 굳이 알필요가 없을 수도 있습니다. 그냥 편하게 쓰고자 하면 말이죠.

 

 

 

 

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
 
public Handler mHandler;
 
...
 
mHandler = new Handler();
mHandler.post(new Runnable(){
    @override
    public void run(){
        ...
    }
});

 

 

이건 우리가 자주 쓰는 구조 인데요. 개발 초기에는 이게 뭔지도 모르고 사용 했었죠. 예전에 웹 개발만 하다보니 쓰레드에 대한 이해가 많이 부족 했었죠. Handler 자체는 쓰레드의 동작과 연관이 깊습니다. 왜냐면 Handler 자체가 비동기 방식을 편하게 컨트롤 할수 있도록 만들어 논 것이기 때문이죠 ^^; (안드로이드를 잘 만들기? 위해서는 쓰레드에 대한 어느정도 깊이있는 이해가 필요하다고 생각합니다. 그냥 개인적인 생각입니다^^;

)

Handler 는 new Handler() 에서 new 를 만나게 되면서 객체가 생성되게 되는데요. 이때 내부적으로 Looper 와 MessageQueue 라는 놈과 연관지어 지게 됩니다. 객체를 생성 하게 되면 그 객체를 생성한 쓰레드에 Looper와 MessageQueue에 바인딩 되어지게 됩니다. 즉 이 3종세트가 handler 사용에 연관된 전부다. 라고 생각하면 쉬울것 같습니다. 위에 간단한 소스 코드를 보면 사실 post 메서드는 runnable 태스크를 queue에 전달하여 그 메세지는 Looper에 의해 꺼내져 해당 동작을 수행하게 됩니다. 이게 다 입니다! ^^; 참 쉽죠잉. 

 

자 그럼 저기서 Handler는 누구의 Handler 입니까? 

 

누구긴요. ㅎㅎ new를 실행한 쓰레드의 핸들러지요. 이제 이해가 좀 되시죠? 우리가 따로 쓰레드를 생성하지 않았다면 저건 메인쓰레드의 Handler가 분명합니다. 자 그럼 좀더 비동기적인 접근을 위해 백그라운드 쓰레드를 운용하여 handler를 사용하는 방법을 알아보겠습니다. 

 

 

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
...
 
 
 
private static class LooperThread extends Thread{
    public Handler mhandler;
 
    @Override
    public void run() {
        Looper.prepare(); //핸들러와 루퍼 큐의 연결
        mhandler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if(msg.what == 0){
                    doLongRunningOperation();
                }
            }
        };
 
        Looper.loop(); //루퍼 시작 (run이 끝나지 않음을 보장하는 차단적 메서드)
    }
 
 
    private void doLongRunningOperation(){
        // 동작 구현 ...
    }
}
cs

 

 

액티비티 안에 간단한 이너 클래스를 구현하였습니다. 쓰레드를 상속하며 내부적으로 핸들러를 구현하고 있죠. 여기서 특이한 점은 run메서드 안에 있는 Looper.prepare() 와 Looper.loop() 입니다. 이 메서드 들을 통해 핸들러와 looper, queue가 유기적으로 결합되어 우리가 보내는 핸들러 메세지를 지속적으로 받을수 있게 됩니다. 

 

그렇다면 우리가 메인쓰레드에서 핸들러를 사용할때는 왜 이런 일련의 작업들을 해주지 않은것이죠?? 

 

메인쓰레드의 경우 다른 쓰레드와 다르게 좀 특이한데요. 메인쓰레드는 태생적으로 Looper에 대한 셋팅을 자동으로 가지고 있습니다.그래서 우리가 해줄 필요가 없는것이죠. 또한 메인루퍼는 전역적인 접근( getMainLooper() 메서드를 통해 )이 가능한 유일한 놈이죠. 어디서든 메인루퍼로의 접근이 가능합니다. 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private LooperThread mlooperThread;
private Button btnClick;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_basic_handler);
 
    btnClick = findViewById(R.id.btnClick);
    btnClick.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View view) {
            //루퍼 쓰레드에게 (핸들러를 통해)임무 전달
            //그러면 그 핸들러가 자신의 큐에 메세지를 넣고 그 루퍼가 메세지를 꺼내 실행한다
            mlooperThread.mhandler.obtainMessage();
            mlooperThread.mhandler.sendEmptyMessage(0);
        }
    });
 
    //루퍼스레드 동작
    mlooperThread = new LooperThread();
    mlooperThread.start();
}
cs

 

 

비동기 방식의 핸들러 사용을 정의 했으니, 이제 사용해 봐야죠. 화면에 버튼을 만들고 그에 따른 동작을 구현합니다. 화면이 그려질때 루퍼쓰레드를 start 해주는것도 잊으면 안되겠죠? 버튼을 누르면 작업쓰레드의 핸들러로 빈 메세지 하나를 전달합니다. 그럼 그 메세지는 큐에 저장되고 루퍼는 그걸 다시 핸들러에게 전달하여 드디어 메세지에 해당하는 동작이 실행되어 집니다. 이렇게 우리는 백그라운드 쓰레드를 사용하여 비동기적인 방식으로 작업을 할 수 있게 되었습니다. 다음은 안드로이드에서 제공하는 핸들러 쓰레드를 사용하여 좀 더 편리하게 작업하는 방법을 알아보겠습니다. 그럼 이만!

 

본 글은 인터넷, 책, 등으로 습득한 지식을 토대로 내용을 전달하고자 하며 개인적으로는 정리하는 개념으로 사용하고자 작성되었습니다. 틀린점이 있을 수 있다는 점 양해 부탁 드리며, 그저 참고용 으로 봐주시면 감사하겠습니다.