Handler 스 레 드 메시지 처리 논리

전재 출처 를 밝 혀 주 십시오:http://blog.csdn.net/droyon/article/details/20732979
우 리 는 Handler 를 열 어 메시지 처리 에 사용 할 수 있다.우 리 는 메 인 스 레 드 looper 를 사용 하여 메시지 의 송 수신 loop 을 진행 할 수 있다.우리 가 새로 개척 한 비동기 스 레 드 도 사용 할 수 있다.
주 루틴:
mHandler = new Handler(){

			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
			}
			
		};
비동기 스 레 드:
HandlerThread hdThread = new HandlerThread("");
		hdThread.start();
		mHandler = new Handler(hdThread.getLooper());

사용자 정의 스 레 드.(사용자 정의 스 레 드 가 Handler 비동기 처리 메시지 의 스 레 드 로 바 뀌 려 면 run 에서 먼저 Looper. prepare 를 실행 하여 Messsage Queue 를 만 든 다음 Looper. loop () 에서 스 레 드 를 무선 순환 에 들 어가 게 해 야 합 니 다)
newThread newT = new newThread();
		newT.start();
		new Handler(newT.mLooper);
class newThread extends Thread{
		public Looper mLooper;
		public newThread(){}
		@Override
		public void run() {
			super.run();
			
			Looper.prepare();
			Looper newLoop = Looper.myLooper();
			mLooper = newLoop;
			Log.d("hlwang","onCreate looper ..... newLooper is:"+mLooper);
			mLooper.loop();
		}
		
	}

이 문 서 는 주로 Handler 가 정 보 를 어떻게 처리 하 는 지 논술 한 것 이다.
일반 스 레 드: run 방법 을 실행 하고 스 레 드 가 끝 났 습 니 다.(스 레 드 수명 주기 완료) [새 상태 (New), 준비 상태 (Runnable):, 실행 상태 (Running): 차단 상태 (Blocked)]
Handler 스 레 드: 스 레 드 시작 은 무선 순환 체 에 들 어가 순환 할 때마다 메시지 내부 에서 메 시 지 를 꺼 내 해당 하 는 메시지 처리 함 수 를 되 돌려 줍 니 다.메시지 대기 열 이 비어 있 으 면 메시지 대기 열 에 새로운 메시지 가 있 을 때 까지 스 레 드 를 중단 합 니 다.
handler 가 해결 해 야 할 문 제 는 다음 과 같 습 니 다.
1. 메시지 대기 열 을 포함 해 야 합 니 다. 대기 열 에 있 는 메 시 지 는 보통 대기 열 체 제 를 사용 합 니 다. 즉, 먼저 도착 한 메 시 지 를 먼저 처리 합 니 다.
2. 스 레 드 는 while (true) 를 실행 하여 무선 순환 을 하고 순환 에서 메시지 대기 열 에서 메 시 지 를 꺼 내 고 메시지 의 출처 에 따라 메시지 처리 함 수 를 되 돌려 줍 니 다.
3. 다른 외부 스 레 드 는 이 스 레 드 에 메 시 지 를 보 낼 수 있 습 니 다. 메시지 큐 에 삽입 하려 면 메시지 큐 에 자 물 쇠 를 추가 해 야 합 니 다. 즉, 메시지 큐 는 읽 기와 쓰기 작업 을 동시에 할 수 없습니다.
안 드 로 이 드 에서 Handler 의 이 세 가지 문 제 를 어떻게 실현 하 는 지 살 펴 보 자.
1. 먼저 메시지 큐 를 만 듭 니 다.외부 프로그램 은 Handler 를 통 해 스 레 드 에 메 시 지 를 보 내 고 메 시 지 는 Handler 를 통 해 Message Queue 대상 에 전 달 됩 니 다.
Message Queue 는 Looper 를 통 해 prepare 방법 을 실행 하여 만 들 었 습 니 다.모든 스 레 드 는 start 방법 을 실행 하고 run 방법 을 시작 합 니 다.Activity 메 인 스 레 드 든 HandlerThread 가 열 린 스 레 드 든
우리 의 Looper. prepare 방법 은 바로 run 방법 에서 실 행 된 것 이다.다음 과 같다.
HandlerThread. java (Looper. loop 방법 을 실행 하 는 것 을 잊 지 마 세 요)
public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
우 리 는 Loop. prepare 방법 에 들 어 갔다.
Looper.java
if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
그 중에서 ThreadLocal 은 스 레 드 로 컬 저장 소 입 니 다.
ps: 스 레 드 로 컬 저장 소 는 무엇 입 니까? (자바 프로 그래 밍 사상 참조)클래스 의 내부 정적 변 수 는 프로 세 스 의 그 스 레 드 에 접근 하 더 라 도 그 내용 은 항상 같 습 니 다. 컴 파일 러 내부 에 static 정적 변 수 를 위해 공간 을 따로 분 배 했 기 때 문 입 니 다.스 레 드 로 컬 저장 소 는 정반 대 입 니 다. 서로 다른 스 레 드 접근 은 서로 다른 결 과 를 얻 을 수 있 습 니 다.다시 말 하면 Looper 는 대응 하 는 스 레 드 이 고 하나의 스 레 드 는 Looper 대상 만 있 을 수 있 습 니 다. 이것 은 Looper 가 정의 한 것 입 니 다.처음으로 Looper. prepare 방법 을 호출 하면 sThreadLocal. get, null 을 얻 을 수 있 습 니 다. 이 때 new Looper 대상 을 호출 하고 sThreadLocal. set 에 줍 니 다.같은 스 레 드 에서 prepare 를 다시 호출 하면 throw Runtime Exception 입 니 다.
이어서 내 려 다 보 세 요. new Looper 에서 뭐 했 어 요?
private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }

Looper 의 구조 방법 에 Message Queue 를 만 들 었 습 니 다.
2. 스 레 드 를 무한 순환 에 들 어가 게 합 니 다.
Looper. loop 방법 을 실행 합 니 다.이 방법 은 다음 과 같다.
public static void loop() {
        Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        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();
        
        while (true) {
            Message msg = queue.next(); // might block
            if (msg != null) {
                if (msg.target == null) {
                    // No target is a magic identifier for the quit message.
                    return;
                }

                long wallStart = 0;
                long threadStart = 0;

                // 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);
                    wallStart = SystemClock.currentTimeMicro();
                    threadStart = SystemClock.currentThreadTimeMicro();
                }

                msg.target.dispatchMessage(msg);

                if (logging != null) {
                    long wallTime = SystemClock.currentTimeMicro() - wallStart;
                    long threadTime = SystemClock.currentThreadTimeMicro() - threadStart;

                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                    if (logging instanceof Profiler) {
                        ((Profiler) logging).profile(msg, wallStart, wallTime,
                                threadStart, threadTime);
                    }
                }

                // 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.recycle();
            }
        }
    }

2.1. 여기 서 먼저 my Looper 를 호출 하여 현재 Looper 대상 으로 돌아 갑 니 다. 이 함수 내 부 는 스 레 드 로 컬 저장 소 sThreadLocal. get 을 통 해서 만 가 져 옵 니 다.
2.2 while (true) 는 무한 순환 에 들어간다.
2.2.1. Message Queue. next 방법 으로 대기 열 에 있 는 Message 를 꺼 냅 니 다.현재 대기 열 이 비어 있 으 면 현재 스 레 드 가 걸 립 니 다. 즉, next 방법 내부 에서 스 레 드 를 걸 어 놓 는 논 리 를 처리 합 니 다.
2.2.2. message 가 null 이 아니라면 msg. target. dispatchMessage 함 수 를 되 돌려 줍 니 다. 이곳 의 target 은 msg 에 대응 하 는 Handler 입 니 다.
target 에 대해 논 리 는 다음 과 같 습 니 다.
Handler 가 Message 를 구성 할 때:
public final Message obtainMessage()
    {
        return Message.obtain(this);
    }
public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;

        return m;
    }

또는:
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

2.2.3 메시지 처리 가 완료 되면 msg. recycle 방법 으로 이 Message 대상 이 차지 하 는 시스템 자원 을 회수 합 니 다.Message 클래스 내부 에 하나의 데이터 풀 을 사용 하여 Message 대상 을 저장 하기 때문에 끊임없이 Message 클래스 대상 을 만 들 고 삭제 하 는 것 을 피 할 수 있 습 니 다.따라서 이 Message 를 처리 할 때마다 Message 대상 을 빈 상태 로 표시 하여 이 Message 대상 을 다시 사용 할 수 있 도록 해 야 합 니 다.
3. 메시지 대기 열 은 줄 을 서서 메 시 지 를 처리 합 니 다. 즉, 먼저 도착 한 메 시 지 를 먼저 처리 하지만 메시지 자체 가 처리 되 는 시간 을 지정 하면 이 시간 을 기 다 려 야 처리 할 수 있 습 니 다.메 시 지 는 Message Queue 에서 Message 를 사용 하여 열 에 있 는 메 시 지 를 링크 구조 로 저장 합 니 다.Message 에서 next 변 수 는 다음 메 시 지 를 가리 키 고 있 습 니 다.
Message Queue 에는 두 가지 주요 함수 가 있 습 니 다. '메시지 꺼 내기' next 방법, '메시지 추가' enquenceMessage 방법 입 니 다.
next 방법 내부 절 차 는 세 단계 로 나 뉜 다.
final Message next() {
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(mPtr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                final Message msg = mMessages;
                if (msg != null) {
                    final long when = msg.when;
                    if (now >= when) {
                        mBlocked = false;
                        mMessages = msg.next;
                        msg.next = null;
                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    } else {
                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
                    }
                } else {
                    nextPollTimeoutMillis = -1;
                }

                // If first time, then get the number of idlers to run.
                if (pendingIdleHandlerCount < 0) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount == 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

3.1, nativePollOnce 호출 방법
nativePollOnce(mPtr, nextPollTimeoutMillis);
이것 은 jni 함수 로 메시지 대기 열 에서 메 시 지 를 꺼 내 는 역할 을 합 니 다.Message Queue 클래스 내부 에 메시지 큐 가 저장 되 어 있 지 않 습 니 다. 실제 메시지 큐 는 jni 가 구현 한 c 코드 에 저 장 됩 니 다.즉, C 환경 에서 NativeMessage Queue 데이터 대상 을 만 들 었 습 니 다. 이것 은 nativePollOnce 함수 의 첫 번 째 매개 변수의 의미 입 니 다.
private int mPtr; // used by native code
mPtr 는 int 형 변수 로 c 에서 NativeMessage Queue 대상 으로 강제 전 환 됩 니 다. C 환경 에서 메시지 대기 열 에 메시지 가 없 으 면 현재 스 레 드 가 걸 립 니 다 (wait). 메시지 가 있 으 면 c 코드 는 자바 환경 에서 Message 대상 에 게 이 메 시 지 를 할당 합 니 다.
Message Queue 의 jni 코드 는 framework / base / core / jni / android 에 있 습 니 다.os_Message Queue. cpp 에서
3.2 다음 에 synchronized (this) 에 포 함 된 코드 세그먼트 를 실행 합 니 다.this 는 메시지 와 메 시 지 를 쓰 는 자물쇠 로 사 용 됩 니 다.enqueueMessage 방법 에서 도 synchronized (this) 를 사용 하여 코드 동기 화 를 진행 하 였 습 니 다.
이 코드 는 메 시 지 를 꺼 내 서 이 메시지 의 실행 시간 이 도 착 했 는 지 판단 하고 도착 하면 이 메 시 지 를 되 돌려 주 며 mMessage 를 비 웁 니 다.그렇지 않 으 면 다음 메 시 지 를 가 져 오 십시오.
3.3 mMessage 가 null 이면 c 환경의 메시지 대기 열 에 실행 가능 한 메시지 가 없다 는 것 을 의미한다.따라서 mPendingIdleHandler 목록 의 빈 리 셋 함 수 를 실행 합 니 다.
메시지 코드 추가:
enqueueMessage
final boolean enqueueMessage(Message msg, long when) {
        if (msg.isInUse()) {
            throw new AndroidRuntimeException(msg
                    + " This message is already in use.");
        }
        if (msg.target == null && !mQuitAllowed) {
            throw new RuntimeException("Main thread not allowed to quit");
        }
        final boolean needWake;
        synchronized (this) {
            if (mQuiting) {
                RuntimeException e = new RuntimeException(
                    msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                return false;
            } else if (msg.target == null) {
                mQuiting = true;
            }

            msg.when = when;
            //Log.d("MessageQueue", "Enqueing: " + msg);
            Message p = mMessages;
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; // new head, might need to wake up
            } else {
                Message prev = null;
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
                msg.next = prev.next;
                prev.next = msg;
                needWake = false; // still waiting on head, no need to wake up
            }
        }
        if (needWake) {
            nativeWake(mPtr);
        }
        return true;
    }

인자 msg 를 mMessage 에 할당 합 니 다.nativeWake (mPtr) 를 호출 합 니 다.이것 은 jni 함수 입 니 다. 내부 에서 mMessage 메 시 지 를 C 환경의 메시지 대기 열 에 추가 하고 이 스 레 드 가 wait 상태 에 있 으 면 이 스 레 드 를 깨 웁 니 다.

Handler 의 메시지 처리 안 드 로 이 드 가 실 현 됩 니 다. 지금까지 저 희 는 Handler 를 사용 할 때 handleMessage 방법 을 실 현 했 습 니 다.이것 은 Message 가 target 의 dispatchMessage 를 되 돌 릴 때 다음 코드 를 실행 하기 때 문 입 니 다.
Handler.java
 public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

여기까지.부적 절 한 점 이 있 으 면 교 류 를 환영 합 니 다.

좋은 웹페이지 즐겨찾기