생각자유의 안드로이드 이야기

안드로이드 looper, handler, thread가 무엇인지? 본문

Android/Tip

안드로이드 looper, handler, thread가 무엇인지?

생각자유 2017. 1. 26. 11:45
336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

소소하게 Youtube채널을 개설하였습니다.

좋아요 및 구독 부탁드리겠습니다. (곧 안드로이드 관련 많은 자료들을 올릴 예정입니다.)

https://www.youtube.com/channel/UCwgElJMunsiDF8P2UOzjx2g/



오늘은 안드로이드의 기초이자 꼭 사용을 하는 백그라운드에 필요한 녀석들에 대해서 다루어 보고자 합니다.


안드로이드는 메인쓰레드 안에서만 UI를 변경 할 수 있습니다.


당연한 이유겠지만 만일 작업쓰레드를 메인에서 돌리게 되면 작업쓰레드가 끝날떄까지 앱이 멈춰 있고


메인 쓰레드가 아닌 다른 쓰레드에서 동시에 ui변경을 하게 하면 동기화 이슈도 발생을 하게 됩니다.


이에 초기에는 많은 문제가 있었으나 지금은 ui변경은 메인 쓰레드에서만, 오랜 시간이 걸리는 작업은 작업쓰레드에서 


하는걸로 변경되었습니다.


그럼 작업쓰레드에서 작업하고 ui를 변경하려면 어떻게 해야 하는가?


간략한 예제와 설명을 통해서 알아보고자 합니다.



looper와 Handler



항상 개발할땐 공식문서를 참조하는게 좋습니다.


[looper]

https://developer.android.com/reference/android/os/Looper.html


[Handler]

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



간단히 말씀드리면 looper는 사전 어원을 보면 1. 고리를 만드는 사람이라고 명시가 되는것처럼..


먼가 계속 반복되는 단어 느낌이지요? 맞습니다. 


looper는 쓰레드에 통해서 생성되고 계속 적으로 반복을 하면서 어떠한 일을 처리합니다.


그 일은 바로 메시지큐를 읽어오는 일입니다.


안드로이드 프로세스엔 1개의 메인쓰레드가 있고 메인 쓰레드는 내부적으로 looper를 생성해서 MessageQueue에 Handler를 통해서 데이터를 읽고 그것을 다시 Handler를 통해서 내려줍니다.


그럼 Handler는 뭘까요?


간단히 말씀드리면 MessageQueue에 보낼 데이터를 넣고 Looper를 통해서 처리할 데이터를 받고 보내는 중간에 보르커 같은 역활을 하는 녀석입니다.


그리고 Hander는 하나의 쓰레드와 바인딩이됩니다.


이제 예제를 보면서 이해를 하시면 쉽게 이해가 되실겁니다.


이번 예제를 쓰레드를 쓰지 않고 메인쓰레드에 긴 작업 쓰레드를 돌리면 어떻게 되는지 먼저 봅시나.


EmptyActivity를 하나 만듭니다.


메인 레이아웃에


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="sourceforge.net.myapplication.MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />

<Button
android:layout_marginTop="50dp"
android:id="@+id/btnOk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView"
android:text="ButtonClick" />
</RelativeLayout>



이제 클릭이벤트를 만들어 봅시다.


TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = (TextView)findViewById(R.id.textView);

Button button = (Button)findViewById(R.id.btnOk);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int count = 0;
while (count < 10000000){
count++
;
}

textView.setText("End...");
}
})
;
}

버튼 크릭을 해보시면 반복문이 끝날때까지...멈춰 있는걸 볼 수 있습니다.


안드로이드에서는 멈춰 있으면 ANR이 발생할 가능성이 큽니다.


그럼 이걸 쓰레드로 바꾸어 보겠습니다.



다시 예제를 보시면

TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = (TextView)findViewById(R.id.textView);

Button button = (Button)findViewById(R.id.btnOk);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Runnable runnable = new Runnable() {
@Override
public void run() {
int count = 0;
while (count < 1000000){
count++;
}

textView.setText("End...");
}
};

Thread thread = new Thread(runnable);
thread.start();
}
});
}

텍스트뷰에 end가 찍히는 걸 볼 수 있습니다.


그런데 앱이 예외를 발생하네요?


왜 일까요???


바로 서두에 말씀드린 작업쓰레드에서 텍스트뷰에 접근 하려고 한 이유때문입니다. UI변경을 오직 메인 쓰레드에서만 할 수 있습니다.


그러면 어떻게 해야 될까요?


그래서 Looper랑 Handler를 공부하신겁니다.


다시 핸들러를 추가한 예제를 보시면


TextView textView;
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
textView.setText("End...");
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

textView = (TextView)findViewById(R.id.textView);

Button button = (Button)findViewById(R.id.btnOk);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Runnable runnable = new Runnable() {
@Override
public void run() {
int count = 0;
while (count < 1000000){
count++;
}
handler.sendEmptyMessage(0);

}
};

Thread thread = new Thread(runnable);
thread.start();
}
});
}


이제 앱이 안죽고 잘 됩니다.


핸들러를 생성하고 Handler에 SendEmptyMessage라고 메서드를 호출 했습니다.


공식문서에서 메서드 설명를 보면

Sends a Message containing only the what value.

라고 되어있습니다. id같은개념이라 보시면 되고요.


가장 중요한건 


핸들러 안에 오버라이드한 handleMessage입니다.


Looper가 MessageQueue에서 꺼내서 Handler에게 처리해 달라 메시지를 보낼때 바인딩 되는 메서드립니다.


꼭 구현을 해야 메시지큐에 값을 처리 할 수 있겠죠?


오늘은 간단하게 알아봤구요.


다음에는 좀더 심화 과정을 알아 보겠습니다.


Comments