정상에서 IT를 외치다

[Android, Thread] Thread 넌 무엇이더냐? 본문

안드로이드

[Android, Thread] Thread 넌 무엇이더냐?

Black-Jin 2019. 3. 26. 00:00
반응형


안녕하세요. 블랙진입니다.


안드로이드 개발을 하게되면 Thread(스레드)를 상황에 맞게 잘 사용해야 멋진 앱을 개발할 수 있습니다. 그럼 이 스레드란게 무엇일까요?


스레드란?



thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently. - 구글문서


프로그램 안에서의 실행을 담당하는 하나의 흐름입니다. Thread 는 한국어로 실을 뜻 합니다. 실처럼 한가닥의 실행 혹은 작업을 담당하는 역활을 하죠. JVM 은 어플리케이션이 동시에 수행할 수 있는 멀티 스레드를 할당할 수 있게 해줍니다. 이를 통해 어플리케이션은 동시에 여러 작업을 수행할 수 있게 됩니다.


JAVA 에서는 프로그램이 실행되면 최초의 스레드가 Main 스레드가 되어 실행됩니다. 이 메인스레드를 통해 여러 작업 스레드를 생성할 수 있습니다. 그러면 스레드를 어떻게 생성하는지 알아보겠습니다.



스레드를 생성하는 방법


1. Thread 클래스 사용


2. Runnable 인터페이스 사용



예제


activity_main.xml


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:id="@+id/linearLayout"
android:layout_centerInParent="true"
android:orientation="horizontal"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<TextView
android:id="@+id/mainText"
android:text="0"
android:textSize="30sp"
android:layout_marginRight="20dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/subText"
android:text="0"
android:textSize="30sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

<Button
android:id="@+id/btnIncrease"
android:text="increase"
android:layout_below="@+id/linearLayout"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</RelativeLayout>

두 개의 TextView 와 한 개의 Button 이 있습니다. 



MainActivity.class

public class MainActivity extends AppCompatActivity {

private int mMainValue = 0;
private int mSubValue = 0;

private TextView mainText, subText;

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

mainText = findViewById(R.id.mainText);
subText = findViewById(R.id.subText);

findViewById(R.id.btnIncrease).setOnClickListener(view -> {
mMainValue++;
mainText.setText(String.valueOf(mMainValue));
subText.setText(String.valueOf(mSubValue));
});
}
}

MainActivity 의 동작은 위와 같습니다. btnIncrease 를 누르면 mMainValue 값이 1씩 증가하게 되고 main 과 sub TextView에 값을 보여줍니다. 여기서 mMainValue 만 값이 증가되니 mSubValue 의 값을 작업 스레드를 만들어 증가시켜 보겠습니다.


1. Thread 클래스를 사용해 보자


MainActivity 내부에  subThread 클래스를 새로 만들어 줍니다. 이 클래스는 Thread 를 상속하고 있습니다.

class SubThread extends Thread {

@Override
public void run() {
while (true) {
mSubValue++;

try {
//1초의 간격을 줍니다.
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

MainActivity의 onCreate 안에 SubThread 를 생성 후 실행해 줍니다.

SubThread subThread = new SubThread();
//setDaemon를 설정하여 mainThread와 종료 동기화를 해줍니다.
//이렇게 안해줄 경우 어플이 종료 되어도 작업스레드는 계속 동작하게 됩니다.
subThread.setDaemon(true);
subThread.start();


2. Runnable 인터페이스를 사용해 보자


1번에서 상속하는 클래스만 Runnable 인터페이스로 변경해 줍니다.

class SubRunnable implements Runnable {

@Override
public void run() {
while (true) {
mSubValue++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

MainActivity의 onCreate 안에 Thread 를 생성 후 실행해 줍니다.

SubRunnable subRunnable = new SubRunnable();
Thread thread = new Thread(subRunnable);
thread.setDaemon(true);
thread.start();

1번의 방법이 동일하나 Thread 클래스를 생성한 후 생성자 파라미터로 Runnable 을 넣어 줍니다.



2-1. Runnable 인터페이스를 익명 클래스로 사용해 보자

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
mSubValue++;

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.setDaemon(true);
thread.start();

SubRunnable 클래스를 따로 생성하지 않고 new Runnable() 를 통해 익명 클래스로 만들어 보았습니다. 역시 동작은 모두 동일합니다.

이렇게 스레드를 생성하는 2가지 방법을 모두 살펴보았습니다.



3. 작업 스레드에서는 UI 작업을 할 수 없다

 

위 에제에서 버튼을 누르면 TextView에 변수 값이 문제없이 들어갑니다. 이는  메인스레드(UI 스레드)에서 View 에 접근했기 때문입니다. (View는 무조건 메인스레드를 통해서만 변경이 가능합니다)  Activity 가 실행되면 그 안에서 작업 스레드를 새로 생성하지 않은 이상 처리하는 모든 과정은 메인스레드에서 일어난다고 생각하시면 됩니다. 만약 우리가 만든 SubThread 에서 View 에 접근하게 되면 어떻게 될까요?

Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
mSubValue++;
//작업 스레드에서 view 에 접근하면 무슨일이 일어날까?
subText.setText(String.valueOf(mSubValue));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.setDaemon(true);
thread.start();

아래 에러와 함께 앱이 폭파됩니다...(펑)


android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.


에러를 보시면 생성된 original thread 에서만 view 를 건드릴 수 있다는 내용입니다. 이렇듯 View의 접근은 오직 메인스레드에서만 할 수 있게 되어 있습니다. 하지만 개발을 하다보면 작업 스레드에서 메인 스레드로 접근해야 되는 일이 발생할 수 있습니다. 그럼 이럴때 어떻게 해야될까요?


정답은 Handler 를 사용하는 것입니다

이와 관련되어 Handler 넌 무엇이더냐? 에서 이어서 포스팅을 진행하겠습니다.


<참고자료>

구글 문서

Thread 예제

반응형
Comments