国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handl

Jochen / 1588人閱讀

摘要:今天看到這個(gè)方法之后決定再去翻翻源代碼梳理一下的實(shí)現(xiàn)機(jī)制。而通常會(huì)伴隨著一個(gè)耗時(shí)的后臺(tái)線(xiàn)程一起出現(xiàn),這個(gè)后臺(tái)線(xiàn)程在任務(wù)執(zhí)行完畢后發(fā)送消息去更新。解決方法之一,使用弱引用

Handler

每個(gè)初學(xué)Android開(kāi)發(fā)的都繞不開(kāi)Handler這個(gè)“坎”,為什么說(shuō)是個(gè)坎呢,首先這是Android架構(gòu)的精髓之一,其次大部分人都是知其然卻不知其所以然。今天看到Handler.post這個(gè)方法之后決定再去翻翻源代碼梳理一下Handler的實(shí)現(xiàn)機(jī)制。

異步更新UI

先來(lái)一個(gè)必背口訣“主線(xiàn)程不做耗時(shí)操作,子線(xiàn)程不更新UI”,這個(gè)規(guī)定應(yīng)該是初學(xué)必知的,那要怎么來(lái)解決口訣里的問(wèn)題呢,這時(shí)候Handler就出現(xiàn)在我們面前了(AsyncTask也行,不過(guò)本質(zhì)上還是對(duì)Handler的封裝),來(lái)一段經(jīng)典常用代碼(這里忽略?xún)?nèi)存泄露問(wèn)題,我們后面再說(shuō)):

首先在Activity中新建一個(gè)handler:

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 0:
                    mTestTV.setText("This is handleMessage");//更新UI
                    break;
            }
        }
    };

然后在子線(xiàn)程里發(fā)送消息:

new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);//在子線(xiàn)程有一段耗時(shí)操作,比如請(qǐng)求網(wǎng)絡(luò)
                    mHandler.sendEmptyMessage(0);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

至此完成了在子線(xiàn)程的耗時(shí)操作完成后在主線(xiàn)程異步更新UI,可是并沒(méi)有用上標(biāo)題的post,我們?cè)賮?lái)看post的版本:

private Handler mHandler;//全局變量
@Override
protected void onCreate(Bundle savedInstanceState) {
    mHandler = new Handler();
    new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);//在子線(xiàn)程有一段耗時(shí)操作,比如請(qǐng)求網(wǎng)絡(luò)
                        mHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                mTestTV.setText("This is post");//更新UI
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
}

從表面上來(lái)看,給post方法傳了個(gè)Runnable,像是開(kāi)了個(gè)子線(xiàn)程,可是在子線(xiàn)程里并不能更新UI啊,那么問(wèn)題來(lái)了,這是怎么個(gè)情況呢?帶著這個(gè)疑惑,來(lái)翻翻Handler的源碼:

先來(lái)看看普通的sendEmptyMessage是什么樣子:

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

將我們傳入的參數(shù)封裝成了一個(gè)消息,然后調(diào)用sendMessageDelayed:

public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

再調(diào)用sendMessageAtTime:

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);
    }

好了,我們?cè)賮?lái)看post():

public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);//getPostMessage方法是兩種發(fā)送消息的不同之處
    }

方法只有一句,內(nèi)部實(shí)現(xiàn)和普通的sendMessage是一樣的,但是只有一點(diǎn)不同,那就是 getPostMessage(r) 這個(gè)方法:

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

這個(gè)方法我們發(fā)現(xiàn)也是將我們傳入的參數(shù)封裝成了一個(gè)消息,只是這次是m.callback = r,剛才是msg.what=what,至于Message的這些屬性就不看了

Android消息機(jī)制

看到這里,我們只是知道了post和sendMessage原理都是封裝成Message,但是還是不清楚Handler的整個(gè)機(jī)制是什么樣子,繼續(xù)探究下去。

剛才看到那兩個(gè)方法到最終都調(diào)用了sendMessageAtTime

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);
    }

這個(gè)方法又調(diào)用了 enqueueMessage,看名字應(yīng)該是把消息加入隊(duì)列的意思,點(diǎn)進(jìn)去看下:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

mAsynchronous這個(gè)異步有關(guān)的先不管,繼續(xù)將參數(shù)傳給了queue的enqueueMessage方法,至于那個(gè)msgtarget的賦值我們后面再看,現(xiàn)在繼續(xù)進(jìn)入MessageQueue類(lèi)的enqueueMessage方法,方法較長(zhǎng),我們看看關(guān)鍵的幾行:

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;

果然像方法名說(shuō)的一樣,一個(gè)無(wú)限循環(huán)將消息加入到消息隊(duì)列中(鏈表的形式),但是有放就有拿,這個(gè)消息怎樣把它取出來(lái)呢?

翻看MessageQueue的方法,我們找到了next(),代碼太長(zhǎng),不贅述,我們知道它是用來(lái)把消息取出來(lái)的就行了。不過(guò)這個(gè)方法是在什么地方調(diào)用的呢,不是在Handler中,我們找到了Looper這個(gè)關(guān)鍵人物,我叫他環(huán)形使者,專(zhuān)門(mén)負(fù)責(zé)從消息隊(duì)列中拿消息,關(guān)鍵代碼如下:

for (;;) {
     Message msg = queue.next(); // might block
     ...
     msg.target.dispatchMessage(msg);
     ...
     msg.recycleUnchecked();
}

簡(jiǎn)單明了,我們看到了我們剛才說(shuō)的msg.target,剛才在Handler中賦值了msg.target=this,所以我們來(lái)看Handler中的dispatchMessage:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

msg的callback不為空,調(diào)用handleCallback方法(message.callback.run()

mCallback不為空,調(diào)用mCallback.handleMessage(msg)

最后如果其他都為空,執(zhí)行Handler自身的 handleMessage(msg) 方法

msg的callback應(yīng)該已經(jīng)想到是什么了,就是我們通過(guò)Handler.post(Runnable r)傳入的Runnable的run方法,這里就要提提java基礎(chǔ)了,直接調(diào)用線(xiàn)程的run方法相當(dāng)于是在一個(gè)普通的類(lèi)調(diào)用方法,還是在當(dāng)前線(xiàn)程執(zhí)行,并不會(huì)開(kāi)啟新的線(xiàn)程。

所以到了這里,我們解決了開(kāi)始的疑惑,為什么在post中傳了個(gè)Runnable還是在主線(xiàn)程中可以更新UI

繼續(xù)看如果msg.callback為空的情況下的mCallback,這個(gè)要看看構(gòu)造方法:

1.
public Handler() {
        this(null, false);
    }
2.    
public Handler(Callback callback) {
        this(callback, false);
    }
3.
public Handler(Looper looper) {
        this(looper, null, false);
    }
4.
public Handler(Looper looper, Callback callback) {
        this(looper, callback, false);
    }
5.
public Handler(boolean async) {
        this(null, async);
    }
6.
public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class 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;
    }
7.
public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

具體的實(shí)現(xiàn)就只有最后兩個(gè),已經(jīng)知道mCallback是怎么來(lái)的了,在構(gòu)造方法中傳入就行。

最后如果這兩個(gè)回調(diào)都為空的話(huà)就執(zhí)行Handler自身的handleMessage(msg)方法,也就是我們熟知的新建Handler重寫(xiě)的那個(gè)handleMessage方法。

Looper

看到了這里有一個(gè)疑惑,那就是我們?cè)谛陆℉andler的時(shí)候并沒(méi)有傳入任何參數(shù),也沒(méi)有哪里顯示調(diào)用了Looper有關(guān)方法,那Looper的創(chuàng)建以及方法調(diào)用在哪里呢?其實(shí)這些東西Android本身已經(jīng)幫我們做了,在程序入口ActivityThread的main方法里面我們可以找到:

 public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ...
    Looper.loop();
    ...
總結(jié)

已經(jīng)大概梳理了一下Handler的消息機(jī)制,以及post方法和我們常用的sendMessage方法的區(qū)別。來(lái)總結(jié)一下,主要涉及四個(gè)類(lèi)Handler、Message、MessageQueue、Looper

新建Handler,通過(guò)sendMessage或者post發(fā)送消息,Handler調(diào)用sendMessageAtTimeMessage交給MessageQueue

MessageQueue.enqueueMessage方法將Message以鏈表的形式放入隊(duì)列中

Looperloop方法循環(huán)調(diào)用MessageQueue.next()取出消息,并且調(diào)用HandlerdispatchMessage來(lái)處理消息

dispatchMessage中,分別判斷msg.callback、mCallback也就是post方法或者構(gòu)造方法傳入的不為空就執(zhí)行他們的回調(diào),如果都為空就執(zhí)行我們最常用重寫(xiě)的handleMessage

最后談?wù)刪andler的內(nèi)存泄露問(wèn)題

再來(lái)看看我們的新建Handler的代碼:

private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            ...
        }
    };

當(dāng)使用內(nèi)部類(lèi)(包括匿名類(lèi))來(lái)創(chuàng)建Handler的時(shí)候,Handler對(duì)象會(huì)隱式地持有Activity的引用。

而Handler通常會(huì)伴隨著一個(gè)耗時(shí)的后臺(tái)線(xiàn)程一起出現(xiàn),這個(gè)后臺(tái)線(xiàn)程在任務(wù)執(zhí)行完畢后發(fā)送消息去更新UI。然而,如果用戶(hù)在網(wǎng)絡(luò)請(qǐng)求過(guò)程中關(guān)閉了Activity,正常情況下,Activity不再被使用,它就有可能在GC檢查時(shí)被回收掉,但由于這時(shí)線(xiàn)程尚未執(zhí)行完,而該線(xiàn)程持有Handler的引用(不然它怎么發(fā)消息給Handler?),這個(gè)Handler又持有Activity的引用,就導(dǎo)致該Activity無(wú)法被回收(即內(nèi)存泄露),直到網(wǎng)絡(luò)請(qǐng)求結(jié)束。

另外,如果執(zhí)行了Handler的postDelayed()方法,那么在設(shè)定的delay到達(dá)之前,會(huì)有一條MessageQueue -> Message -> Handler -> Activity的鏈,導(dǎo)致你的Activity被持有引用而無(wú)法被回收。

解決方法之一,使用弱引用:

static class MyHandler extends Handler {
    WeakReference mActivityReference;
    MyHandler(Activity activity) {
        mActivityReference= new WeakReference(activity);
    }
    @Override
    public void handleMessage(Message msg) {
        final Activity activity = mActivityReference.get();
        if (activity != null) {
            mImageView.setImageBitmap(mBitmap);
        }
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/65059.html

相關(guān)文章

  • AsyncTask你真用對(duì)了嗎?

    摘要:從頭來(lái)看一下的執(zhí)行過(guò)程,來(lái)到方法這行注釋為官方注釋注意此方法必須在線(xiàn)程調(diào)用,這里就不做測(cè)試了。 前言 在之前的文章深入探究了Handler,《從Handler.post(Runnable r)再一次梳理Android的消息機(jī)制(以及handler的內(nèi)存泄露)》我們知道了Android的消息機(jī)制主要靠Handler來(lái)實(shí)現(xiàn),但是在Handler的使用中,忽略?xún)?nèi)存泄露的問(wèn)題,不管是代碼量還是...

    jifei 評(píng)論0 收藏0
  • Thread、HandlerHandlerThread關(guān)系何在?

    摘要:總結(jié)的使用方法還是比較簡(jiǎn)單的,但是我們要明白一點(diǎn)的是如果一個(gè)線(xiàn)程要處理消息,那么它必須擁有自己的,并不是在哪里創(chuàng)建,就可以在哪里處理消息的。如果不用的話(huà),需要手動(dòng)去調(diào)用和這些方法。 前言 前幾天看到一道面試題:Thread、Handler和HandlerThread有什么區(qū)別?,這個(gè)題目有點(diǎn)意思,對(duì)于很多人來(lái)說(shuō),可能對(duì)Thread和Handler很熟悉,主要涉及到Android的消息機(jī)...

    hankkin 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<