ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 Looper, Message Queue, Handler
    안드로이드 2020. 5. 9. 23:31
    728x90

    안드로이드 멀티스레드 프로그래밍에 대해 제대로 이해하려면 Looper 에 대해서 제대로 이해하고 있어야해요

     

    구조

    Looper 구조

    메인스레드 내부적으로 Looper 객체가 있습니다.

    Looper 는 무한 루프를 돈다고하여 Looper 라는 이름이 붙었는데요

    무한 루프를 돌면서 하는 일이 뭘까요?

    하는 일

    메시지 큐에서 메시지를 꺼내어 읽고, 처리한다.

     

    자 그럼 메시지 큐란게 뭔지 알아야겠죠?

     

    메시지 큐

    메시지 큐

    Looper 내부에 있는 Message Queue 는 메시지를 관리해주는 객체에요

    그럼... 메시지는 뭔데...?

    메시지

    안드로이드에서 하나의 작은 처리 단위라고 생각하시면 돼요. 메시지 큐에 메시지를 넣으려면 Handler 객체를 사용해야 합니다.

    핸들러 객체 구조

    Handler 안에 Message 객체와 Looper 객체가 있습니다.

    Message 객체 안에는 Runnable 객체와 Handler 객체가 있구요

    Looper 객체는 자신이 어느 Looper 에 메시지를 전달해야하는지 그 Looper 의 참조를 갖고 있어요 (기본 = 메인 스레드 루퍼)

     

    Handler 안에 Handler 객체가 있는게 좀 이상하죠?

    Handler 는 Looper 에게 메시지를 전달하는 역할도 하지만 본인 스스로가 메시지가 되기도 합니다.

     

    그럼 Message 안에 Runnable 과 Handler 의 역할은 뭘까요?

    Looper 객체가 메시지를 딱 꺼냈을 때 Runnable 객체를 먼저 열어봐요

    이 때 Runnable 객체가 null 이 아니면 run()을 실행 시키고, Handler 객체는 열어보지도 않아요.

    반면 Runnable 객체가 null이면 Handler 객체를 열어서 handleMessage() 를 실행시켜요

     

    예제로 볼게요

    <layout/activity_main.xml>

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/btn"
            android:text="버튼"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    
    </LinearLayout>

     

    <MainActivity.java>

    package kr.co.sample;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.widget.Button;
    
    import androidx.appcompat.app.AppCompatActivity;
    
    public class MainActivity extends AppCompatActivity {
    
        Handler handler = new Handler();
        Button btn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            btn = findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            for (int i = 0; i < 10; i++) {
                                final int I = i;
                                
                                // 메시지 객체를 얻어서 Runnable 객체를 설정
                                Message message = Message.obtain(handler, new Runnable() {
                                    @Override
                                    public void run() {
                                        btn.setText(String.valueOf(I));
                                    }
                                });
                                
                                // 핸들러를 통해 메시지 전송
                                handler.sendMessage(message);
    
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                }
            });
        }
    }
    

     

    Message.obtain(Handler handler, Runnable runnable);

    handler : 본 메시지를 받게 될 핸들러 객체

    runnable : Looper 가 실행시킬 Runnable 객체

     

    즉 obtain 메소드에서 handler 는 메시지 내부의 작업을 명시하기 위한것이 아니라 메시지를 Looper 에게 전달하기 위한 Handler 를 담는 것입니다.

    handler.sendMessage(message);

    sendMessage 메소드를 통해서 handler 내부의 Looper 로 메시지를 전송하게 됩니다.

    그러면 Looper 가 무한 루프 돌면서 메시지를 하나씩 처리해 나갈거고 어느 순간에는 방금 보낸 Message 가 처리되는거죠.

    중요한 점은 즉시 처리되는게 아니라 어느 순간에 처리된다는거에요. 가끔 이걸 모르고 반응이 빨라야 하는 작업을 여기서 처리하는 경우가 있는데 이러면 안됩니다.

     

    Handler 에서 post 로 시작하는 메소드들은 Message 객체 내부에 Runnable 객체를 넣어서 실행하는 것이고,

    send 로 시작하는 함수들은 메시지를 직접 보내는 메소드들입니다.

     

    Runnable 객체를 세팅하는 예제는 해봤으니 Handler 로 직접 보내는 예제 보고 마칠게요

     

    <MainActivity.java>

    package kr.co.sample;
    
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.view.View;
    import android.widget.Button;
    
    import androidx.annotation.NonNull;
    import androidx.appcompat.app.AppCompatActivity;
    
    public class MainActivity extends AppCompatActivity {
    
        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(@NonNull Message msg) {
            	// sendMessage(message) 하면 호출되는 메소드
                btn.setText(String.valueOf(msg.what));
                return true;
            }
        });
        Button btn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            btn = findViewById(R.id.btn);
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    new Thread(new Runnable() {
                        @Override
                        public void run() {
                            for (int i = 0; i < 10; i++) {
                                final int I = i;
                                
                                // message에 handler의 what 에 I를 넣어줌
                                Message message = Message.obtain(handler, I);
                                handler.sendMessage(message);
    
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }).start();
                }
            });
        }
    }
    

     

    '안드로이드' 카테고리의 다른 글

    안드로이드 CountDownTimer  (0) 2020.05.13
    Android AsyncTask  (0) 2020.05.10
    안드로이드 핸들러  (0) 2020.05.09
    안드로이드 멀티스레드  (0) 2020.05.09
    안드로이드 스레드  (0) 2020.05.09

    댓글

Designed by Tistory.