android 비동기 메시지 메커니즘 소스 차원 철저히 분석(1)
우선 우리 가 평소에 Handler 를 사용 하 는 가장 흔 한 용법 을 살 펴 보 자.
Handler handler =new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// UI
}
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
........
handler.sendMessage(message);
}
});
};
handler 의 구조 함수 의 소스 코드 를 보 세 요.
public Handler() {
this(null, false);
}
//
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
mLooper==null 을 볼 때"Can't create handler inside thread that has not called Looper.prepare()"라 는 이상 을 던 집 니 다.따라서 handler 인 스 턴 스 를 만 들 기 전에 먼저 Looper.prepare()를 호출 해 야 합 니 다.
public static void prepare() {
prepare(true);
}
// looper ThreadLocal , ThreadLocal Map, looper
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
// new Looper(quitAllowed) , MessageQueen
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
다음은 handler.sendmessage(message)라 는 방법 을 살 펴 보 겠 습 니 다.문자 그대로 메 시 지 를 보 내 는 것 입 니 다.일반적으로 sendmessage 힘 든 방법 은 결국 sendmessage AtTime(Message msg,long uptime Millis)이라는 방법 을 호출 합 니 다.
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
우 리 는 최종 적 으로 enqueueMessage(queue,msg,uptime Millis)라 는 방법 을 실행 하 는 것 을 보 았 다.
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
마지막 으로 Message Queen 의 quue.enqueue Message(msg,uptime Millis)방법 을 호출 합 니 다.여기 있 는 quue 는 looper 구조 방법 에서 만 든 메시지 대기 열 입 니 다.
//MessageQueen enqueueMessage
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
Message Queen 은 이름 이 하나의 대기 열 이지 만 실질 적 으로 그 는 단 방향 링크 입 니 다.이 구 조 는 빠 른 삽입 과 삭제 작업 을 할 수 있 습 니 다.위의 소스 코드 에서 알 수 있 듯 이 주로 메 시 지 를 보 내 는 시간 순서에 따라 msg 를 메시지 대기 열 에 삽입 합 니 다.이제 우 리 는 메시지 대기 열 에서 msg 를 꺼 내야 한다.이 럴 때 는 Looper.loop()방법 을 사용 해 야 합 니 다.
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
// msg
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// msg handler
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
Looper.loop()방법 을 볼 수 있 습 니 다.quue.next()방법의 역할 은 메시지 대기 열 에서 msg 를 가 져 오 는 것 입 니 다.순환 을 뛰 어 내 리 는 유일한 방법 은 Message Queen 의 next 방법 으로 null 을 되 돌려 줍 니 다.현재 msg 는 이미 꺼 냈 습 니 다.다음 단 계 는 어떻게 그 를 handler 에 전달 하 는 것 입 니까?그래서 죽은 순환 에서 msg.target.dispatchMessage(msg)방법 도 있 습 니 다.msg.target 은 handler 입 니 다.위 handler 의 enqueueMessage()방법 에 들 어 오 는 msg.target=this,this 는 handler 자체 입 니 다.다음은 handler 의 dispatchMessage()방법 을 살 펴 보 겠 습 니 다.
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
만약 에 우리 가 인삼 이 없 는 구조 함수 로 handler 를 만 들 면 msg.callback 과 mCallback 이 모두 비어 있 기 때문에 우 리 는 handle Message(msg)를 호출 할 것 입 니 다.그러면 글 에서 시작 하 는 인 스 턴 스 의 전체 절 차 는 끝 납 니 다.handle Message(msg)는 handler 인 스 턴 스 가 있 는 스 레 드 에서 실 행 됩 니 다.
// handler ,dispatchMessage mCallback null
public Handler(Callback callback) {
this(callback, false);
}
//Callback , handleMessage(Message msg),dispatchMessage if (mCallback != null) , handleMessage(Message msg)
public interface Callback {
public boolean handleMessage(Message msg);
}
// handler.post()
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//getPostMessage(r) m.callback , runnable
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
// handleCallback run , handler.post(Runnable r) UI
private static void handleCallback(Message message) {
message.callback.run();
}
총결산전체 집행 과정 을 정리 하 다.
1.Looper.prepare()방법 을 호출 합 니 다.이것 은 handler 를 만 드 는 데 필요 한 것 입 니 다.메 인 스 레 드 에 서 는 Activity Thread 가 Looper.prepareMainLooper()방법 으로 looper 를 만 들 었 기 때문에 메 인 스 레 드 에 handler 를 만 들 기 전에 looper 를 만 들 필요 가 없 으 며,Looper.loop()을 통 해 메 인 스 레 드 의 메시지 순환 을 엽 니 다.
2.handler.sendmessage(message)를 호출 하 는 방법 으로 최종 적 으로 enqueueMessage(queue,msg,uptimeMillis)를 실행 하고,enqueueMessage 는 MessageQueen 의 queue.enqueueMessage(msg,uptimeMillis)를 호출 합 니 다.그러면 메시지 대기 열 에 메시지 가 추 가 됩 니 다.
3.Looper.loop()방법 을 호출 하여 죽은 순환 에서 Message msg=quue.next()를 실행 하고 msg 를 메시지 대기 열 에서 계속 꺼 내 는 동시에 msg.target.dispatchMessage(msg)를 실행 하여 handler 에 메 시 지 를 전달 하고 handler 에서 처리 합 니 다.예 를 들 어 우리 가 호출 한 handleMessage 는 메 시 지 를 처리 하 는 방식 중 하나 입 니 다.
비동기 처리 메커니즘 흐름 도
하위 스 레 드 에서 UI 작업 을 하 는 몇 가지 방식
Android 는 다른 스 레 드 에서 UI 스 레 드 에 접근 할 수 있 는 몇 가지 경 로 를 제공 합 니 다.아래 에 몇 가지 유용 한 방법 을 제시 하 였 다.
• Activity.runOnUiThread(Runnable)
•View.post(Runnable)여기 view 는 우리 가 바 꿔 야 할 ui 컨트롤 입 니 다.
• View.postDelayed(Runnable, long)
• Handler.post(Runnable, long)
그러나 조작 이 갈수 록 복잡 해 지면 서 이런 코드 도 복잡 해 지고 유지 하기 어려워 질 것 이다.작업 스 레 드 를 통 해 더욱 복잡 한 상호작용 을 처리 하려 면 작업 스 레 드 에서 Handler 를 사용 하여 UI 스 레 드 에서 온 메 시 지 를 처리 하 는 것 을 고려 할 수 있 습 니 다.물론 가장 좋 은 해결 방안 은 AsyncTask 류 를 확장 하 는 것 일 수도 있 습 니 다.UI 와 상호작용 을 하 는 데 필요 한 작업 스 레 드 작업 을 간소화 합 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.