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에 따라 라이센스가 부여됩니다.