編輯:關於Android編程
1、Handler的由來
當程序第一次啟動的時候,Android會同時啟動一條主線程( Main Thread)來負責處理與UI相關的事件,我們叫做UI線程。
Android的UI操作並不是線程安全的(出於性能優化考慮),意味著如果多個線程並發操作UI線程,可能導致線程安全問題。
為了解決Android應用多線程問題—Android平台只允許UI線程修改Activity裡的UI組建,就會導致新啟動的線程無法改變界面組建的屬性值。
簡單的說:當主線程隊列處理一個消息超過5秒,android 就會拋出一個 ANP(無響應)的異常,所以,我們需要把一些要處理比較長的消息,放在一個單獨線程裡面處理,把處理以後的結果,返回給主線程運行,就需要用的Handler來進行線程建的通信。
2、Handler的作用
2.1 讓線程延時執行
主要用到的兩個方法:
final boolean postAtTime(Runnable r, long uptimeMillis)
final boolean postDelayed(Runnable r, long delayMillis)
2.2 讓任務在其他線程中執行並返回結果
分為兩個步驟:
在新啟動的線程中發送消息
使用Handler對象的sendMessage()方法或者SendEmptyMessage()方法發送消息。
在主線程中獲取處理消息
重寫Handler類中處理消息的方法(void handleMessage(Message msg)),當新啟動的線程發送消息時,消息發送到與之關聯的MessageQueue。而Hanlder不斷地從MessageQueue中獲取並處理消息。
3、Handler更新UI線程一般使用
首先要進行Handler 申明,復寫handleMessage方法( 放在主線程中)
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// TODO 接收消息並且去更新UI線程上的控件內容
if (msg.what == UPDATE) {
// 更新界面上的textview
tv.setText(String.valueOf(msg.obj));
}
super.handleMessage(msg);
}
};
子線程發送Message給ui線程表示自己任務已經執行完成,主線程可以做相應的操作了。
new Thread() {
@Override
public void run() {
// TODO 子線程中通過handler發送消息給handler接收,由handler去更新TextView的值
try {
//do something
Message msg = new Message();
msg.what = UPDATE;
msg.obj = "更新後的值" ;
handler.sendMessage(msg);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
4、Handler原理分析
4.1 Handler的構造函數
第①個和第②個構造函數都沒有傳遞Looper,這兩個構造函數都將通過調用Looper.myLooper()獲取當前線程綁定的Looper對象,然後將該Looper對象保存到名為mLooper的成員字段中。① public Handler()
② public Handler(Callbackcallback)
③ public Handler(Looperlooper)
④ public Handler(Looperlooper, Callbackcallback)
113 public Handler() {
114 this(null, false);
115 }
127 public Handler(Callback callback) {
128 this(callback, false);
129 }
//他們會調用Handler的內部構造方法
188 public Handler(Callback callback, boolean async) {
189 if (FIND_POTENTIAL_LEAKS) {
190 final Class klass = getClass();
191 if ((klass.isAnonymousClass() ||klass.isMemberClass()
|| klass.isLocalClass()) &&
192 (klass.getModifiers() & Modifier.STATIC) == 0) {
193 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
194 klass.getCanonicalName());
195 }
196 }
197/************************************
198 mLooper = Looper.myLooper();
199 if (mLooper == null) {
200 throw new RuntimeException(
201 "Can't create handler inside thread that has not called Looper.prepare()");
202 }
203 mQueue = mLooper.mQueue;
204 mCallback = callback;
205 mAsynchronous = async;
206 }
我們看到暗紅色的重點部分:
通過Looper.myLooper()獲取了當前線程保存的Looper實例,又通過這個Looper實例獲取了其中保存的MessageQueue(消息隊列)。每個Handler 對應一個Looper對象,產生一個MessageQueue
136 public Handler(Looper looper) {
137 this(looper, null, false);
138 }
147 public Handler(Looper looper, Callback callback) {
148 this(looper, callback, false);
149 }
//他們會調用Handler的內部構造方法
227 public Handler(Looper looper, Callback callback, boolean async) {
228 mLooper = looper;
229 mQueue = looper.mQueue;
230 mCallback = callback;
231 mAsynchronous = async;
232 }
第②個和第④個構造函數還傳遞了Callback對象,Callback是Handler中的內部接口,需要實現其內部的handleMessage方法,Callback代碼如下:
80 public interface Callback {
81 public boolean More ...handleMessage(Message msg);
82 }
Handler.Callback是用來處理Message的一種手段,如果沒有傳遞該參數,那麼就應該重寫Handler的handleMessage方法,也就是說為了使得Handler能夠處理Message,我們有兩種辦法:
1. 向Hanlder的構造函數傳入一個Handler.Callback對象,並實現Handler.Callback的handleMessage方法
2. 無需向Hanlder的構造函數傳入Handler.Callback對象,但是需要重寫Handler本身的handleMessage方法
也就是說無論哪種方式,我們都得通過某種方式實現handleMessage方法,這點與Java中對Thread的設計有異曲同工之處。
4.2 Handle發送消息的幾個方法源碼
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
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);
}
我們可以看出他們最後都調用了sendMessageAtTime(),然後返回了enqueueMessage方法,下面看一下此方法源碼:
626 private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//把當前的handler作為msg的target屬性
627 msg.target = this;
628 if (mAsynchronous) {
629 msg.setAsynchronous(true);
630 }
631 return queue.enqueueMessage(msg, uptimeMillis);
632 }
在該方法中有兩件事需要注意:
msg.target = this
該代碼將Message的target綁定為當前的Handler
queue.enqueueMessage過下圖可以看到完整的方法調用順序:
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxzdHJvbmc+NaGiTG9vcGVy1K3A7bfWzvY8L3N0cm9uZz4NCjxwPs7Sw8fSu7Dj1NrW98/fs8zJ6sP3SGFuZGxlcqOs09DKsc7Sw8fQ6NKqvMyz0FRocmVhZMDgyrXP1tfUvLq1xM/fs8y5psTco6y1sc7Sw8fU2sDvw+bJ6sP3SGFuZGxlcrXEyrG68rvhsai07aGjxuTUrdLyysfW98/fs8zW0NLRvq3Ktc/WwcvBvbj21tjSqrXETG9vcGVyt723qKOsz8LD5r+00ru/tEFjdGl2aXR5VGhyZWFkLmphdmHW0G1haW63vbeotcTUtMLro7o8L3A+DQo8cHJlIGNsYXNzPQ=="brush:java;">
public static void main(String[] args) {
//......省略
5205 Looper.prepareMainLooper();//>
5206
5207 ActivityThread thread = new ActivityThread();
5208 thread.attach(false);
5209
5210 if (sMainThreadHandler == null) {
5211 sMainThreadHandler = thread.getHandler();
5212 }
5213
5214 AsyncTask.init();
5215
5216 if (false) {
5217 Looper.myLooper().setMessageLogging(new
5218 LogPrinter(Log.DEBUG, "ActivityThread"));
5219 }
5220
5221 Looper.loop();//>
5222
5223 throw new RuntimeException("Main thread loop unexpectedly exited");
5224 }
5225}
5.1 首先看prepare()方法
70 public static void prepare() {
71 prepare(true);
72 }
73
74 private static void prepare(boolean quitAllowed) {
//證了一個線程中只有一個Looper實例
75 if (sThreadLocal.get() != null) {
76 throw new RuntimeException("Only one Looper may be created per thread");
77 }
78 sThreadLocal.set(new Looper(quitAllowed));
79 }
該方法會調用Looper構造函數同時實例化出MessageQueue和當前thread.
186 private Looper(boolean quitAllowed) {
187 mQueue = new MessageQueue(quitAllowed);
188 mThread = Thread.currentThread();
189 }
182 public static MessageQueue myQueue() {
183 return myLooper().mQueue;
184 }
prepare()方法中通過ThreadLocal對象實現Looper實例與線程的綁定。
5.2 loop()方法
109 public static void loop() {
110 final Looper me = myLooper();
111 if (me == null) {
112 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
113 }
114 final MessageQueue queue = me.mQueue;
115
118 Binder.clearCallingIdentity();
119 final long ident = Binder.clearCallingIdentity();
120
121 for (;;) {
122 Message msg = queue.next(); // might block
123 if (msg == null) {
124
125 return;
126 }
127
129 Printer logging = me.mLogging;
130 if (logging != null) {
131 logging.println(">>>>> Dispatching to " + msg.target + " " +
132 msg.callback + ": " + msg.what);
133 }
//重點****
135 msg.target.dispatchMessage(msg);
136
137 if (logging != null) {
138 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
139 }
140
142 // identity of the thread wasn't corrupted.
143 final long newIdent = Binder.clearCallingIdentity();
144 if (ident != newIdent) {
145 Log.wtf(TAG, "Thread identity changed from 0x"
146 + Long.toHexString(ident) + " to 0x"
147 + Long.toHexString(newIdent) + " while dispatching to "
148 + msg.target.getClass().getName() + " "
149 + msg.callback + " what=" + msg.what);
150 }
151
152 msg.recycleUnchecked();
153 }
154 }
首先looper對象不能為空,就是說loop()方法調用必須在prepare()方法的後面。
Looper一直在不斷的從消息隊列中通過MessageQueue的next方法獲取Message,然後通過代碼msg.target.dispatchMessage(msg)讓該msg所綁定的Handler(Message.target)執行dispatchMessage方法以實現對Message的處理。
Handler的dispatchMessage的源碼如下:
93 public void dispatchMessage(Message msg) {
94 if (msg.callback != null) {
95 handleCallback(msg);
96 } else {
97 if (mCallback != null) {
98 if (mCallback.handleMessage(msg)) {
99 return;
100 }
101 }
102 handleMessage(msg);
103 }
104 }
我們可以看到Handler提供了三種途徑處理Message,而且處理有前後優先級之分:首先嘗試讓postXXX中傳遞的Runnable執行,其次嘗試讓Handler構造函數中傳入的Callback的handleMessage方法處理,最後才是讓Handler自身的handleMessage方法處理Message。
6、如何在子線程中使用HandlerHandler本質是從當前的線程中獲取到Looper來監聽和操作MessageQueue,當其他線程執行完成後回調當前線程。
子線程需要先prepare()才能獲取到Looper的,是因為在子線程只是一個普通的線程,其ThreadLoacl中沒有設置過Looper,所以會拋出異常,而在Looper的prepare()方法中sThreadLocal.set(new Looper())是設置了Looper的。
6.1 實例代碼
定義一個類實現Runnable接口或繼承Thread類(一般不繼承)。
class Rub implements Runnable {
public Handler myHandler;
// 實現Runnable接口的線程體
@Override
public void run() {
/*①、調用Looper的prepare()方法為當前線程創建Looper對象並,
創建Looper對象時,它的構造器會自動的創建相對應的MessageQueue*/
Looper.prepare();
/*.②、創建Handler子類的實例,重寫HandleMessage()方法,該方法處理除當前線程以外線程的消息*/
myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
String ms = "";
if (msg.what == 0x777) {
}
}
};
//③、調用Looper的loop()方法來啟動Looper讓消息隊列轉動起來
Looper.loop();
}
}
注意分成三步:
1.調用Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。
2.有了Looper之後,創建Handler子類實例,重寫HanderMessage()方法,該方法負責處理來自於其他線程的消息。
3.調用Looper的looper()方法啟動Looper。
然後使用這個handler實例在任何其他線程中發送消息,最終處理消息的代碼都會在你創建Handler實例的線程中運行。
7、總結
Handler:
發送消息,它能把消息發送給Looper管理的MessageQueue。
處理消息,並負責處理Looper分給它的消息。
Message:
Handler接收和處理的消息對象。
Looper:
每個線程只有一個Looper,它負責管理對應的MessageQueue,會不斷地從MessageQueue取出消息,並將消息分給對應的Hanlder處理。
主線程中,系統已經初始化了一個Looper對象,因此可以直接創建Handler即可,就可以通過Handler來發送消息、處理消息。 程序自己啟動的子線程,程序必須自己創建一個Looper對象,並啟動它,調用Looper.prepare()方法。
prapare()方法:保證每個線程最多只有一個Looper對象。
looper()方法:啟動Looper,使用一個死循環不斷取出MessageQueue中的消息,並將取出的消息分給對應的Handler進行處理。
MessageQueue:由Looper負責管理,它采用先進先出的方式來管理Message。
Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue想關聯。
Handler的sendMessage方法,會給msg的target賦值為handler自身,然後加入MessageQueue中。
android 圖片放大縮小的邊界簡單的限制處理首先,你要明白,即使是微信這樣出色的軟件對4邊界限制處理也不是很完善的。具體你可以在上邊界將圖片放大之後再縮小,等等。所以
與SAX和PULL解析不同,Dom解析是將XML文件全部載入,組裝成一顆Dom樹,然後通過節點以及節點之間的關系來解析XML文件,占用內存比較大,一般比較推薦用SAX和P
Android技術精髓-BackupActivity 首先介紹下今天主題BackupActivity功能:在Android應用UI activity 中繼承Asy
在安卓手機上,不少用戶都會遇過com.android.phone已停止的彈窗,尤其經常刷機的最明顯。導致的原因實在太多,有刷機步驟不對的,亂改系統文件的,這