20190908關於handler/http/https等

Handler

  1. 日常編碼的使用:
    使用前初始化Handler對象,重寫handleMessage()方法,拿到Message對象後,做延時處理/定時處理/更新UI等操作,通常直接初始化會有警告提示 @SuppressLint(“HandlerLeak”)
    原因:作爲匿名內部類的方式使用時,會持有外部類的對象(一般是Activity),當外部類被關閉,GC回收會因爲外部類對象還被持有,造成內存泄露
    解決辦法:( https://blog.csdn.net/androidsj/article/details/79865091 詳解)
    推薦採用靜態內部類實現, 並持有Activity 的弱引用,可以避免無法被回收導致內存泄露。
    當然很多時候在寫demo的時候,爲了方便(懶)可以直接構造匿名內部類,但要在onDestory生命週期中主動釋放未處理消息 mHandler.removeCallbacksAndMessages(null).
private MyHandler handler = new MyHandler(this);
    static class MyHandler extends Handler {
        WeakReference weakReference;
        public MyHandler(SecondActivity activity) {
            weakReference = new WeakReference(activity);
        }
 
        @Override
        public void handleMessage(Message msg) {
            
        }
    }
  1. 用的最多的異步更新UI方法(post/postDelayed):
    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

可以看到post方法其實也是sendMessageDelayed – > 最終的enqueueMessage 入隊來處理消息。
在這邊要注意的是,在子線程中使用時此處的Runnable 只是重寫run,在方法內實現自己的目的。
那麼所有我們sendMessage等方法是如何一步步處理的呢:
Handler.postDelayed(Runnable r, long delayMillis)
–> Handler.sendMessageDelayed(getPostMessage®, delayMillis)
–> Handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
–> Handler.enqueueMessage(queue, msg, uptimeMillis)
–> MessageQueue.enqueueMessage(msg, uptimeMillis)

(https://www.cnblogs.com/ldq2016/p/9260783.html 詳解)

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 {
...
}

在enqueueMessage中 是一個鏈表,把傳遞的信息依次入隊,如果有delayed時間,則:when < p.when 判斷時間,來決定插入順序,接下來就是Looper.loop()中的循環讀取,在一個死循環中,從mQueue中取出Message對象, 調用每個Message對象的msg.target.dispatchMesssge方法:

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
//派發消息到對應的handler
 msg.target.dispatchMessage(msg);
 //無效的資源回收
 msg.recycle();
}

讓我們看看next()方法的具體做法:

for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
 
nativePollOnce(ptr, nextPollTimeoutMillis);
 
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
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 {
// No more messages.
nextPollTimeoutMillis = -1;
}
...
}
}

疑問 - -爲什麼不會阻塞?是因爲循環取值爲空,則blocked,不會耗費太多性能 之後會調用native中的喚醒方法進行精確喚醒 nativePollOnce(ptr, nextPollTimeoutMillis); 到此爲止已經大體流程已經結束。

小結

  1. Looper類用來爲一個線程開啓一個消息循環。
    默認情況下android中新誕生的線程是沒有開啓消息循環的。(主線程除外,主線程系統會自動爲其創建Looper對象,開啓消息循環。)
    Looper對象通過MessageQueue來存放消息和事件。一個線程只能有一個Looper,對應一個MessageQueue。(如果對一個已經quit的Looper重新start會出現異常)
  2. 通常是通過Handler對象來與Looper進行交互的。Handler可看做是Looper的一個接口,用來向指定的Looper發送消息及定義處理方法。
    默認情況下Handler會與其被定義時所在線程的Looper綁定,比如,Handler在主線程中定義,那麼它是與主線程的Looper綁定。
    mainHandler = new Handler() 等價於new Handler(Looper.myLooper()).
    Looper.myLooper():獲取當前進程的looper對象,類似的 Looper.getMainLooper() 用於獲取主線程的Looper對象。
  3. 在非主線程中直接new Handler() 會報如下的錯誤:
    E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception
    E/AndroidRuntime( 6173): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare()
    原因是非主線程中默認沒有創建Looper對象,需要先調用Looper.prepare()啓用Looper。
  4. Looper.loop(); 讓Looper開始工作,從消息隊列裏取消息,處理消息。
    注意:寫在Looper.loop()之後的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()後,loop纔會中止,其後的代碼才能得以運行。
  5. 基於以上知識,可實現主線程給子線程(非主線程)發送消息。

Http/https

http協議特點:
1.支持客戶/服務器模式
2.簡單快速:客戶向服務端請求服務時,只需傳送請求方式和路徑。
3.靈活:允許傳輸任意類型的數據對象。由Content-Type加以標記。
4.無連接:每次響應一個請求,響應完成以後就斷開連接。
5.無狀態:服務器不保存瀏覽器的任何信息。每次提交的請求之間沒有關聯。

https協議:
HTTPS相當於HTTP的安全版本了,是在http的基礎之上加上ssl(Secure Socket Layer)

在項目中發現每次開機的https請求都會失敗,追溯後發現是時間未同步導致證書驗證失敗,隨即找後端同事增加了安全證書,並本地加載使用。
安全證書:
https://blog.csdn.net/axi295309066/article/details/52494832 詳解
自己的總結下次再寫。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章