Android/java 多執行緒(二)-Thread的好兄弟Handl
簡介
Handler機制在安卓中應用非常廣泛,像我們常見的用於在子執行緒中更新UI:
public class MainActivity extends AppCompatActivity { @SuppressLint("HandlerLeak") private Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { //更新UI } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyThread thread = new MyThread(); thread.start(); } class MyThread extends Thread { @Override public void run() { //處理子執行緒邏輯 //.... //傳送訊息更新UI Message message = mHandler.obtainMessage(1); mHandler.sendMessage(message); } } }
由此引出一些問題,為什麼sendMessage
之後handleMessage
方法會被執行?.為什麼handleMessage
是在主執行緒中?.它們的訊息是如何傳遞的?.這些會在下面講解
且看還有一種使用情況,在子執行緒中使用Handler:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); MyThread thread = new MyThread(); thread.start(); } class MyThread extends Thread { @Override public void run() { Looper.prepare(); @SuppressLint("HandlerLeak") Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg) { //處理邏輯 l(Thread.currentThread().getName()); } }; Message message = mHandler.obtainMessage(1); mHandler.sendMessage(message); Looper.loop(); } } public void l(String s) { Log.i("HJ", s); } }
列印結果如下:
2018-12-20 10:48:21.682 1808-1824/? I/HJ: Thread-2
如果我們不使用Looper.prepare()
和Looper.loop()
方法,直接執行,那麼會丟擲以下異常:
2018-12-20 11:00:49.654 2029-2045/com.zj.example.customview.funnel E/AndroidRuntime: FATAL EXCEPTION: Thread-2 Process: com.zj.example.customview.funnel, PID: 2029 java.lang.RuntimeException: Only one Looper may be created per thread at android.os.Looper.prepare(Looper.java:95) at android.os.Looper.prepare(Looper.java:90) at com.zj.example.customview.funnel.MainActivity$MyThread.run(MainActivity.java:48)
這是為什麼呢?,為什麼子執行緒不能直接建立Handler呢?使用了Looper.prepare()
和Looper.loop()
為什麼又可以了呢?,這兩個方法又是幹什麼用的呢?請看以下原始碼分析。
原理分析
Handler機制的核心類主要有三個,Handler
,Message
,Looper
,而與Looper
相關聯的類還有ThreadLocal
和MessageQueue
,下面來一一介紹下它們的作用:
Message
訊息的載體,內部主要存放一些訊息型別,引數主要有:
(1)public int what:變數,用於定義此Message屬於何種操作
(2)public Object obj:變數,用於定義此Message傳遞的資訊資料,透過它傳遞資訊
(3)public int arg1:變數,傳遞一些整型資料時使用
(4)public int arg2:變數,傳遞一些整型資料時使用
(5)public Handler getTarget():普通方法,取得操作此訊息的Handler物件。
在該類的使用上,儘量使用obtain()
方法來獲取,而非構造方法,這樣可以節省記憶體。而在資料傳遞方面,如果是int
型別的,建議使用arg1
和arg2
,其次再考慮使用Bundle
Looper
訊息通道,訊息機制的主要邏輯實現類,內部有使用ThreadLocal
用來儲存當前執行緒的Looper
,使用MessageQueue
用來維護Message
池,而我們使用Looper.prepare()
方法其實也就是將此執行緒的Looper
物件加入到ThreadLocal
中去:
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //一個執行緒只能建立一個Looper throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
看原始碼就知道,當前執行緒中有且只有一個Looper,當我們再次建立,就會丟擲異常,所以上面拋異常的原因就是在這裡。
而我們的loop()
方法就是訊息機制的核心,原理是實現了一個無限迴圈,用於從MessageQueue
中取出訊息分發到各個Handler
中去,以下是精簡原始碼:
public static void loop() { //從ThreadLocal中取出Looper final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //從Looper中獲取到MessageQueue final MessageQueue queue = me.mQueue; ........ //建立迴圈 for (;;) { //從MessageQueue中一個一個的取出Message Message msg = queue.next(); // might block if (msg == null) { //沒訊息取了就退出 return; } ........... final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); final long end; try { //dispatchMessage方法的呼叫在這 msg.target.dispatchMessage(msg); end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ......... //回收訊息,同時將Message置為空訊息物件,儲存在訊息池中,用於obtain()方法再次獲取 msg.recycleUnchecked(); } }
綜合要點就是使用loop()
來開啟訊息分發,並且使用prepare
來建立Looper。並且一個執行緒只能有一個Looper。
可以看到,dispatchMessage
就是在這裡回撥的,那這個target是什麼東西呢,其實就是我們的Handler物件。那這個Handler物件是在哪裡賦值的呢,請看Handler原始碼
Handler
首先看我們的構造方法最終會呼叫到這裡:
public Handler(Callback callback, boolean async) { 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) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
這裡主要起到從ThreadLocal中獲取Looper物件的作用。看Looper.myLooper()
方法:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
隨後我們會呼叫sendMessage()
型別的方法來傳送訊息,最終都會呼叫這個方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; //這裡將Handler賦值了 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
注意看這裡msg.target = this
,這裡實現了將當前Handler賦值到了Message中。隨後呼叫了enqueueMessage
方法就將此Message投入到了訊息池MessageQueue
中.到這裡,一系列的初始化與呼叫都完成了。
所以,經過以上分析,我們之前的問題就迎刃而解了,但是還是有一個問題,我們在第一個例項中並沒有使用Looper
的prepare
和loop
方法,那我們的訊息機制為什麼會生效呢,其實在我們的app建立的時候系統已經預設為我們開啟了,並且是在主程式的最後呼叫的,並沒有在onCreate()
方法中,而其他的生命週期方法,是透過AMS進行回撥。這就保證了我們的迴圈不會造成主執行緒卡頓,具體的原始碼呼叫是在ActivityThread
類中的main
函式中:
public static void main(String[] args) { SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via // StrictMode) on debug builds, but using DropBox, not logs. CloseGuard.setEnabled(false); Environment.initForCurrentUser(); // Set the reporter for event logging in libcore EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); // Make sure TrustedCertificateStore looks in the right place for CA certificates final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId()); TrustedCertificateStore.setDefaultUserDirectory(configDir); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); // //為當前執行緒(主執行緒)建立一個Looper物件 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); //為當前執行緒設定Handler } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); // 執行從訊息佇列中獲取Message,並呼叫Handler進行處理的無限迴圈;所有和主執行緒相關的訊息處理都在該方法中執行 throw new RuntimeException("Main thread loop unexpectedly exited"); }
分析到這,Handler流程其實也差不多了,最後做下總結:
1.在主執行緒中初始化了一個Handler,此時Looper.loop()已經開啟,透過Handler構造方法建立了一個在主執行緒中的Looper,並儲存到了ThreadLocal中,此時主執行緒不允許再建立第二個Looper.
2.在子執行緒中使用sendMessage
方法,此時會呼叫enqueueMessage()方法並建立一個Message(或從池中取出空Message物件),然後我們將需要傳送的訊息儲存在Message中,並將此Handler儲存到Message的target物件中,然後壓入到MessageQueue訊息池中
3.Looper中會迴圈從MessageQueue中取出Message物件,並回撥到target物件上的Handler的dispatchMessage()方法中去,由於Looper.loop()方法是在主執行緒中呼叫,所以dispatchMessage()方法也是執行在主執行緒中。Looper.loop()方法執行所在的執行緒決定dispatchMessage()方法回撥的執行緒,並且需要與Looper.prepare()方法配套使用
作者:我是黃教主啊
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4328/viewspace-2821566/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java多執行緒(二):Thread類Java執行緒thread
- java多執行緒之Thread類Java執行緒thread
- Java多執行緒Thread類使用Java執行緒thread
- 多執行緒系列(二)之Thread類執行緒thread
- 【java多執行緒】(二)執行緒停止Java執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- Java多執行緒的使用(二)Java執行緒
- java 多執行緒(關於Thread的講解)Java執行緒thread
- Java多執行緒之Thread原始碼分析Java執行緒thread原始碼
- JAVA多執行緒Thread VS Runnable詳解Java執行緒thread
- Android/java 多執行緒(一)-Thread的使用以及原始碼分析AndroidJava執行緒thread原始碼
- Android執行緒篇(二)Java執行緒池Android執行緒Java
- Java 中的執行緒 threadJava執行緒thread
- Java多執行緒2:Thread中的例項方法Java執行緒thread
- Java多執行緒3:Thread中的靜態方法Java執行緒thread
- Java多執行緒/併發07、Thread.Join()讓呼叫執行緒等待子執行緒Java執行緒thread
- Java多執行緒之二(Synchronized)Java執行緒synchronized
- 走進Java Android 的執行緒世界(二)執行緒池JavaAndroid執行緒
- Java多執行緒——執行緒Java執行緒
- Java 多執行緒(Java.Thread)------ 執行緒協作(生產者消費者模式)Java執行緒thread模式
- 【原創】Java多執行緒初學者指南(2):用Thread類建立執行緒Java執行緒thread
- python 多執行緒之threadPython執行緒thread
- 多執行緒(二)執行緒
- 多執行緒系列(二):多執行緒基礎執行緒
- Java多執行緒-執行緒中止Java執行緒
- Java多執行緒——執行緒池Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- Java高併發與多執行緒(二)-----執行緒的實現方式Java執行緒
- Swift多執行緒:使用Thread進行多執行緒間通訊,協調子執行緒任務Swift執行緒thread
- java——多執行緒Java執行緒
- java 多執行緒Java執行緒
- 【Java】多執行緒Java執行緒
- JAVA 多執行緒 ??Java執行緒
- java多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒-執行緒通訊Java執行緒