安卓looper怎麼工作的
Ⅰ android中looper的實現原理,為什麼調用looper.prepare就在當前線程關聯了一個lo
實際上:消息發送和計劃任務提交之後,它們都會進入某線程的消息隊列中,我們可以把這個線程稱之為目標線程。不論是主線程還是子線程都可以成為目標線程。上例中之所以在主線程中處理消息,是因為我們要更新UI,按照android中的規定我們必須由主線程更新UI。所以我們讓主線程成為了目標線程。
那麼如何控制讓某個線程成為目標線程呢?
這就引出了Looper的概念。Android系統中實現了消息循環機制,Android的消息循環是針對線程的,每個線程都可以有自己的消息隊列和消息循環。Android系統中的通過Looper幫助線程維護著一個消息隊列和消息循環。通過Looper.myLooper()得到當前線程的Looper對象,通過Looper.getMainLooper()得到當前進程的主線程的Looper對象。
前面提到每個線程都可以有自己的消息隊列和消息循環,然而我們自己創建的線程默認是沒有消息隊列和消息循環的(及Looper),要想讓一個線程具有消息處理機制我們應該在線程中先調用Looper.prepare()來創建一個Looper對象,然後調用Looper.loop()進入消息循環。如上面的源碼所示。
當我們用Handler的構造方法創建Handler對象時,指定handler對象與哪個具有消息處理機制的線程(具有Looper的線程)相關聯,這個線程就成了目標線程,可以接受消息和計劃任務了。Handler中的構造方法如下:
[java] view
plainprint?
public Handler() {
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 = null;
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
public Handler() {
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 = null;
}
public Handler(Looper looper) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = null;
}
在上述的計時器的例子中,之所以可以在主線程中處理消息而我們自己並沒有調用Looper.prepare()等方法,是因為Android系統在Activity啟動時為其創建一個消息隊列和消息循環,當我們用無參的Handler構造方法創建對象時又用了當前線程的Looper對象,及將handler與主線程中的Looper對象進行了關聯。
android中是使用Looper機制來完成消息循環的,但每次創建線程時都先初始化Looper比較麻煩,因此Android為我們提供了一個HandlerThread類,他封裝了Looper對象,是我們不用關心Looper的開啟和釋放問題。
不管是主線程還是其他線程只要有Looper的線程,別的線程就可以向這個線程的消息隊列中發送消息和任務。
我們使用HandlerThread類代替上一篇文章中的子線程,並用HandlerThread類中的Looper對象構造Handler,則接受消息的目標線程就不是主線程了,而是HandlerThread線程。代碼如下:
[java] view
plainprint?
public class clockActivity extends Activity {
/** Called when the activity is first created. */
private String TAG="clockActivity";
private Button endButton;
private TextView textView;
private int timer=0;
private boolean isRunning=true;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
endButton=(Button)findViewById(R.id.endBtn);
textView=(TextView)findViewById(R.id.textview);
endButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
isRunning=false;
}
});
HandlerThread thread=new HandlerThread("myThread");
handler=new Handler(thread.getLooper());//與HandlerThread中的Looper對象關聯
thread.start();
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
if(isRunning){
textView.setText("走了"+timer+"秒");
timer++;
handler.postDelayed(this, 1000);//提交任務r,延時1秒執行
}
}
};
handler.postDelayed(r, 1000);
}
}
public class clockActivity extends Activity {
/** Called when the activity is first created. */
private String TAG="clockActivity";
private Button endButton;
private TextView textView;
private int timer=0;
private boolean isRunning=true;
private Handler handler;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
endButton=(Button)findViewById(R.id.endBtn);
textView=(TextView)findViewById(R.id.textview);
endButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
isRunning=false;
}
});
HandlerThread thread=new HandlerThread("myThread");
handler=new Handler(thread.getLooper());//與HandlerThread中的Looper對象關聯
thread.start();
Runnable r=new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
if(isRunning){
textView.setText("走了"+timer+"秒");
timer++;
handler.postDelayed(this, 1000);//提交任務r,延時1秒執行
}
}
};
handler.postDelayed(r, 1000);
}
}
此時處理任務會在handlerThread線程中完成。當然這個例子會出線異常:依然是因為在非主線程中更新了UI。這樣做只是為了大家能夠理解這種機制。
深入理解Android消息處理機制對於應用程序開發非常重要,也可以讓我們對線程同步有更加深刻的認識,希望這篇文章可以對朋友們有所幫助。
Ⅱ android looper 怎麼理解
Android
中
Looper
和
handler
的聯系及處理
最近一直在看
Android
內核的代碼。把
Looper
及
Handler
這一節先分享一下:
Handle
主要起一個代理中介的作用,具體對象要向底層發請求,得通過
Handler
,
Handler
把這個請求放進對應的
MessageQueue
(消息隊列)中,而
MessageQueue
是由
Looper
來維護的。
Looper
中有一個循環,循環讀取
MessageQueue
中的請求,發給相應的底層處理(這個過程有點復雜)。當底層
有信息返回時,
也會先放進
MessageQueue
中,
然後對應的
Looper
遍歷,
交給對
應的
Handler
處理。
Handler
找出對應的回調函數或其他已注冊的相關方法,讓
其進行處理,最終在界面有一個反應。
以下是我寫的一個
demo
,
主要是如何關聯
Looper
、
Handler
、
以及對應的線程
(
MessageQueue
是
Looper
的一個成員)。
1.
測試方法入口:
testLooper1.java
package com.linquan.test.loop1;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
public class testLooper1 extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Log.d(MyConstant.TAG, "main start...");
new MyThread().start();
Message msg = new Message();
msg.what = MyConstant.CONSTANT3;
while(MyThread.h == null){
Log.d(MyConstant.TAG, "handler is not init ! please wait a
monment....");
try {
Thread.sleep(300);//
由於這里是測試,
所以加上此行代碼,
避免出錯。
實
際中多為用戶觸發事件,此時
handle
應該已經初始化了。
} catch (InterruptedException e) {
e.printStackTrace();
Ⅲ android裡面所說的looper是什麼意思啊
Message:消息,其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。
Handler:處理者,負責Message的發送及處理。使用Handler時,需要實現handleMessage(Message
msg)方法來對特定的Message進行處理,例如更新UI等。
MessageQueue:消息隊列,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等待Looper的抽取。
Looper:消息泵,不斷地從MessageQueue中抽取Message執行。因此,一個MessageQueue需要一個Looper。
Thread:線程,負責調度整個消息循環,即消息循環的執行場所
Ⅳ looper的作用 android 為什麼要循環
給你舉個例子:
用handler傳遞消息時候。你先sendmessage。這時候消息到looper然後你再looper當中更具msg.what獲取相應的消息。looper相當於一個消息收取中介。所以要有循環。我自己的理解。
Ⅳ Android的handler機制的原理
Android的handler機制的原理分為非同步通信准備,消息發送,消息循環,消息處理。
1、非同步通信准備
在主線程中創建處理器對象(Looper)、消息隊列對象(Message Queue)和Handler對象。
2、消息入隊
工作線程通過Handler發送消息(Message) 到消息隊列(Message Queue)中。
3、消息循環
消息出隊: Looper循環取出消息隊列(Message Queue) 中的的消息(Message)。
消息分發: Looper將取出的消息 (Message) 發送給創建該消息的處理者(Handler)。
4、消息處理
處理者(Handler) 接收處理器(Looper) 發送過來的消息(Message),根據消息(Message) 進行U操作。
handler的作用
handler是android線程之間的消息機制,主要的作用是將一個任務切換到指定的線程中去執行,(准確的說是切換到構成handler的looper所在的線程中去出處理)android系統中的一個例子就是主線程中的所有操作都是通過主線程中的handler去處理的。
Handler的運行需要底層的 messagequeue和 looper做支撐。
Ⅵ [Android源碼分析] - 非同步通信Handler機制
一、問題:在Android啟動後會在新進程里創建一個主線程,也叫UI線程( 非線程安全 )這個線程主要負責監聽屏幕點擊事件與界面繪制。當Application需要進行耗時操作如網路請求等,如直接在主線程進行容易發生ANR錯誤。所以會創建子線程來執行耗時任務,當子線程執行完畢需要通知UI線程並修改界面時,不可以直接在子線程修改UI,怎麼辦?
解決方法:Message Queue機制可以實現子線程與UI線程的通信。
該機制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable對象 發給Looper,由它把消息放入所屬線程的消息隊列中,然後Looper又會自動把消息隊列里的消息/Runnable對象 廣播 到所屬線程里的Handler,由Handler處理接收到的消息或Runnable對象。
1、Handler
每次創建Handler對象時,它會自動綁定到創建它的線程上。如果是主線程則默認包含一個Message Queue,否則需要自己創建一個消息隊列來存儲。
Handler是多個線程通信的信使。比如在線程A中創建AHandler,給它綁定一個ALooper,同時創建屬於A的消息隊列AMessageQueue。然後在線程B中使用AHandler發送消息給ALooper,ALooper會把消息存入到AMessageQueue,然後再把AMessageQueue廣播給A線程里的AHandler,它接收到消息會進行處理。從而實現通信。
2、Message Queue
在主線程里默認包含了一個消息隊列不需要手動創建。在子線程里,使用Looper.prepare()方法後,會先檢查子線程是否已有一個looper對象,如果有則無法創建,因為每個線程只能擁有一個消息隊列。沒有的話就為子線程創建一個消息隊列。
Handler類包含Looper指針和MessageQueue指針,而Looper里包含實際MessageQueue與當前線程指針。
下面分別就UI線程和worker線程講解handler創建過程:
首先,創建handler時,會自動檢查當前線程是否包含looper對象,如果包含,則將handler內的消息隊列指向looper內部的消息隊列,否則,拋出異常請求執行looper.prepare()方法。
- 在 UI線程 中,系統自動創建了Looper 對象,所以,直接new一個handler即可使用該機制;
- 在 worker線程 中,如果直接創建handler會拋出運行時異常-即通過查『線程-value』映射表發現當前線程無looper對象。所以需要先調用Looper.prepare()方法。在prepare方法里,利用ThreadLocal<Looper>對象為當前線程創建一個Looper(利用了一個Values類,即一個Map映射表,專為thread存儲value,此處為當前thread存儲一個looper對象)。然後繼續創建handler, 讓handler內部的消息隊列指向該looper的消息隊列(這個很重要,讓handler指向looper里的消息隊列,即二者共享同一個消息隊列,然後handler向這個消息隊列發送消息,looper從這個消息隊列獲取消息) 。然後looper循環消息隊列即可。當獲取到message消息,會找出message對象里的target,即原始發送handler,從而回調handler的handleMessage() 方法進行處理。
- handler與looper共享消息隊列 ,所以handler發送消息只要入列,looper直接取消息即可。
- 線程與looper映射表 :一個線程最多可以映射一個looper對象。通過查表可知當前線程是否包含looper,如果已經包含則不再創建新looper。
5、基於這樣的機制是怎樣實現線程隔離的,即在線程中通信呢。
核心在於 每一個線程擁有自己的handler、message queue、looper體系 。而 每個線程的Handler是公開 的。B線程可以調用A線程的handler發送消息到A的共享消息隊列去,然後A的looper會自動從共享消息隊列取出消息進行處理。反之一樣。
二、上面是基於子線程中利用主線程提供的Handler發送消息出去,然後主線程的Looper從消息隊列中獲取並處理。那麼還有另外兩種情況:
1、主線程發送消息到子線程中;
採用的方法和前面類似。要在子線程中實例化AHandler並設定處理消息的方法,同時由於子線程沒有消息隊列和Looper的輪詢,所以要加上Looper.prepare(),Looper.loop()分別創建消息隊列和開啟輪詢。然後在主線程中使用該AHandler去發送消息即可。
2、子線程A與子線程B之間的通信。
1、 Handler為什麼能夠實現不同線程的通信?核心點在哪?
不同線程之間,每個線程擁有自己的Handler、消息隊列和Looper。Handler是公共的,線程可以通過使用目標線程的Handler對象來發送消息,這個消息會自動發送到所屬線程的消息隊列中去,線程自帶的Looper對象會不斷循環從裡面取出消息並把消息發送給Handler,回調自身Handler的handlerMessage方法,從而實現了消息的線程間傳遞。
2、 Handler的核心是一種事件激活式(類似傳遞一個中斷)的還是主要是用於傳遞大量數據的?重點在Message的內容,偏向於數據傳輸還是事件傳輸。
目前的理解,它所依賴的是消息隊列,發送的自然是消息,即類似事件中斷。
0、 Android消息處理機制(Handler、Looper、MessageQueue與Message)
1、 Handler、Looper源碼閱讀
2、 Android非同步消息處理機制完全解析,帶你從源碼的角度徹底理解
謝謝!
wingjay

Ⅶ android裡面所說的looper是什麼意思啊
Looper即:有消息循環的線程。
在Android里線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個Looper,這個事android的新概念。主線程(UI線程)就是一個消息循環的線程。針對這種消息循環的機制,引入一個新的機制Handle,有消息循環,就要往消息循環里 面發送相應的消息,自定義消息一般都會有對應的處理,消息的發送和清除,消息的處理,把這些都封裝在Handle裡面,注意Handle只是針對那些有Looper的線程,不管是UI線程還是子線程,只要有Looper,就可以往消息隊列裡面添加東西,並做相應的處理。
Ⅷ Handler的工作原理。為什麼在子線程中使用Handler會拋出異常
Handler了它在安卓中常見的工作就是子線程與主線程的通信,其實可以直接歸類為線程和線程間的通信
談到Handler會涉及到以下幾個類
Handler、Message、Looper、MessageQueue
我們一個一個來說,首先設置場景在一個線程中通知另一個線程
1,創建Looper和創建MessageQueue
首先在一個線程里要用Handler,那麼需要准備Looper,調用Looper.prepare(),我們在主線程裡面不用准備Looper,那麼是因為在我們的主線程中已經給我們初始化好了Looper,
在准備Looper的時候會去校驗這個線程中是否存在Looper,如果有Looper那麼拋出異常(注意:有一些面試官會問如何判斷一個線程中是否存在一個Looper的,它用的是ThreadLocal,它的作用是在線程范圍內保證變數的唯一性,Thread中會維護一個類似HashMap的東西,然後用ThreadLocal對象作為key,value就是要存儲的變數值,這樣就保證了存儲數據的唯一性)
如果沒有Looper那麼new一個Looper,new Looper的同時會new 一個MessageQueue(注意:一個線程中只有一個Looper一個MessageQueue)
然後Looper.loop()就可以啟動輪訓來輪訓消息隊列了
2,創建Handler
這個我們在熟悉不過了,繼承一個Handler然後復寫handlerMessage方法,這里其實面試官也可以問一些比較細致的問題,如下
注意:有面試官會問可以創建幾個Handler,我負責任的說是多個,哪個Handler發送的消息哪個Handler處理(吐槽一下有的面試官,你自己把自己要問的問題搞清楚了在去問別人,非要跟我犟只能創建一個)
3,創建Message
Message一般都是使用Message.obtain(),它這裡面是有一個spool指向一個Message對象,還有一個next指向下一個Message,它裡面維護了一個鏈表,obtain的時候在表頭頭取Message,在Message回收的時候在表頭添加一個Message,類似棧,默認大小是50
4,消息的處理
Handler對象sendMessage發送消息放入的MessageQueue隊列中,Looper輪訓到它,然後就開始處理Message,Message會有一個target去記錄是哪個Handler發送的它,會調用這個Handler中的dispatchMessage()方法,如果說Message中實現了CallBack那麼調用Message中的CallBack,如果Handler中實現了Callback調用Handler中CallBack,否則就都調動Handler中的handleMessage方法
根據上面的原理分析,那就是因為你子線程中沒有Looper,給他一個Looper就好了
Ⅸ Android-Looper
Looper.loop是一個死循環,拿不到需要處理的Message就會阻塞,那在UI線程中為什麼不會導致ANR?
首先我們來看造成ANR的原因:
1.當前的事件沒有機會得到處理(即主線程正在處理前一個事件,沒有及時的完成或者looper被某種原因阻塞住了)
2.當前的事件正在處理,但沒有及時完成
我們再來看一下APP的入口ActivityThread的main方法:
顯而易見的,如果main方法中沒有looper進行死循環,那麼主線程一運行完畢就會退出,會導致直接崩潰,還玩什麼!
現在我們知道了消息循環的必要性,那為什麼這個死循環不會造成ANR異常呢?
我們知道Android 的是由事件驅動的,looper.loop() 不斷地接收事件、處理事件,每一個點擊觸摸或者說Activity的生命周期都是運行在 Looper的控制之下,如果它停止了,應用也就停止了。只能是某一個消息或者說對消息的處理阻塞了 Looper.loop(),而不是 Looper.loop() 阻塞它,這也就是我們為什麼不能在UI線程中處理耗時操作的原因。
主線程Looper從消息隊列讀取消息,當讀完所有消息時,主線程阻塞。子線程往消息隊列發送消息,喚醒主線程,主線程被喚醒只是為了讀取消息,當消息讀取完畢,再次睡眠。因此loop的循環並不會對CPU性能有過多的消耗。
初始化當前線程和Looper,這樣可以在實際開始啟動循環(loop())之前創建一個Handler並且關聯一個looper。確保在先調用這個方法,然後調用loop()方法,並且通過調用quit()結束。
這裡面的入參boolean表示Looper是否允許退出,true就表示允許退出,對於false則表示Looper不允許退出。
初始化當前當前線程的looper。並且標記為一個程序的主Looper。由Android環境來創建應用程序的主Looper。因此這個方法不能由咱們來調用。另請參閱prepare()
這里的sThreadLocal.get()是和prepare(boolean)方法裡面的sThreadLocal.set(new Looper(quitAllowed));一一對應的。而在prepareMainLooper()方法裡面。
退出循環
將終止(loop()方法)而不處理消息隊列中的任何更多消息。在調用quit()後,任何嘗試去發送消息都是失敗的。例如Handler.sendMessage(Message)方法將返回false。因為循環終止之後一些message可能會被無法傳遞,所以這個方法是不安全的。可以考慮使用quitSafely()方法來確保所有的工作有序地完成。
安全退出循環
調用quitSafely()方法會使循環結束,只要消息隊列中已經被傳遞的所有消息都將被處理。然而,在循環結束之前,將來不會提交處理延遲消息。
調用退出後,所有嘗試去發送消息都將失敗。就像調用Handler.sendMessage(Message)將返回false。