Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature/WEEK 2] WEEK2 part2 #10

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions Week2/SIBA WEEK 2 Part 1.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# 메인 스레드와 Handler - Handler와 Looper 그리고 MessageQueue의 동작 방식

> Handler와 Looper 그리고 MessageQueued의 의 동작 방식을 안드로이드 프레임 워크를 통해 알아봅니다.

안드로이드의 메인스레드인 ActivityThread.java 를 이해하는데에 큰 배경 지식이 된다는 점에서 Handler와 Looper 그리고 MessageQueue의 동작 방식을 이해하는 것은 큰 가치가 있는 일입니다. 스레드 통신과 약간의 자료구조에 대한 부분까지 생각해보는 좋은 기회가 될 것입니다. 따라서 이번 아티클에서는 안드로이드 프레임워크의 코드와 함께 Handler와 Looper 그리고 MessageQueue의 동작 방식에 대해 깊게 알아보겠습니다.


Expand Down Expand Up @@ -141,7 +143,7 @@ public static void loop() {

[android.os.HandlerThread](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/HandlerThread.java?q=android.os.HandlerThread&hl=ko) 에선 Looper에 대한 널 체크를 한후, Looper에 정의된 동명의 함수를 호출합니다.

```
```java
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
Expand All @@ -164,7 +166,7 @@ public static void loop() {

물론 Looper.quit()과 Looper.quitSafely() 동작은 MessageQueue.quit() 함수의 boolean 형의 매개변수 값으로 분기 처리됩니다.

```
```java
public void quit() {
mQueue.quit(false);
}
Expand All @@ -184,7 +186,7 @@ public static void loop() {
> 무한 루프를 돌며 기존 Looper의 작업을 재개합니다.
> 다만, 작업 재개에 있어 [uptimeMillis](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/SystemClock.java;drc=master;l=178?hl=ko)()과 when을 비교하는데, 만약 when이 [uptimeMillis](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/SystemClock.java;drc=master;l=178?hl=ko)()보다 더 미래일 경우 작업을 멈추고, MessageQueue의 남은 메세지에 대해 [recycleUnchecked](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Message.java;drc=master;l=324?hl=ko)()를 호출합니다.

```
```java
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
Expand Down Expand Up @@ -229,7 +231,7 @@ MessageQueue는 Message를 담고 있는 자료구조라 말씀드렸습니다.

먼저 [Message 클래스](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/os/Message.java?q=public%20final%20class%20Message%20implement%20Parcelable&ss=android&hl=ko) 에는 핸들러가 스레드간의 통신 매개로서 전달해야할, (다른 스레드에서 실행될) 작업에 대한 정보를 담고있습니다.

```
```java
public final class Message implements Parcelable {
/**
* User-defined message code so that the recipient can identify
Expand Down
145 changes: 134 additions & 11 deletions Week2/SIBA WEEK 2 Part 2.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,148 @@
# 메인 스레드와 Handler Part2 - 안드로이드 애플리케이션에서의 메인 스레드

- 안드로이드 애플리케이션에서 Handler와 Looper 그리고 MessageQueue 구조가 어떻게 쓰이는지 알아봅니다.
> 안드로이드 애플리케이션에서 Handler와 Looper 그리고 MessageQueue 구조가 어떻게 쓰이는지 알아봅니다.

앞서 Part 1에서 알아보았던, Handler와 Looper 그리고 MessageQueue의 동작이 과연 왜 필요할까요? 바로 여러 스레드를 사용하는 멀티스레드 환경에서, 특정 스레드에게 일을 위임하기 위한 수단으로 의미가 있습니다.
앞서 Part 1에서 알아보았던, Handler와 Looper 구조와 동작이 과연 왜 필요할까요?

백그라운드 스레드가 (UI와 관련하여 단일 스레드 모델이 적용된다는 점에서) 여러 특권을 가진 메인스레드에게 일을 분담하기 위해, 기본적으로 사용되는 구조가 되는 것입니다. 이번 아티클에서는 과연 그 '특권'이란 무엇이고 왜 생겨났는지, 어떻게 사용되어야하는지에 대해 알고자 합니다. 안드로이드 프레임 워크의 관점에서, 메인 스레드의 의미와 역할에 대해 알아보겠습니다.
이는 여러 스레드를 사용하는 멀티스레드 환경에서, 특정 스레드에게 일을 위임하기 위한 수단으로 의미가 있습니다. 백그라운드 스레드가 특권을 가진 메인스레드에게 일을 분담하기 위해, 기본적으로 사용되는 구조가 되는 것입니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
이는 여러 스레드를 사용하는 멀티스레드 환경에서, 특정 스레드에게 일을 위임하기 위한 수단으로 의미가 있습니다. 백그라운드 스레드가 특권을 가진 메인스레드에게 일을 분담하기 위해, 기본적으로 사용되는 구조가 되는 것입니다.
이는 멀티스레드 환경에서, 특정 스레드에게 일을 위임하기 위한 수단으로 의미가 있습니다. 백그라운드 스레드가 **'특권'** 가진 메인스레드에게 일을 분담하기 위해, 기본적으로 사용되는 구조가 되는 것입니다.
  • 단어 중복을 최대한 자제해주시면 감사링
  • 따옴표를 빼면 특권이라는 단어 자체가 일반적인 의미를 가지기 때문에 여기서는 다른 의미를 주었다는 텐스를 주기 위해서 강조점을 주는게 좋을 것 같음

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

일을 분담한다는건 백그라운드 스레드가 그 일에 주도권을 가지고 있었는데 그걸 메인스레드에게 넘긴다라는 뜻도 내포되어있는 것 같습니다(뉘앙스). 메인스레드와 협업을 한다는 표현이 좀 더 적절할 수 있겠네요. 결국에는 스레드도 객체니까요.


UI 처리를 위한 메인 스레드
이번 아티클에서는 과연 그 '특권'이란 무엇이고 왜 생겨났는지, 어떻게 사용되어야하는지에 대해 알고자 합니다. 안드로이드 프레임 워크의 관점에서의 메인 스레드의 의미와 역할에 대해 알아보겠습니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
이번 아티클에서는 과연 그 '특권'이란 무엇이고 왜 생겨났는지, 어떻게 사용되어야하는지에 대해 알고자 합니다. 안드로이드 프레임 워크의 관점에서의 메인 스레드의 의미와 역할에 대해 알아보겠습니다.
이번 아티클에서는 과연 그 '특권'이란 무엇이고 왜 생겨났는지, 어떻게 사용되어야하는지에 대해 알고자 합니다. 안드로이드 프레임워크의 관점에서의 메인 스레드의 의미와 역할에 대해 알아보겠습니다.

Framework, not Frame Work


백그라운드 스레드에서 UI 업데이트
## 안드로이드의 메인 스레드

메인 스레드에서 다음 작업 예약
### 안드로이드의 메인 스레드가 하는 일

반복 UI 갱신
- 안드로이드의 메인스레드를 찾기 위해, 일반적인 프로그램의 시작점인 main()를 찾아보도록 하겠습니다. Android FW의 main()는 [ActivityThread.java](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java;l=247?q=ActivityThread&sq=&hl=ko) 에서 찾을 수 있습니다.

시간 제한
```java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

// Install selective syscall interception
AndroidOs.install();

// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);

Environment.initForCurrentUser();

// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);

// Call per-process mainline module initialization.
initializeMainlineModules();
Comment on lines +19 to +36
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 이 아티클과 관련이 덜한 부분이기 때문에 지우면 감사하겠습니다. (보여주고자 하는 부분만 제대로 보여주자는 의미)


Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

// Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
// It will be in the format "seq=114"
long startSeq = 0;
if (args != null) {
for (int i = args.length - 1; i >= 0; --i) {
if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
startSeq = Long.parseLong(
args[i].substring(PROC_START_SEQ_IDENT.length()));
}
}
}
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
```

ANR
- 안드로이드의 메인스레드는 Activity, Service, Broadcast Reciver, Application와 같은 컴포넌트 생명주기 메서드와 관련된 작업들을 기본적으로 담당하고 있습니다.

Invalidate
### UI 작업에는 단일 스레드 모델을 적용

- UI 작업에 필요한 UI 자원 공유를 하면서 발생할 수 있는 경합 상태, 교착 상태를 방지하고자, **메인스레드의 UI 작업에는 단일 스레드 모델**이 적용됩니다.

> 단일 스레드 모델은 자원 접근에 대한 동기화를 신경쓰지 않아도 되고, 작업전환(context switching) 비용을 요구하지 않으므로, 경합 상태와 교착 상태를 방지할 수 있다.

소결
- 즉, 안드로이드에서의 단일 스레드 모델이란 안드로이드 화면을 구성하는 뷰나 뷰그룹을 하나의 스레드(메인 스레드)에서만 담당하는 원칙을 말합니다. 단일 스레드 모델은 아래 두 가지 규칙을 갖습니다.

> 첫째, 메인 스레드(UI 스레드)를 블럭하지 말 것
>
> 둘째, 안드로이드 UI 툴킷은 오직 UI 스레드에서만 접근할 수 있도록 할 것

### 메인스레드와 백그라운드 스레드 통신을 위한 Handler와 Looper 구조

- 단일 스레드에서의 긴 작업은 어플리케이션의 반응성을 낮추거나, ANR의 원인이 될 수 있습니다. 따라서 메인 스레드에선 정해진 최소한의 일만 담당하고, 특히 긴 작업은 다른 스레드가 담당하게 해야합니다.
- 따라서 이 메인 스레드와 다른 스레드가 협업하기위해, **스레드간 통신**이 필요하게 되었습니다.
- 안드로이드에선 **Looper와 Handler를 사용하여, 다른 스레드와 메인 스레드간의 통신**을 할 수 있습니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 다른 스레드와 메인 스레드간 통신만 가능하다는 오해를 불러일으킬 수 있을 것 같네요(물론 그건 읽는 사람이 잘못한거지만)
우리는 친절한 시바놈이니까 스레드간의 통신을 사용할 때 Handler와 Looper를 사용하기에 메인스레드와 다른 스레드 간에 통신을 할 때에도 핸들러 루퍼구조를 활용한다 정도로 바꿔도 좋을 것 같습니다.

@jinsu4755 @SSong-develop @kym1924 이에대한 의견 부탁드립니다.

- 위 내용을 정리하면, **안드로이드에서의 Thread-Looper-Handler 구조는, 메인 스레드에 단일 스레드 모델이 적용되면서 요구되는 스레드간의 통신 방법을 지원하는 구조**라고 이해하면 좋습니다.

## Handler 사용 예시

### 백그라운드 스레드에서 UI 작업

- 앞서 설명 드렸듯 UI 작업은 단일 스레드 모델이 적용되어 메인 스레드에서만 실행됩니다. 따라서 네트워크 통신 같은 백그라운드 작업을 하면서 발생하는 UI 변경 작업은 Handler 사용하여 메인 스레드로 위임되고, 메인 스레드에서 실행됩니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이에 관련된 설명이 부족한 감이 없잖아 있습니다. Room에서 데이터를 빼오는 코드 정도만 있어도 이에 대한 보충 코드로 충분할 것 같습니다.


### 반복 UI 갱신를 위한 recursive Runnable

- 시계나 타이머처럼 반복적으로 UI 작업을 해야할 때가 있습니다. 이 UI 갱신 작업은 Runnable을 재귀적으로 설계하는 것으로 구현할 수 있습니다.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

구현화면 부착하면 더 좋을 듯?


```kotlin
package com.example.myapplication

import android.os.Bundle
import android.os.Handler
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity


class MainActivity : AppCompatActivity() {
private val DELAY_TIME = 2000L
private val handler = Handler(mainLooper)
private var updateTimeRunnable: Runnable? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
updateTimeRunnable = Runnable {
findViewById<TextView>(R.id.tv1).text = System.currentTimeMillis().toString()
updateTimeRunnable?.let { handler.postDelayed(it, DELAY_TIME) }
}

}


fun onClickButton(view: TextView){
updateTimeRunnable?.let { handler.post(it) }
}

}
Comment on lines +113 to +133
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good!


```



### 타이머, ANR 판단

-
Comment on lines +139 to +141
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
### 타이머, ANR 판단
-

delete plz


## 요약

- 안드로이드의 메인 스레드는 [ActivityThread.java](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/app/ActivityThread.java;l=247?q=ActivityThread&sq=&hl=ko) 에서 시작되며, 컴포넌트 생명주기 메서드와 관련된 작업들을 담당합니다.
- 경합 상태, 교착 상태를 방지하고자, UI 작업에는 단일 스레드 모델을 적용합니다. 따라서 UI 작업은 메인 스레드에서만 실행 가능합니다.
- UI 작업은 메인 스레드에서만 실행 가능하기 때문에, 작업에 따라 메인 스레드와 백그라운드 스레드간의 통신이 필요합니다. Handler와 Looper 구조는 스레드간 통신을 위한 구조 입니다.
- Handler 사용 (스레드간 통신)의 예시로는 백그라운드 스레드에서 UI 작업, UI 갱신, 시간 제한 작업이 있습니다.
Loading