runOnUiThread란 무엇인가?
예제를 살펴보기 전에 Android의 runOnUiThread()가 무엇인지 먼저 이해할 필요가 있습니다. 때때로 메인 스레드(Main Thread)는 무거운 작업을 수행하게 되는데, 이 상태에서 사용자가 추가적인 UI 작업을 요청하면 스레드에 부하가 걸려 ANR(Application Not Responding) 오류가 발생할 수 있습니다.
runOnUiThread()를 사용하면 워커 스레드(Worker Thread)에서 백그라운드 작업을 처리하고, 그 결과를 메인 스레드에서 안전하게 UI에 반영할 수 있습니다. 즉, 시간이 오래 걸리는 작업은 별도의 스레드에서 실행하되, 화면 갱신 같은 UI 작업만 메인 스레드로 전달하는 효율적인 방식입니다.
이 글에서는 실제 예제를 통해 Android에서 runOnUiThread를 사용하는 방법을 단계별로 알아보겠습니다.
1단계: 새 프로젝트 생성
Android Studio에서 File → New Project를 선택하고, 새 프로젝트 생성에 필요한 모든 세부 정보를 입력하여 프로젝트를 만듭니다.
2단계: 레이아웃 파일 작성
res/layout/activity_main.xml 파일에 다음 코드를 추가합니다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:id="@+id/parent"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/runOn"
android:text="Run"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/text"
android:textSize="20sp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
위 코드는 하나의 버튼과 텍스트 뷰(TextView)로 구성되어 있습니다. 버튼을 클릭하면 텍스트 뷰가 업데이트되는 구조입니다.
3단계: MainActivity 작성
src/MainActivity.java 파일에 다음 코드를 추가합니다.
package com.example.andy.myapplication;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
int i = 0;
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView textView = findViewById(R.id.text);
final Button runOn = findViewById(R.id.runOn);
runOn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("#" + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
});
}
}
핵심 코드 분석
위 코드에서 사용자가 버튼을 클릭하면, runOnUiThread()를 활용해 텍스트 뷰가 업데이트됩니다. 핵심 로직은 다음과 같습니다.
new Thread() {
public void run() {
while (i++ < 1000) {
try {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("#" + i);
}
});
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();코드의 동작 흐름을 살펴보면 다음과 같습니다.
- 버튼 클릭 시 새로운 스레드가 시작됩니다.
- 워커 스레드는 0부터 999까지 반복하며 매번 300밀리초(0.3초)씩 대기합니다.
- 각 반복마다 runOnUiThread()를 호출하여 카운터 값(#)을 메인 스레드의 텍스트 뷰에 표시합니다.
- 이렇게 하면 백그라운드 작업 중에도 UI가 멈추지 않고 부드럽게 갱신됩니다.
애플리케이션 실행 및 결과 확인
이제 애플리케이션을 실행해 보겠습니다. 실제 Android 모바일 기기가 컴퓨터에 연결되어 있다고 가정합니다. Android Studio에서 프로젝트의 액티비티 파일 중 하나를 연 뒤, 툴바에서 Run 아이콘을 클릭하세요. 그다음 모바일 기기를 실행 옵션으로 선택하면, 기기에 다음과 같은 초기 화면이 표시됩니다.

위 결과는 앱의 초기 화면입니다. 여기서 Run 버튼을 클릭하면 텍스트 뷰가 아래와 같이 실시간으로 업데이트되는 것을 확인할 수 있습니다.

마무리
runOnUiThread는 워커 스레드에서 수행한 작업 결과를 메인 스레드의 UI에 안전하게 반영할 수 있는 간편한 방법입니다. 다만 복잡한 비동기 처리에는 Handler, Executor, Kotlin Coroutines 등의 대안도 고려해 보는 것이 좋습니다. ANR 오류를 방지하고 쾌적한 사용자 경험을 제공하기 위해, 무거운 작업은 반드시 백그라운드 스레드에서 처리하는 습관을 들이시기 바랍니다.