国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android 消息機制以及handler的內(nèi)存泄露

2019-12-12 05:14:30
字體:
供稿:網(wǎng)友

Handler

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

異步更新UI

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

首先在Activity中新建一個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;      }    }  };

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

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

至此完成了在子線程的耗時操作完成后在主線程異步更新UI,可是并沒有用上標題的post,我們再來看post的版本:

new Thread(new Runnable() {      @Override      public void run() {        try {          Thread.sleep(1000);//在子線程有一段耗時操作,比如請求網(wǎng)絡(luò)          Handler handler = new Handler();          handler.post(new Runnable() {            @Override            public void run() {              mTestTV.setText("This is post");//更新UI            }          });        } catch (InterruptedException e) {          e.printStackTrace();        }      }    }).start();

從表面上來看,給post方法傳了個Runnable,像是開了個子線程,可是在子線程里并不能更新UI啊,那么問題來了,這是怎么個情況呢?帶著這個疑惑,來翻翻Handler的源碼:

先來看看普通的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ù)封裝成了一個消息,然后調(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);  }

好了,我們再來看post():

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

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

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

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

Android消息機制

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

剛才看到那兩個方法到最終都調(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);  }

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

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

mAsynchronous這個異步有關(guān)的先不管,繼續(xù)將參數(shù)傳給了queue的enqueueMessage方法,至于那個msg的target的賦值我們后面再看,現(xiàn)在繼續(xù)進入MessageQueue類的enqueueMessage方法,方法較長,我們看看關(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.nextprev.next = msg;

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

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

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

簡單明了,我們看到了我們剛才說的msg.target,剛才在Handler中賦值了msg.target=this,所以我們來看Handler中的dispatchMessage:

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

1.msg的callback不為空,調(diào)用handleCallback方法(message.callback.run())
2.mCallback不為空,調(diào)用mCallback.handleMessage(msg)
3.最后如果其他都為空,執(zhí)行Handler自身的 handleMessage(msg) 方法
msg的callback應(yīng)該已經(jīng)想到是什么了,就是我們通過Handler.post(Runnable r)傳入的Runnable的run方法,這里就要提提java基礎(chǔ)了,直接調(diào)用線程的run方法相當于是在一個普通的類調(diào)用方法,還是在當前線程執(zhí)行,并不會開啟新的線程。

所以到了這里,我們解決了開始的疑惑,為什么在post中傳了個Runnable還是在主線程中可以更新UI。

繼續(xù)看如果msg.callback為空的情況下的mCallback,這個要看看構(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<? 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;  }7.public Handler(Looper looper, Callback callback, boolean async) {    mLooper = looper;    mQueue = looper.mQueue;    mCallback = callback;    mAsynchronous = async;  }

具體的實現(xiàn)就只有最后兩個,已經(jīng)知道m(xù)Callback是怎么來的了,在構(gòu)造方法中傳入就行。

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

Looper

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

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

總結(jié)

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

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

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

Looper的loop方法循環(huán)調(diào)用MessageQueue.next()取出消息,并且調(diào)用Handler的dispatchMessage來處理消息

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

最后談?wù)刪andler的內(nèi)存泄露問題
再來看看我們的新建Handler的代碼:

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

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

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

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

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

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

以上就是對Android handler 消息機制的資料整理,后續(xù)繼續(xù)補充相關(guān)資料,謝謝大家對本站的支持!

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 岑溪市| 鄂伦春自治旗| 海门市| 隆尧县| 华池县| 绍兴市| 达日县| 泰州市| 泸溪县| 白沙| 电白县| 淮阳县| 恩施市| 东源县| 西藏| 蓬溪县| 元谋县| 治县。| 南投县| 定安县| 四平市| 崇信县| 虞城县| 正宁县| 寿光市| 泾阳县| 波密县| 大田县| 庆元县| 长泰县| 冀州市| 忻州市| 观塘区| 黔西县| 临颍县| 荃湾区| 清远市| 万宁市| 德江县| 大埔区| 正宁县|