Handler 메커니즘 상세 분석
13096 단어 Android
약술2: Hander 메커니즘의 네 가지 중요한 구성원: Looper Handler Message MessageQueue.대략적인 절차는 다음과 같다.
1.현재 라인에서 Looper 대상을 만들고 그 내부에 MessageQueue를 만들고 이 Looper 대상을 현재 라인의 sThreadLocal에 저장합니다.
2.현재 스레드에서 Hander 대상을 만들고 HandMessage () 방법을 다시 씁니다.Handler 대상을 만드는 과정에서 sThreadLocal에서 현재 스레드의looper 대상과 그 중의 MessageQueue를 가져옵니다.handler 대상에 이 MessageQueue가 있습니다.
3.Looper.loop (), 로퍼 내부의 MessageQueue에서 끊임없이 정보를 찾습니다. 메시지 대기열에 메시지가 없으면 막힙니다.
4.다른 라인에서handler를 호출합니다.hande Message (msg) 가 메시지를 보낼 때, 이 msg는 이handler를 msg에 포장하고, 이 msg를 이MessageQueue에 추가합니다.이handler 대상을 만드는 라인에서 대응하는 Looper는 이 msg로 돌아가며 이handler의handleMessage () 방법을 호출하여 대응하는 논리를 처리합니다.
5.이 looper의 윤문은 이 looper 대상을 만드는 라인에 있기 때문에 라인의 전환을 실현했습니다.
참고:
1.handler가 보낸 메시지는 대응하는 looper에 의해 조회될 수 있습니다. 왜냐하면 그들은 모두 같은MessageQueue를 가지고 있기 때문입니다.
2.서로 다른 라인의handler와looper의 착란이 일어나지 않는 이유는 Looper의 접근이 sThreadLocal을 통해 이루어졌기 때문이다.Handler와 Looper는 같은 라인을 기반으로 만들어졌으며 같은 메시지 대기열이 있습니다.
위에서 서술한 절차는 사실 몇 가지 원리를 포함하고 있다. 다음은 원본을 분석하여 이 절차들을 더욱 전면적으로 이해하자.
1.우선 Looper는 Looper를 호출하여 생성됩니다.prepare();방법:
public static void prepare() {
prepare(true);
}
// Looper, sThreadLocal 。
private static void prepare(boolean quitAllowed) {
//quitAllowed Looper 。 , 。
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//다음 코드는 Looper 객체를 만드는 것입니다. 이때 구조 매개 변수에 mQueue 메시지 대기열을 만들고 현재 스레드 mThread를 가져옵니다.
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
ThreadLocal 지식점: 이 클래스는 각종 범주형의 데이터를 저장하는 대상이다. 다른 저장 도구 클래스와 달리 같은 mThreadLocal 대상이 서로 다른 라인에서 데이터를 저장하는 액세스 조작은 서로 독립적이다. 즉, 모든 라인에 이 mThreadLocal 복사본이 있는데 그들은 각자 저장하고 각자 사용하며 서로 간섭하지 않는다.Looper 클래스의 sThreadLocal 대상은 정적 전역 변수입니다. 각 라인에서 만든 Looper 대상을 저장하고 가져올 수 있습니다.그가 서로 간섭하지 않는 이유는 그가 액세스하고 사용하는 키가 현재의 라인이기 때문이다. 즉, Looper와 Threader의 일일이 대응을 실현했기 때문이다.다음은 원본 코드를 살펴보자.
static final ThreadLocal sThreadLocal = new ThreadLocal();
// : , getMap(t) ThreadLocalMap
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
//그리고 현재 스레드 t에 따라 이 스레드 t의 ThreaLocaMap 대상을 꺼내서 set 방법으로 저장합니다
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
private void set(ThreadLocal> key, Object value) {
Entry[] tab = table; //
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);// sThreadLocal, i index
tab[i] = new Entry(key, value); // sThreadLocal Looper Entry table index
int sz = ++size; //
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
만약 처음 사용한다면 getMap이 비어 있습니다. ThreadLocalMap 대상을 다시 만들고 이 r의threadLocals 변수에 값을 부여해야 합니다. new 대상에서 저장을 완성해야 합니다. 위와 같습니다.
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
사고방식: 현재 스레드 --->를 가져오고 이 스레드에 대응하는ThreadLocalMap 대상 --->에서 대상 가져오는 그룹 탭------>value를 생성entry로 봉하여 탭[i]에 저장합니다.
데이터 추출은 저장의 원리와 마찬가지로 역행이다. 그래서 같은 sThreadLocal 대상은 서로 다른 라인에서 추출한 것은 이 라인 아래의 looper 대상일 뿐 착란이 일어나지 않고 looper는 착란되지 않는다.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
2. Handler의 생성
상황1:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
:
public Handler() {
this(null, false);
}
//이 과정은 looper와handler를 한 라인에 묶고 메시지 Queue 메시지 대기열을 함께 사용합니다.하나의 라인의looper가 확실하기 때문에 이 라인의handler도 확실합니다.
public Handler(Callback callback, boolean async) {
mLooper = Looper.myLooper();// Looper
if (mLooper == null) { // handler looper
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
상황2:Handler를 실현했다.Callback 인터페이스의handleMessage () 방법
Handler handler=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
상황3: 이때handler가 있는 라인이 Looper입니다.myLooper()가 있는 스레드입니다.
Handler handler=new Handler(Looper.myLooper(), new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
})
Handler handler=new Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
3. 메시지 보내기
상황1:send 발송
handler.sendEmptyMessage(100);
상황2: 사실 Runnable을 Message에 포장한 것이다. 즉, Message이다.callback==Message.runnable, 그리고send로 보내기
handler.post(new Runnable() {
@Override
public void run() {
}
});
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
그래서 우리는 이어서 send 방법을 보았다.
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
//MessageQueue 메시지 대기열에 전송된 메시지를 추가합니다. 이전에 차단된 상태이면 활성화됩니다.그렇지 않으면 바로 안에 메시지를 추가합니다.
//MessageQueue는 단일 체인 테이블의 구조를 통해 메시지를 유지하는데 효율이 높다.
boolean enqueueMessage(Message msg, long when) {
.......
synchronized (this) {
if (mQuitting) {// looper quie , quit(), mQuitting=true 。
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// ,
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
// MessageQueue , , MessageQueue, Msg
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
4.Looper.poop();폴링은 MessageQueue의 메시지를 읽고 처리합니다.선진적으로 추가된 선처리.
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // looper, messageQueue, next()
if (msg == null) {
// , messageQueue , loop()
return;
}
...............
// msg handler( handler) dispatchMessage(msg) 。
msg.target.dispatchMessage(msg);
..............
}
:
Message next() {
.......
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// 。0 , -1 。 。
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
.....
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// -1,
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
}
}
다시 한 번handler.dispatchMessage () 는 메시지를 어떻게 처리합니까: (loop에서 돌아가는 루틴을 처리합니다. 즉 looper,handler의 루틴을 만들기 때문에 루틴을 전환하는 목적을 실현했습니다)
public void dispatchMessage(Message msg) {
if (msg.callback != null) {// post(Runnable), msg.callback==runnable, runnable
handleCallback(msg);
} else {
if (mCallback != null) {// handler handle.CallBack, handMeaasge(msg)
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);// handMessage(msg);
}
}
Looper의 종료:
public void quit() {
mQueue.quit(false);
}
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();// , msg,
} else {
removeAllMessagesLocked();//
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
참고: 수동으로 생성된 Looper의 경우 작업을 완료하려면 quit()를 호출하여 종료해야 합니다. 그렇지 않으면 Looper가 폴링 상태로 남아 성능이 저하됩니다.
여기까지도 관련된 몇 가지 원리를 포함해서 절차를 상세하게 한 번 걸은 셈이다.이 절차에 따라 메인 라인이 어떻게 된 일인지 따라갈 수 있다. 물론 메인 라인도 이 원리에 따라 일한다. 단지 그는 비교적 특수하기 때문에 전문적인 방법으로 이 메커니즘을 초기화하고 Looper의 퇴출을 하지 못하게 한다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.