當前位置:首頁 » 安卓系統 » androidlooper

androidlooper

發布時間: 2023-01-02 12:36:23

❶ 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。

❷ Android如何保證一個線程最多隻能有一個Looper

Looper的構造方法為private,所以不能直接使用其構造方法創建。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}1234

要想在當前線程創建Looper,需使用Looper的prepare方法,Looper.prepare()。
如果現在要我們來實現Looper.prepare()這個方法,我們該怎麼做?我們知道,Android中一個線程最多隻能有一個Looper,若在已有Looper的線程中調用Looper.prepare()會拋出RuntimeException(「Only one Looper may be created per thread」)。面對這樣的需求,我們可能會考慮使用一個HashMap,其中Key為線程ID,Value為與線程關聯的Looper,再加上一些同步機制,實現Looper.prepare()這個方法,代碼如下:

❸ 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的實現原理,為什麼調用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】【Choreographer】篇

目錄:
1 MessageQueue next()
2 Vsync
3 Choreographer doFrame
4 input

系統是一個無限循環的模型, Android也不例外,進程被創建後就陷入了無限循環的狀態

系統運行最重要的兩個概念:輸入,輸出。

Android 中輸入 輸出 的往復循環都是在 looper 中消息機制驅動下完成的

looper 的循環中, messageQueue next 取消息進行處理, 處理輸入事件, 進行輸出, 完成和用戶交互

應用生命周期內會不斷 產生 message 到 messageQueue 中, 有: java層 也有 native層

其中最核心的方法就是 messageQueue 的 next 方法, 其中會先處理 java 層消息, 當 java 層沒有消息時候, 會執行 nativePollOnce 來處理 native 的消息 以及監聽 fd 各種事件

從硬體來看, 屏幕不會一直刷新, 屏幕的刷新只需要符合人眼的視覺停留機制

24Hz , 連續刷新每一幀, 人眼就會認為畫面是流暢的

所以我們只需要配合上這個頻率, 在需要更新 UI 的時候執行繪制操作

如何以這個頻率進行繪制每一幀: Android 的方案是 Vsync 信號驅動。

Vsync 信號的頻率就是 24Hz , 也就是每隔 16.6667 ms 發送一次 Vsync 信號提示系統合成一幀。

監聽屏幕刷新來發送 Vsync 信號的能力,應用層 是做不到的, 系統是通過 jni 回調到 Choreographer 中的 Vsync 監聽, 將這個重要信號從 native 傳遞到 java 層。

總體來說 輸入事件獲取 Vsync信號獲取 都是先由 native 捕獲事件 然後 jni 到 java 層實現業務邏輯

執行的是 messageQueue 中的關鍵方法: next

next 主要的邏輯分為: java 部分 和 native 部分

java 上主要是取java層的 messageQueue msg 執行, 無 msg 就 idleHandler

java層 無 msg 會執行 native 的 pollOnce@Looper

native looper 中 fd 監聽封裝為 requestQueue, epoll_wait 將 fd 中的事件和對應 request 封裝為 response 處理, 處理的時候會調用 fd 對應的 callback 的 handleEvent

native 層 pollOnce 主要做的事情是:

vsync 信號,輸入事件, 都是通過這樣的機制完成的。

epoll_wait 機制 拿到的 event , 都在 response pollOnce pollInner 處理了

這里的 dispatchVsync 從 native 回到 java 層

native:

java:

收到 Vsync 信號後, Choreographer 執行 doFrame

應用層重要的工作幾乎都在 doFrame 中

首先看下 doFrame 執行了什麼:

UI 線程的核心工作就在這幾個方法中:

上述執行 callback 的過程就對應了圖片中 依次處理 input animation traversal 這幾個關鍵過程

執行的周期是 16.6ms, 實際可能因為一些 delay 造成一些延遲、丟幀

input 事件的整體邏輯和 vsync 類似

native handleEvent ,在 NativeInputEventReceiver 中處理事件, 區分不同事件會通過 JNI

走到 java 層,WindowInputEventReceiver 然後進行分發消費

native :

java:

input事件的處理流程:

輸入event deliverInputEvent

deliver的 input 事件會來到 InputStage

InputStage 是一個責任鏈, 會分發消費這些 InputEvent

下面以滑動一下 recyclerView 為例子, 整體邏輯如下:

vsync 信號到來, 執行 doFrame,執行到 input 階段

touchEvent 消費, recyclerView layout 一些 ViewHolder

scroll 中 fill 結束,會執行 一個 recyclerView viewProperty 變化, 觸發了invalidate

invalidate 會走硬體加速, 一直到達 ViewRootImpl , 從而將 Traversal 的 callback post choreographer執行到 traversal 階段就會執行

ViewRootImpl 執行 performTraversal , 會根據目前是否需要重新layout , 然後執行layout, draw 等流程

整個 input 到 traversal 結束,硬體繪制後, sync 任務到 GPU , 然後合成一幀。

交給 SurfaceFlinger 來顯示。

SurfaceFlinger 是系統進程, 每一個應用進程是一個 client 端, 通過 IPC 機制,client 將圖像顯示工作交給 SurfaceFlinger

launch 一個 app:

❻ android裡面所說的looper是什麼意思啊

Looper即:有消息循環的線程。
在Android里線程分為有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個Looper,這個事android的新概念。主線程(UI線程)就是一個消息循環的線程。針對這種消息循環的機制,引入一個新的機制Handle,有消息循環,就要往消息循環里 面發送相應的消息,自定義消息一般都會有對應的處理,消息的發送和清除,消息的處理,把這些都封裝在Handle裡面,注意Handle只是針對那些有Looper的線程,不管是UI線程還是子線程,只要有Looper,就可以往消息隊列裡面添加東西,並做相應的處理。

熱點內容
編程找點 發布:2025-05-15 20:43:10 瀏覽:586
php上傳臨時文件夾 發布:2025-05-15 20:43:00 瀏覽:656
impala資料庫 發布:2025-05-15 20:42:12 瀏覽:648
android安裝插件 發布:2025-05-15 20:41:31 瀏覽:241
神秘顧客訪問 發布:2025-05-15 20:33:39 瀏覽:298
安卓市場手機版從哪裡下載 發布:2025-05-15 20:17:28 瀏覽:815
幼兒速演算法 發布:2025-05-15 20:15:08 瀏覽:87
best把槍密碼多少 發布:2025-05-15 20:13:42 瀏覽:549
android安裝程序 發布:2025-05-15 20:13:20 瀏覽:560
c語言跳出死循環 發布:2025-05-15 20:06:04 瀏覽:825