亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中Handler,MessageQueue與Looper關系是什么

發布時間:2021-09-03 11:22:54 來源:億速云 閱讀:102 作者:小新 欄目:移動開發

這篇文章主要介紹了Android中Handler,MessageQueue與Looper關系是什么,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

一說到Android的消息機制,自然就會聯想到Handler,我們知道Handler是Android消息機制的上層接口,因此我們在開發過程中也只需要和Handler交互即可,很多人認為Handler的作用就是更新UI,這也確實沒錯,但除了更新UI,Handler其實還有很多其他用途,比如我們需要在子線程進行耗時的I/O操作,可能是讀取某些文件或者去訪問網絡等,當耗時操作完成后我們可能需要在UI上做出相應的改變,但由于Android系統的限制,我們是不能在子線程更新UI控件的,否則就會報異常,這個時候Handler就可以派上用場了,我們可以通過Handler切換到主線程中執行UI更新操作。

下面是Handler一些常用方法:

void handleMessage(Message msg):處理消息的方法,該方法通常會被重寫。

final boolean hasMessages(int what):檢測消息隊列中是否包含what屬性為指定值的消息。

Message obtainMessage():獲取消息的方法,此函數有多個重載方法。

sendEmptyMessage(int what):發送空消息。

final boolean sendEmptyMessageDelayed(int what , long delayMillis):指定多少毫秒后發送空消息。

final boolean sendMessage(Message msg):立即發送消息。

final boolean sendMessageDelayed(Message msg ,long delayMillis):指定多少毫秒后發送消息。

final boolean post(Runnable r):執行runnable操作。

final boolean postAtTime(Runnable r, long upTimeMillis):在指定時間執行runnable操作。

final boolean postDelayed(Runnable r, long delayMillis):指定多少毫秒后執行runnable操作。

介紹完方法后,我們就從一個簡單的例子入手吧,然后一步步的分析:

public class MainActivity extends AppCompatActivity {
 
 public static final int MSG_FINISH = 0X001;
 //創建一個Handler的匿名內部類
 private Handler handler = new Handler() {
 
 @Override
 public void handleMessage(Message msg) {
  switch (msg.what) {
  case MSG_FINISH:
   LogUtils.e("handler所在的線程id是-->" + Thread.currentThread().getName());
   break;
  }
 }
 
 };
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 //啟動耗時操作
 consumeTimeThread(findViewById(R.id.tv));
// handler.post()
 }
 
 //啟動一個耗時線程
 public void consumeTimeThread(View view) {
 new Thread() {
  public void run() {
  try {
   LogUtils.e("耗時子線程的Name是--->" + Thread.currentThread().getName());
   //在子線程運行
   Thread.sleep(2000);
   //完成后,發送下載完成消息
   handler.sendEmptyMessage(MSG_FINISH);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  }
 }.start();
 }
}

運行結果:

Android中Handler,MessageQueue與Looper關系是什么

上面的例子其實就是Handler的基本使用,在主線中創建了一個Handler對象,然后通過在子線程中模擬一個耗時操作完成后通過sendEmptyMessage(int)方法發送一個消息通知主線程的Handler去執行相應的操作。通過運行結果我們也可以知道Handler確實也是在主線程運行的。

那么問題來了,通過Handler發送的消息是怎么到達主線程的呢?接下來我們就來掰掰其中的奧妙,前方高能,請集中注意力!為了更好的理解Handler的工作原理,我們先來介紹與Handler一起工作的幾個組件:

Message:Handler接收和處理消息的對象。

Looper:每個線程只能有一個Looper。它的loop方法負責讀取MessageQueue中的消息,讀到消息后把消息發送給Handler進行處理。

MessageQueue:消息隊列,它采用先進先出的方式來管理Message。程序創建Looper對象時,會在它的構造方法中創建MessageQueue對象。

Handler:它的作用有兩個—發送消息和處理消息,程序使用Handler發送消息,由Handler發送的消息必須被送到指定的MessageQueue;否則消息就沒有在MessageQueue進行保存了。而MessageQueue是由Looper負責管理的,也就是說,如果希望Handler正常工作的話,就必須在當前線程中有一個Looper對象。我們先對上面的幾個組件有大概的了解就好,后面我們都會詳細分析,既然消息是從Handler發送出去,那么我們就先從Handler入手吧。先來看看Handler 的構造方法源碼:

public class Handler {
	/**
	 * 未實現的空方法handleMessage()
	 */
	public void handleMessage(Message msg) {
	}
	/**
	 * 我們通常用于創建Handler的構造方法之一
	 */
	public Handler() {
		this(null, false);
	}
	// 構造方法的內調用的this(null, false)的具體實現
	public Handler(Callback callback, boolean async) {
//檢查Handler是否是static的,如果不是的,那么有可能導致內存泄露
 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()); 
  } 
 } 
 //重要的組件出現啦!Looper我們先理解成一個消息隊列的管理者,用來從消息隊列中取消息的,后續會詳細分析
 mLooper = Looper.myLooper(); 
 if (mLooper == null) { 
  //這個異常很熟悉吧,Handler是必須在有Looper的線程上執行,這個也就是為什么我在HandlerThread中初始化Handler 
  //而沒有在Thread里面初始化,如果在Thread里面初始化需要先調用Looper.prepare方法 
  throw new RuntimeException( 
  "Can't create handler inside thread that has not called Looper.prepare()"); 
 } 
 //將mLooper里面的消息隊列復制到自身的mQueue,這也就意味著Handler和Looper是公用一個消息隊列 
 mQueue = mLooper.mQueue; 
 //回調函數默認是Null 
 mCallback = null; 
	}

分析:Handler的構造方法源碼不是很多,也比較簡單,但是我們從源碼中也可以得知,在創建Handler時,Handler內部會去創建一個Looper對象,這個Looper對象是通過Looper.myLooper()創建的(后續會分析這個方法),同時還會創建一個消息隊列MessageQueue,而這個MessageQueue是從Looper中獲取的,這也就意味著Handler和Looper共用一個消息隊列,當然此時Handler,Looper以及MessageQueue已經捆綁到一起了。上面還有一個情況要說明的,那就是:

if (mLooper == null) { 
 //這個異常很熟悉吧,Handler是必須在有Looper的線程上執行,這個也就是為什么我在HandlerThread中初始化Handler 
 //而沒有在Thread里面初始化,如果在Thread里面初始化需要先調用Looper.prepare方法 
 throw new RuntimeException( 
 "Can't create handler inside thread that has not called Looper.prepare()"); 
 }

這里先回去判斷Looper是否為空,如果為null,那么就會報錯,這個錯誤對我們來說應該比較熟悉吧,那為什么會報這個錯誤呢?我們在前面說過Handler的作用有兩個—發送消息和處理消息,我們在使用Handler發送消息,由Handler發送的消息必須被送到指定的MessageQueue;否則就無法進行消息循環。而MessageQueue是由Looper負責管理的,也就是說,如果希望Handler正常工作的話,就必須在當前線程中有一個Looper對象。那么又該如何保障當前線程中一定有Looper對象呢?這里其實分兩種情況:

(1)在主UI線程中,系統已經初始化好了一個Looper對象,因此我們可以直接創建Handler并使用即可。

(2)在子線程中,我們就必須自己手動去創建一個Looper對象,并且去啟動它,才可以使用Handler進行消息發送與處理。使用事例如下:

class childThread extends Thread{
 public Handler mHandler;
 
 @Override
 public void run() {
  //子線程中必須先創建Looper
  Looper.prepare();
  
  mHandler =new Handler(){
  @Override
  public void handleMessage(Message msg) {
   super.handleMessage(msg);
   //處理消息
  }
  };
  //啟動looper循環
  Looper.loop();
 }
 }

分析完Handler的構造方法,我們接著看看通過Handler發送的消息到底是發送到哪里了?我們先來看看Handler的幾個主要方法源碼:

// 發送一個空消息的方法,實際上添加到MessagerQueue隊列中
public final boolean sendEmptyMessage(int what) {
	return sendEmptyMessageDelayed(what, 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);
}
// 最后調用此方法添加到消息隊列中
private boolean enqueueMessage(MessageQueue queue, Message msg,
		long uptimeMillis) {
	msg.target = this;// 設置發送目標對象是Handler本身
	if (mAsynchronous) {
		msg.setAsynchronous(true);
	}
	return queue.enqueueMessage(msg, uptimeMillis);// 添加到消息隊列中
}
// 在looper類中的loop()方法內部調用的方法
public void dispatchMessage(Message msg) {
	if (msg.callback != null) {
		handleCallback(msg);
	} else {
		if (mCallback != null) {
			if (mCallback.handleMessage(msg)) {
				return;
			}
		}
		handleMessage(msg);
	}
}

分析:通過源碼我們可以知道,當我們調用sendEmptyMessage(int)發送消息后。最終Handler內部會去調用enqueueMessage(MessageQueue queue,Message msg)方法把發送的消息添加到消息隊列MessageQueue中,同時還有設置msg.target=this此時就把當前handler對象綁定到msg.target中了,這樣就完成了Handler向消息隊列存放消息的過程。這個還有一個要注意的方法 dispatchMessage(Message),這個方法最終會在looper中被調用(這里我們先知道這點就行,后續還會分析)。話說我們一直在說MessageQueue消息隊列,但這個消息隊列到底是什么啊?其實在Android中的消息隊列指的也是MessageQueue,MessageQueue主要包含了兩種操作,插入和讀取,而讀取操作本身也會伴隨著刪除操作,插入和讀取對應的分別是enqueueMessage和next,其中enqueueMessage是向消息隊列中插入一條消息,而next的作用則是從消息隊列中取出一條消息并將其從隊列中刪除。雖然我們一直稱其為消息隊列但是它的內部實現并不是隊列,而是通過一個單鏈表的數據結構來維護消息列表的,因為我們知道單鏈表在插入和刪除上比較有優勢。至內MessageQueue的內部實現,這個屬于數據結構的范疇,我們就不過多討論了,還是回到原來的主題上來,到這里我們都知道Handler發送的消息最終會添加到MessageQueue中,但到達MessageQueue后消息又是如何處理的呢?還記得我們前面說過MessageQueue是由Looper負責管理的吧,現在我們就來看看Looper到底是如何管理MessageQueue的?

public final class Looper {
	// sThreadLocal.get() will return null unless you've called prepare().
	//存放線程的容器類,為確保獲取的線程和原來的一樣
	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
	private static Looper sMainLooper; // guarded by Looper.class
	//消息隊列
	final MessageQueue mQueue;
	final Thread mThread;
	//perpare()方法,用來初始化一個Looper對象
	public static void prepare() {
		prepare(true);
	}
		
	private static void prepare(boolean quitAllowed) {
		if (sThreadLocal.get() != null) {
		throw new RuntimeException("Only one Looper may be created per thread");
		}
		sThreadLocal.set(new Looper(quitAllowed));
	}
	//handler調用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。
	public static Looper myLooper() {
		return sThreadLocal.get();
	}
		
	//Looper類的構造方法,可以發現創建Looper的同時也創建了消息隊列MessageQueue對象
	private Looper(boolean quitAllowed) {
		mQueue = new MessageQueue(quitAllowed);
		mRun = true;
		mThread = Thread.currentThread();
	}
		
//這個方法是給系統調用的,UI線程通過調用這個線程,從而保證UI線程里有一個Looper 
//需要注意:如果一個線程是UI線程,那么myLooper和getMainLooper是同一個Looper 
 public static final void prepareMainLooper() { 
	prepare(); 
	setMainLooper(myLooper()); 
	if (Process.supportsProcesses()) { 
	 myLooper().mQueue.mQuitAllowed = false; 
	 } 
	} 
	 
//獲得UI線程的Looper,通常我們想Hanlder的handleMessage在UI線程執行時通常會new Handler(getMainLooper()); 
 public synchronized static final Looper getMainLooper() { 
	 return mMainLooper; 
 } 
		
//looper中最重要的方法loop(),該方法是個死循環,會不斷去消息隊列MessageQueue中獲取消息,然后調dispatchMessage(msg)方法去執行
 public static void loop() {
	final Looper me = myLooper();
	if (me == null) {
		throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
		}
	 final MessageQueue queue = me.mQueue;
	 //死循環
	 for (;;) {
		Message msg = queue.next(); // might block
		if (msg == null) {
		// No message indicates that the message queue is quitting.
				return;
		}
//這里其實就是調用handler中的方法,而在Handler的源碼中也可以知道dispatchMessage(msg)內部調用的就是handlerMessage()方法
		msg.target.dispatchMessage(msg);
		msg.recycle();
	}
}

分析:代碼不算多,我們拆分開慢慢說,在Looper源碼中我們可以得知其內部是通過一個ThreadLocal的容器來存放Looper的對象本身的,這樣就可以確保每個線程獲取到的looper都是唯一的。那么Looper對象是如何被創建的呢?通過源碼我們可以知道perpare()方法就可以創建Looper對象:

//perpare()方法,用來初始化一個Looper對象
public static void prepare() {
	prepare(true);
}
		
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
	throw new RuntimeException("Only one Looper may be created per thread");
	}
	sThreadLocal.set(new Looper(quitAllowed));
}

在創建Looper對象前先會去判斷ThreadLocal中是否已經存在Looper對象,如果不存在就新創建一個Looper對象并且存放ThreadLocal中。這里還有一個要注意的是在Looper創建的同時MessageQueue消息隊列也被創建完成,這樣的話Looper中就持有了MessageQueue對象。

//Looper類的構造方法,可以發現創建Looper的同時也創建了消息隊列MessageQueue對象
	private Looper(boolean quitAllowed) {
		mQueue = new MessageQueue(quitAllowed);
		mRun = true;
		mThread = Thread.currentThread();
	}

那么我們如何獲取已經創建好的Looper對象呢?通過源碼我們知道myLooper()方法就可以獲取到Looper對象:

//handler調用的獲取Looper對象的方法。實際是在ThreadLocal中獲取。
	public static Looper myLooper() {
		return sThreadLocal.get();
	}

Looper對象的創建和獲取,還有MessageQueue對象的創建,現在我們都很清楚了,但是Looper到底是怎么管理MessageQueue對象的呢?這就要看looper()方法了:

//looper中最重要的方法loop(),該方法是個死循環,
//會不斷去消息隊列MessageQueue中獲取消息,
//然后調dispatchMessage(msg)方法去執行
 public static void loop() {
	final Looper me = myLooper();
	if (me == null) {
		throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
		}
	 final MessageQueue queue = me.mQueue;
	 //死循環
	 for (;;) {
		Message msg = queue.next(); // might block
		if (msg == null) {
		// No message indicates that the message queue is quitting.
				return;
		}
//這里其實就是調用handler中的方法,而在Handler的源碼中也可以知道dispatchMessage(msg)內部調用的就是handlerMessage()方法
		msg.target.dispatchMessage(msg);
		msg.recycle();
	}

通過looper()方法內部源碼我們可以知道,首先會通過myLoooper()去獲取一個Looper對象,如果Looper對象為null,就會報出一個我們非常熟悉的錯誤提示,“No Looper;Looper.prepare() wasn't called on this thread”,要求我們先通過Looper.prepare()方法去創建Looper對象;如果Looper不為null,那么就會去獲取消息隊列MessageQueue對象,接著就進入一個for的死循環,不斷從消息隊列MessageQueue對象中獲取消息,如果消息不為空,那么久會調用msg.target的dispatchMessage(Message)方法,那么這個target又是什么,沒錯target就是我們創建的Handler對象,還記得我們前面分析Handler源碼時說過的那個方法嘛?

// 在looper類中的loop()方法內部調用的方法
public void dispatchMessage(Message msg) {
	if (msg.callback != null) {
		handleCallback(msg);
	} else {
		if (mCallback != null) {
			if (mCallback.handleMessage(msg)) {
				return;
			}
		}
		handleMessage(msg);
	}

現在明白了吧?首先,檢測Message的callback是否為null,不為null就通過handleCallback方法來處理消息,那么Message的callback是什么?其實就是一個Runnable對象,實際上就是Handler的post方法所傳遞的Runnable參數,我們順便看看post方法源碼:

public final boolean post(Runnable r)
{
 return sendMessageDelayed(getPostMessage(r), 0);
 }

現在明白Message的callback是什么了吧?而對應handleCallback方法邏輯也比較簡單:

private static void handleCallback(Message message) {
 message.callback.run();
 }

嗯,是的,因此最終執行的還是通過post方法傳遞進來的Runnable參數的run方法。好了,我們繼續dispatchMessage()方法的分析,接著會去檢查mCallback是否為null,不為null,則調用mCallback的handleMessage方法來處理消息。至于Callback則就是一個接口定義如下:

/**
 * Callback interface you can use when instantiating a Handler to avoid
 * having to implement your own subclass of Handler.
 *
 * @param msg A {@link android.os.Message Message} object
 * @return True if no further handling is desired
 */
 public interface Callback {
 public boolean handleMessage(Message msg);
 }

這個接口有什么用呢?其實通過Callback接口我們就可以采取如下方法來創建Handler對象:

Handler handler =new Handler(callback)

那么這樣做到底有什么意義,其實這樣做可以用callback來創建一個Handler的實例而無需派生Handler的子類。在我們的開發過程中,我們經常使用的方法就是派生一個Hanlder子類并重寫其handleMessage方法來處理具體的消息,而Callback給我們提供了另外一種方式,那就是當我們不想派生子類的時候,可以通過Callback來實現。繼續dispatchMessage()方法的分析,最后如果以上條件都不成立的話,就會去調用Handler的handleMessage方法來處理消息。而 我們的Handler是在主線程創建的,也就是說Looper也是主線程的Looper,因此handleMessage內部處理最終都會在主線程上執行,就這樣整個流程都執行完了。下面提供一個圖解幫助大家理解:

Android中Handler,MessageQueue與Looper關系是什么

最后我們來個小總結:Android中的Looper類主要作用是來封裝消息循環和消息隊列的,用于在android線程中進行消息處理。handler是用來向消息隊列中插入消息的并最好對消息進行處理。

(1) Looper類主要是為每個線程開啟的單獨的消息循環。 默認情況下android中新誕生的線程是沒有開啟消息循環的。(主線程除外,主線程系統會自動為其創建Looper對象,開啟消息循環) Looper對象負責管理MessageQueue,而MessageQueue主要是用來存放handler發送的消息,而且一個線程只能有一個Looper,對應一個MessageQueue。

(2) 我們通常是通過Handler對象來與Looper進行交互的。Handler可看做是Looper的一個接口,用來向指定的Looper中的MessageQueue發送消息并且Handler還必須定義自己的處理方法。 默認情況下Handler會與其被定義時所在線程的Looper綁定,如Handler在主線程中定義,它是與主線程的Looper綁定。 mainHandler = new Handler() 等價于 new Handler(Looper.myLooper())Looper.myLooper():獲取當前進程的looper對象, Looper.getMainLooper() 用于獲取主線程的Looper對象。

(3) 在非主線程中直接new Handler() 會報如下的錯誤: Can't create handler inside thread that has not called Looper.prepare() 原因是非主線程中默認沒有創建Looper對象,需要先調用Looper.prepare()啟用Looper,然后再調用Looper.loop()。

(4) Looper.loop():啟動looper中的循環線程,Handler就會從消息隊列里取消息并進行對應處理。 最后要注意的是寫在Looper.loop()之后的代碼不會被執行,這個函數內部應該是一個循環,當調用mHandler.getLooper().quit()后,loop()才會中止,其后的代碼才能得以運行。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Android中Handler,MessageQueue與Looper關系是什么”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

营山县| 九龙县| 海宁市| 花莲县| 石河子市| 沈丘县| 昆明市| 罗江县| 迁西县| 从江县| 乌兰县| 福州市| 汉中市| 乌兰察布市| 安徽省| 桐柏县| 青铜峡市| 通州区| 密山市| 霞浦县| 宁明县| 丰宁| 吴桥县| 都兰县| 巨鹿县| 陵川县| 兴国县| 开化县| 宁武县| 怀安县| 边坝县| 册亨县| 商都县| 武穴市| 宜黄县| 当雄县| 长海县| 平南县| 宝鸡市| 宣武区| 文安县|