編輯:關於Android編程
是android給我們提供用來更新UI的一套機制, 也是一套消息處理的機制, 我們可以發送消息, 也可以通過它處理消息
Android在設計的時候, 就封裝了一套消息創建, 傳遞, 處理機制, 如果不遵循這樣的機制就沒有辦法更新UI信息, 就會拋出一異常信息
文檔表述:
A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it – from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
最根本的原因是解決多線程並發問題.
假設如果在一個Activity當中, 有多個線程去更新UI, 並且都沒有加鎖機制, , 那麼會產生什麼樣的現象呢 ?
更新界面錯亂 !
如果對更新UI的操作都進行加鎖處理的話又會產生什麼樣的問題呢
性能下降!!!!!
處於對以上目的問題的考慮, android給我們一套更新Ui的機制, 我們只需要遵循這樣的機制就可以了,
根本不用關心多線程的問題, 所以更新UI的操作, 都是在主線程的消息隊列當中去輪詢處理的
面試重要!!!!!
Android中對應 “生產者和消費者” 模型
Message : 產品
MessageQueue : 倉庫 (永遠用不到, Android已封裝好)
Looper : 循環
Handler : 對倉庫和循環的一個持有 (通過Handler放置產品) [類似物流]
Handler.Callback 回調接口, 必須自己去實現
Looper
1, 內部包含一個消息隊列也就是MessageQueue, 所有的Handler發送的消息都走向這個消息隊列
2, Looper.looper方法, 就是一個死循環, 不斷的從MessageQueue去取消息, 如果有消息就處理消息, 沒有消息就阻塞
3, 一個線程可以產生一個Looper對象,由它來管理此線程裡的MessageQueue(消息隊列)。
MessageQueue
就是一個消息隊列, 可以添加消息, 並處理消息
Handler
內部會跟Looper進行關聯, 也就是說Handler的內部可以找到Looper, 找到了Looper也就找到了, MessageQueue , 在Handler中發送消息, 其實就是向MessageQueue隊列中發送消息
UIthread 通常就是main thread,而Android啟動程序時會替它建立一個MessageQueue。
handler負責發送消息, Looper負責接收Handler發送的消息, 並直接把消息回傳給handler自己, MessageQueue就是一個存儲消息的容器
Android中的Looper類,是用來封裝消息循環和消息隊列的一個類,用於在android線程中進行消息處理。handler其實可以看做是一個工具類,用來向消息隊列中插入消息的。
(1) Looper類用來為一個線程開啟一個消息循環。 默認情況下android中新誕生的線程是沒有開啟消息循環的。(主線程除外,主線程系統會自動為其創建Looper對象,開啟消息循環。) Looper對象通過MessageQueue來存放消息和事件。一個線程只能有一個Looper,對應一個MessageQueue。
(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才會中止,其後的代碼才能得以運行。
感謝原作者
以上是對Handler整個運行機制內部的詳細描述, 有興趣的同學可以查看一下源碼
每一個消息都需要被指定的Handler處理,通過Handler創建消息便可以完成此功能。Android消息機制中引入了消息池。Handler創建消息時首先查詢消息池中是否有消息存在,如果有直接從消息池中取得,如果沒有則重新初始化一個消息實例。使用消息池的好處是:消息不被使用時,並不作為垃圾回收,而是放入消息池,可供下次Handler創建消息時使用。消息池提高了消息對象的復用,減少系統垃圾回收的次數。消息的創建流程如圖所示。
//推薦使用, Message中有一個消息池, 會循環使用內部的message Message message = Message.obtain(); Message obtain = Message.obtain(handler, 0, String.valueOf(i + 1)) Message.obtain(handler); handler.obtainMessage()
UI主線程初始化第一個Handler時會通過ThreadLocal創建一個Looper,該Looper與UI主線程一一對應。使用ThreadLocal的目的是保證每一個線程只創建唯一一個Looper。之後其他Handler初始化的時候直接獲取第一個Handler創建的Looper。Looper初始化的時候會創建一個消息隊列MessageQueue。至此,主線程、消息循環、消息隊列之間的關系是1:1:1。
Handler、Looper、MessageQueue的初始化流程如圖所示:
Hander持有對UI主線程消息隊列MessageQueue和消息循環Looper的引用,子線程可以通過Handler將消息發送到UI線程的消息隊列MessageQueue中
handler.sendMessage(message); //在有Handler對象的前提下調用 message.sendToTarget();
UI主線程通過Looper循環查詢消息隊列UI_MQ,當發現有消息存在時會將消息從消息隊列中取出。首先分析消息,通過消息的參數判斷該消息對應的Handler,然後將消息分發到指定的Handler進行處理。
子線程通過Handler、Looper與UI主線程通信的流程如圖所示。
// 臨時解決方案, 如果代碼規范的話不會出現下面的情況 Message obtain = Message.obtain(handler, new Runnable() { @Override public void run() { //可以在這個方法中執行UI操作 } }); //不會調用handler中的handleMessage, 而是調用上面的run方法 handler.sendMessage(obtain); handler.post(new Runnable() { @Override public void run() { } }); runOnUiThread(new Runnable() { @Override public void run() { } });
感謝原作者
只要在一個應用中, 所有的Activity共用一個Application, 保存一些全局的變量
注意: 所有的應用被銷毀的時候, Application不一定被銷毀無論怎麼用, Application只會創建一次
BaseApplication.java
public class BaseApplication extends Application { private static final String TAG = BaseApplication.class.getSimpleName(); private String text; /** * 啟動時, 只執行一次, 做應用的初始化 */ @Override public void onCreate() { super.onCreate(); Log.d(TAG, "onCreate: "); text = "BaseApplication"; } public String getText() { return text; } public void setText(String text) { this.text = text; } }
在清單文件中添加
在MainActivity中獲取創建的Application
private static final String TAG = MainActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, "onCreate: "); setContentView(R.layout.activity_main); BaseApplication application = (BaseApplication) getApplication(); String text = application.getText(); Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show(); application.setText("啟動一次後"); }
異步任務AsyncTask AsyncTask主要用來更新UI線程,比較耗時的操作可以在AsyncTask中使用。 AsyncTask是個抽象類,使用時需要繼承這個類
在應用開發中,有時我們需要用代碼計算布局的高度,可能需要減去狀態欄(status bar)的高度。狀態欄高度定義在Android系統尺寸資源中stat
一、概述 Android4.4的電池管理功能用於管理電池的充、放電功能。整個電池管理的部分包括Linux電池驅動、Android電池服務、電池屬性和參數、電
1.打印日志:logt,回車:自動生成靜態Tag: private static final String TAG = "MainActivity";