runOnUiThread 、Handler.post、View.post 분석
예제
먼저 다음 코드를 보고 출력 결과를 판단합니다.
public class MainThreadTestActivity extends AppCompatActivity {
private static final String TAG = MainThreadTestActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_thread_test);
View view = new View(this);
view.post(new Runnable() {
@Override
public void run() {
Log.i(TAG, "[view.post] >>>> 1 ");
}
});
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
Log.i(TAG, "[handler.post] >>>> 2");
}
});
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "[runOnUiThread] >>>>> 3");
}
});
new Thread(new Runnable() {
@Override
public void run() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "[runOnUiThread from thread] >>>> 4");
}
});
}
}).start();
}
}
우선 출력 결과가 어떻게 될지 예측해 봅시다.
실행 결과는 다음과 같습니다.
...I/MainThreadTestActivity: [runOnUiThread] >>>>> 3
...I/MainThreadTestActivity: [handler.post] >>>> 2
...I/MainThreadTestActivity: [runOnUiThread from thread] >>>> 4
그럼 질문이 왔습니다.
View.post
은 왜 실행되지 않았습니까?runOnUiThread
이 Handler.post
보다 빨리 실행합니까?runOnUiThread
, Handler.post
, View.post
이 세 가지의 집행 순서는 어떻게 될까요?다음은 우리가 각각 해석을 진행한다.
해석
2.1 View.post
2.1.1 View.질문
먼저
View.post
소스를 살펴보겠습니다.//View.java
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}
즉,
View.post
방법을 집행할 때 AttachInfo
이 비어 있지 않으면 AttachInfo
의 Handler
을 통해 Runnable
을 집행한다.그렇지 않으면 이 Runnable
을 View
의 집행대열 HandlerActionQueue
에 던진다.void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
//...
}
즉
View
attch가 Window
에 이르러야만 AttachInfo
에 값을 부여할 수 있다.따라서 예시된 코드는 getRunQueue().post(action)
으로 바로 들어간다.우리는 계속해서 원본 코드를 따라 내려다보았다.View
에서 HandlerActionQueue
을 통해 실행 가능한 요청 대기열을 봉인했다.공식 주석은 Class used to enqueue pending work from Views when no Handler is attached.
, 즉view는 Handler
이 후속 임무(attach에서 Window
에 없음)를 수행하기 위해 모든 요청을 입대하지 않았다는 것이다.// HandlerActionQueue.java
public class HandlerActionQueue {
public void removeCallbacks(Runnable action) {
synchronized (this) {
final int count = mCount;
int j = 0;
final HandlerAction[] actions = mActions;
for (int i = 0; i < count; i++) {
if (actions[i].matches(action)) {
// Remove this action by overwriting it within
// this loop or nulling it out later.
continue;
}
if (j != i) {
// At least one previous entry was removed, so
// this one needs to move to the "new" list.
actions[j] = actions[i];
}
j++;
}
// The "new" list only has j entries.
mCount = j;
// Null out any remaining entries.
for (; j < count; j++) {
actions[j] = null;
}
}
}
public void executeActions(Handler handler) {
synchronized (this) {
final HandlerAction[] actions = mActions;
for (int i = 0, count = mCount; i < count; i++) {
final HandlerAction handlerAction = actions[i];
handler.postDelayed(handlerAction.action, handlerAction.delay);
}
mActions = null;
mCount = 0;
}
}
}
View
에서void dispatchAttachedToWindow(AttachInfo info, int visibility) {
...
// Transfer all pending runnables.
if (mRunQueue != null) {
mRunQueue.executeActions(info.mHandler);
mRunQueue = null;
}
...
}
요약하면
View view = new View(this);
이view attch를 window에 저장하지 않았기 때문에 실행 가능한 View.post
방법은 실행 가능한 요청을 요청 대기열에 캐시합니다.따라서 예제의 코드는 다음과 같이 변경될 수 있습니다.
View view = new View(this);
rootView.addView(view);
view.post(new Runnable() {
출력 결과는 다음과 같습니다.
...I/MainThreadTestActivity: [runOnUiThread] >>>>> 3
...I/MainThreadTestActivity: [handler.post] >>>> 2
...I/MainThreadTestActivity: [runOnUiThread from thread] >>>> 4
...I/MainThreadTestActivity: [view.post] >>>> 1
View.post
방법을 성공적으로 집행하여 정확하게 수정하였다.View.post()
은 View
attachedToWindow
에만 즉시 실행2.1.2 View.post 원본 분석
2.1.1
절을 통해 View.post()
은 View
attachedToWindow
에서만 즉시 실행된다는 것을 알 수 있습니다.ViewRootImpl
의 ViewRootHandler
을 실행하여2.2 runOnUiThread
//Activity.java
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
참고:
UI
스레드의 시간 대기열로 발송됩니다.즉, 주 라인이 아닌 라인에서 이 방법을 사용하면 라인 전환 비용이 존재한다.
2.3 Handler.post
먼저 요청을
Handler
대기열에 추가하고 실행해야 합니다.3. 집행 순서 분석
요약하면 주 라인에서 각각
View.post
, Handler.post
, runOnUiThread
, new Thread() - [runOnUiThread]
네 가지 방법의 집행 순서는 속도에서 느림으로 나뉜다.runOnUiThread
- Handler.post
- new Thread() - [runOnUiThread]
- View.post
(전면 유효성 검사 결과 일치)
분석:
runOnUiThread
현재 UI
에서 실행되고 있으므로 스레드 전환 없이 Handler.post
가입 요청UI Handler
새 스레드를 오픈, 시작 완료 후 new Thread() - [runOnUiThread]
가입 요청, 스레드 전환, 입대 및 출전 시간 UI Handler
은view attach에서 Window에 도착한 후 View.post
의 ViewRootImpl
을 통해 요청을 수행해야 한다.스레드 전환 시간이 UI 렌더링 시간보다 훨씬 작기 때문에 최대 ViewRootHandler
과 runOnUiThread
은 비용이 거의 같기 때문에 실행 결과도 순서대로 완성된다.그러므로 UI가 아닌 라인에서 각각 Handler.post
, View.post
, Handler.post
을 호출하면 runOnUiThread
네 가지 방법의 집행 순서는 속도에서 느림으로 한다.new Thread() - [runOnUiThread]
- Handler.post
- runOnUiThread
- new Thread() - [runOnUiThread]
View.post
이 제기한 발산 문제에 감사 드립니다이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.