詳解Android的Handler機(jī)制原理
以下是Android Handler機(jī)制的主要組成部分和工作原理:
1.Message(消息):Message是一個(gè)包含要傳遞的數(shù)據(jù)和指令的對(duì)象。它可以攜帶整數(shù)、字符串、Bundle等不同類型的數(shù)據(jù)。當(dāng)需要在不同線程之間傳遞數(shù)據(jù)或執(zhí)行任務(wù)時(shí),通常會(huì)創(chuàng)建一個(gè)Message并將其發(fā)送給Handler。
2.Handler(處理程序):Handler是用于處理Message的對(duì)象。它通常與一個(gè)特定的線程(通常是主線程)關(guān)聯(lián)。通過Handler,您可以將Message發(fā)送到與其關(guān)聯(lián)的線程的消息隊(duì)列中,以便在那個(gè)線程中執(zhí)行處理。
3.Looper(消息循環(huán)器):Looper是一個(gè)用于管理線程的消息隊(duì)列的對(duì)象。每個(gè)線程都可以有一個(gè)Looper,它會(huì)在線程上創(chuàng)建一個(gè)消息隊(duì)列,允許該線程接收并處理Message。主線程通常已經(jīng)具有一個(gè)默認(rèn)的Looper,而后臺(tái)線程需要顯式創(chuàng)建一個(gè)Looper。
4.MessageQueue(消息隊(duì)列):MessageQueue是一個(gè)FIFO(先進(jìn)先出)隊(duì)列,用于存儲(chǔ)待處理的Message。每個(gè)Looper都有一個(gè)關(guān)聯(lián)的MessageQueue,Handler將Message發(fā)送到這個(gè)隊(duì)列中,然后由Looper依次處理隊(duì)列中的Message。
Handler機(jī)制的工作流程:
1.在主線程(或其他線程)上創(chuàng)建一個(gè)Handler對(duì)象,這個(gè)Handler會(huì)關(guān)聯(lián)到當(dāng)前線程的Looper。
2.在后臺(tái)線程中,創(chuàng)建一個(gè)Message對(duì)象,可以將一些數(shù)據(jù)和處理指令放入這個(gè)Message。
3.使用Handler的sendMessage方法將Message發(fā)送到與Handler關(guān)聯(lián)的Looper的MessageQueue中。
4.Looper在后臺(tái)線程中不斷輪詢MessageQueue,當(dāng)有新的Message到達(dá)時(shí),將Message取出并交給Handler處理。
5.Handler收到Message后,可以根據(jù)Message中的指令執(zhí)行相應(yīng)的操作,通常是在主線程中更新UI。
6.如果需要定時(shí)任務(wù)或循環(huán)執(zhí)行,可以使用Handler的postDelayed方法。
Handler 的三種使用方法,分別是:
Handler.sendMessage(Message)
Handler.post(Runnable)
Handler.obtainMessage(what).sendToTarget();
1. Handler.sendMessage()方法
Handler.sendMessage(Msg) 方法是最為常見的一種方法。
1.1 使用步驟說明
其使用步驟分四步,如下所示:
1、步驟一:新建 Handler 對(duì)象,覆寫 handleMessage(Message) 方法。
2、步驟二:新建 Message 對(duì)象,設(shè)置其攜帶的數(shù)據(jù)。
3、步驟三:在子線程中通過 Handler.sendMessage(Message) 方法發(fā)送信息。
4、步驟四:在 Handler 的 handleMessage(Message msg) 方法中處理消息,通知主線程作出相對(duì)應(yīng)的 UI 工作。
步驟一:新建 Handler 對(duì)象,覆寫 handleMessage(Message) 方法
//創(chuàng)建 Handler對(duì)象,并關(guān)聯(lián)主線程消息隊(duì)列 mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); ···略··· } } };
步驟二:新建 Message 對(duì)象,設(shè)置其攜帶的數(shù)據(jù)
Bundle bundle = new Bundle(); bundle.putInt(CURRENT_PROCESS_KEY, i); Message msg = new Message(); msg.setData(bundle); msg.what = 2;
步驟三:在子線程中通過 Handler.sendMessage(Message) 方法發(fā)送信息
mHandler.sendMessage(msg)
步驟四:在 Handler 的 handleMessage(Message msg) 方法中處理消息,通知主線程作出相對(duì)應(yīng)的 UI 工作
mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); //根據(jù)信息編碼及數(shù)據(jù)做出相對(duì)應(yīng)的處理 switch (msg.what) { case 1: //更新 TextView UI mDisplayTv.setText("CustomChildThread starting!"); break; case 2: //獲取 ProgressBar 的進(jìn)度,然后顯示進(jìn)度值 Bundle bundle = msg.getData(); int process = bundle.getInt(CURRENT_PROCESS_KEY); mProgressBar.setProgress(process); break; default: break; } } };
1.2.1Java版本的具體代碼如下所示:
public class HandlerAddThreadActivity extends AppCompatActivity { public static final String CURRENT_PROCESS_KEY = "CURRENT_PROCESS"; private TextView mDisplayTv; private Handler mHandler; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_add_thread); TextView titleTv = findViewById(R.id.title_tv); titleTv.setText("Handler + Thread"); mDisplayTv = findViewById(R.id.display_tv); mProgressBar = findViewById(R.id.test_handler_progress_bar); //mHandler用于處理主線程消息隊(duì)列中的子線程消息 mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: //更新 TextView UI mDisplayTv.setText("CustomChildThread starting!"); break; case 2: //獲取 ProgressBar 的進(jìn)度,然后顯示進(jìn)度值 Bundle bundle = msg.getData(); int process = bundle.getInt(CURRENT_PROCESS_KEY); mProgressBar.setProgress(process); break; default: break; } } }; Button mClickBtn = findViewById(R.id.click_btn); mClickBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //開啟子線程,子線程處理UI工作 CustomChildThread customThread = new CustomChildThread(); customThread.start(); } }); } /** * 子線程,用于處理耗時(shí)工作 */ public class CustomChildThread extends Thread { @Override public void run() { //在子線程中創(chuàng)建一個(gè)消息對(duì)象 Message childThreadMessage = new Message(); childThreadMessage.what = 1; //將該消息放入主線程的消息隊(duì)列中 mHandler.sendMessage(childThreadMessage); //模擬耗時(shí)進(jìn)度,將進(jìn)度值傳給主線程用于更新 ProgressBar 進(jìn)度。 for (int i = 1; i <= 5; i++) { try { //讓當(dāng)前執(zhí)行的線程(即 CustomChildThread)睡眠 1s Thread.sleep(1000); //Message 傳遞參數(shù) Bundle bundle = new Bundle(); bundle.putInt(CURRENT_PROCESS_KEY, i); Message progressBarProcessMsg = new Message(); progressBarProcessMsg.setData(bundle); progressBarProcessMsg.what = 2; mHandler.sendMessage(progressBarProcessMsg); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
1.2.2Kotlin版本的代碼如下所示:
class TestThreadAddHandlerActivity : AppCompatActivity() { companion object { const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_thread_add_handler) handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener { //工作線程開始模擬下載任務(wù) val workThread: WorkThread = WorkThread(this) workThread.start() }) } class WorkThread(activity: TestThreadAddHandlerActivity) : Thread() { private var handler: MyHandler = MyHandler(activity) override fun run() { super.run() for (i in 0..6) { sleep(1000) //通過 Handler 將進(jìn)度參數(shù)傳遞給 主線程,讓其更新 progressBar 進(jìn)度 val message = Message() message.what = 1 val bundle = Bundle() bundle.putInt(PROGRESS_VALUE_KEY, i) message.data = bundle handler.sendMessage(message) } } } /** * 靜態(tài)內(nèi)部類,防止內(nèi)存泄漏 */ class MyHandler(activity: TestThreadAddHandlerActivity) : Handler() { private var weakReference = WeakReference(activity) override fun handleMessage(msg: Message) { super.handleMessage(msg) //處理消息 when (msg.what) { 1 -> { val activity = weakReference.get() if (activity != null && !activity.isFinishing) { //獲取消息中攜帶的任務(wù)處理進(jìn)度參數(shù),然后設(shè)置成 ProgressBar 的進(jìn)度。 val progressValue: Int = msg.data.get(PROGRESS_VALUE_KEY) as Int activity.handlerAddThreadProgressBar.progress = progressValue } } } } } }
2. Handler.post()方法
除了使用 Handler.sendMessage(Message) 來發(fā)送信息,Handler 還支持 post(Runnable) 方法來傳遞消息,通知主線程做出相對(duì)應(yīng)的 UI 工作。使用方法如下:
/** * 將可運(yùn)行的 Runnable 添加到消息隊(duì)列。Runnable 將在該 Handler 相關(guān)的線程上運(yùn)行處理。 * The runnable will be run on the thread to which this handler is attached. */ new Handler().post(new Runnable() { @Override public void run() { //更新處理 UI 工作 } });
2.1 Java版本的具體代碼如下
public class HandlerPostFunctionActivity extends AppCompatActivity { private Handler mMainHandler; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_add_thread); TextView titleTv = findViewById(R.id.title_tv); titleTv.setText("Handler post() function"); mProgressBar = findViewById(R.id.test_handler_progress_bar); //新建靜態(tài)內(nèi)部類 Handler 對(duì)象 mMainHandler = new Handler(getMainLooper()); Button mClickBtn = findViewById(R.id.click_btn); mClickBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //開啟子線程,子線程處理UI工作 CustomChildThread customThread = new CustomChildThread(); customThread.start(); } }); } /** * 子線程,用于處理耗時(shí)工作 */ public class CustomChildThread extends Thread { @Override public void run() { //模擬耗時(shí)進(jìn)度,將進(jìn)度值傳給主線程用于更新 ProgressBar 進(jìn)度。 for (int i = 1; i <= 5; i++) { try { //讓當(dāng)前執(zhí)行的線程(即 CustomChildThread)睡眠 1s Thread.sleep(1000); //新創(chuàng)建一個(gè) Runnable 用戶處理 UI 工作 MyRunnable runnable = new MyRunnable(HandlerPostFunctionActivity.this, i); //調(diào)用Handler post 方法。 mMainHandler.post(runnable); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 將 Runnable 寫成靜態(tài)內(nèi)部類,防止內(nèi)存泄露 */ public static class MyRunnable implements Runnable { private int progressBarValue; private WeakReference<HandlerPostFunctionActivity> weakReference; MyRunnable(HandlerPostFunctionActivity activity, int value) { this.weakReference = new WeakReference<>(activity); this.progressBarValue = value; } @Override public void run() { HandlerPostFunctionActivity activity = weakReference.get(); if (activity != null && !activity.isFinishing()) { activity.mProgressBar.setProgress(progressBarValue); } } } }
2.2 Kotlin版本的具體代碼如下:
class TestHandlerPostRunnableActivity : AppCompatActivity() { private var mMainHandler: Handler? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_thread_add_handler) handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener { //工作線程開始模擬下載任務(wù) val workThread: WorkThread = WorkThread(this) workThread.start() }) //創(chuàng)建 Handler,關(guān)聯(lián)App的 主Looper 對(duì)象 mMainHandler = Handler(Looper.getMainLooper()) } class WorkThread(private var activity: TestHandlerPostRunnableActivity) : Thread() { private var handler: Handler? = activity.mMainHandler override fun run() { super.run() for (i in 0..6) { sleep(1000) //新建 Runnable 設(shè)置進(jìn)度參數(shù)傳,然后通過 post(Runnable) 方法,讓其更新 progressBar 進(jìn)度 val runnable: MyRunnable = MyRunnable(activity, i) handler?.post(runnable) } } } /** * 處理 UI 工作。 * 靜態(tài)內(nèi)部類,防止內(nèi)存泄露 */ class MyRunnable(activity: TestHandlerPostRunnableActivity, value: Int) : Runnable { private var weakReference = WeakReference(activity) private var progressValue = value override fun run() { val activity = weakReference.get() if (activity != null && !activity.isFinishing) { //獲取任務(wù)執(zhí)行進(jìn)度參數(shù),更新 progressBar 進(jìn)度 activity.handlerAddThreadProgressBar.progress = progressValue } } } }
3. obtainMessage()方法
obtainMessage() 方法與 sendMessage() 方法很相似,通過 mHandler.obtainMessage().sendToTarget() 發(fā)送信息。該方法與 sendMessage() 的區(qū)別就是你不用額外去創(chuàng)建一個(gè) Message 對(duì)象。
obtainMessage() 方法有三種,分別是:
//指定 what 用于區(qū)分,通過 Message.what 獲得 public final Message obtainMessage(int what); //傳遞obj參數(shù),通過 Message.obj 獲得 public final Message obtainMessage(int what, @Nullable Object obj) //傳遞arg1 arg2參數(shù),通過 Message.arg1 Message.arg2 獲得 public final Message obtainMessage(int what, int arg1, int arg2)
3.1 Java版本的具體代碼如下:
public class HandlerObtainMessageActivity extends AppCompatActivity { private TextView mDisplayTv; private Handler mHandler; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_add_thread); TextView titleTv = findViewById(R.id.title_tv); titleTv.setText("Handler + Thread"); mDisplayTv = findViewById(R.id.display_tv); mProgressBar = findViewById(R.id.test_handler_progress_bar); //mHandler用于處理主線程消息隊(duì)列中的子線程消息 mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: //更新 TextView UI mDisplayTv.setText("Handler obtainMessage() Test!!"); break; case 2: //通過 msg.obj 獲取 ProgressBar 的進(jìn)度,然后顯示進(jìn)度值 int process = (int) msg.obj; mProgressBar.setProgress(process); break; default: break; } } }; Button mClickBtn = findViewById(R.id.click_btn); mClickBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { //開啟子線程,子線程處理UI工作 CustomChildThread customThread = new CustomChildThread(); customThread.start(); } }); } /** * 子線程,用于處理耗時(shí)工作 */ public class CustomChildThread extends Thread { @Override public void run() { //發(fā)送第一條消息,代表開始執(zhí)行異步任務(wù) mHandler.obtainMessage(1).sendToTarget(); //模擬耗時(shí)進(jìn)度,將進(jìn)度值傳給主線程用于更新 ProgressBar 進(jìn)度。 for (int i = 1; i <= 5; i++) { try { //讓當(dāng)前執(zhí)行的線程(即 CustomChildThread)睡眠 1s Thread.sleep(1000); //將執(zhí)行進(jìn)度參數(shù) i 傳遞給主線程 progressBar mHandler.obtainMessage(2, i).sendToTarget(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
3.2 Kotlin版本的具體代碼如下:
class TestHandlerObtainMessageActivity : AppCompatActivity() { companion object { const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE" } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_thread_add_handler) handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener { //工作線程開始模擬下載任務(wù) val workThread: WorkThread = WorkThread(this) workThread.start() }) } class WorkThread(activity: TestHandlerObtainMessageActivity) : Thread() { private var handler: MyHandler = MyHandler(activity) override fun run() { super.run() for (i in 0..6) { sleep(1000) //通過 Handler 將進(jìn)度參數(shù)傳遞給 主線程,讓其更新 progressBar 進(jìn)度 val bundle = Bundle() bundle.putInt(PROGRESS_VALUE_KEY, i) handler.obtainMessage(1, bundle).sendToTarget() } } } /** * 靜態(tài)內(nèi)部類,防止內(nèi)存泄漏 */ class MyHandler(activity: TestHandlerObtainMessageActivity) : Handler() { private var weakReference = WeakReference(activity) override fun handleMessage(msg: Message) { super.handleMessage(msg) when (msg.what) { 1 -> { val activity = weakReference.get() if (activity != null && !activity.isFinishing) { //獲取任務(wù)執(zhí)行進(jìn)度參數(shù),然后通過 ProgressBar 顯示出來 val bundle: Bundle = msg.obj as Bundle val progressValue: Int = bundle.get(PROGRESS_VALUE_KEY) as Int activity.handlerAddThreadProgressBar.progress = progressValue } } } } } }
4. 總結(jié):
在實(shí)際開發(fā)中,三種方法的使用都可行,具體用哪種方法,需結(jié)合你的實(shí)際情況及個(gè)人喜好。另外,在實(shí)際使用中往往將 Handler 寫成靜態(tài)內(nèi)部類,這時(shí)需要注意防止內(nèi)存泄露?。═he handler class should be static or leaks might occur),具體代碼見上方!
4.1 在子線程中創(chuàng)建Handler
思考: 在上面代碼中, 我們都是在主線程中創(chuàng)建了 Handler 對(duì)象,那如果在子線程中創(chuàng)建一個(gè) Handler 對(duì)象呢?會(huì)發(fā)生什么呢?
如下所示:我們?cè)?CustomChildThread 線程中,新建一個(gè) Handler 對(duì)象。
public class CustomChildThread extends Thread { @Override public void run() { Handler handler = new Handler(Activity.this); //會(huì)報(bào)錯(cuò):java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() } }
結(jié)果: 拋出異常: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。
原因: 因?yàn)樵趧?chuàng)建 Handler對(duì)象時(shí)要關(guān)聯(lián)所處線程的 Looper對(duì)象,而我們的子線程沒有 Looper,所以會(huì)拋出上述異常。
解決方法: 通過調(diào)用,Looper.prepare() 方法為子線程創(chuàng)建一個(gè) Looper 對(duì)象,并且調(diào)用 Looper.loop() 方法開始消息循環(huán)。如下所示:
class CustomChildThread extends Thread { @Override public void run() { //為當(dāng)前線程創(chuàng)建一個(gè) Looper 對(duì)象 Looper.prepare(); //在子線程中創(chuàng)建一個(gè) Handler 對(duì)象 Handler handler = new Handler() { public void handleMessage(Message msg) { // 在這里處理傳入的消息 } }; //開始消息循環(huán) Looper.loop(); } }
以上就是詳解Android的Handler機(jī)制原理的詳細(xì)內(nèi)容,更多關(guān)于Android Handler機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android部分手機(jī)拍照后獲取的圖片被旋轉(zhuǎn)問題的解決方法
這篇文章主要為大家詳細(xì)介紹了Android部分手機(jī)拍照后獲取的圖片被旋轉(zhuǎn)問題的解決方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01Android 中不用線程如何實(shí)現(xiàn)倒計(jì)時(shí)
本文給大家分享android中不用線程如何實(shí)現(xiàn)倒計(jì)時(shí)功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01Android Studio與SVN版本控制程序的協(xié)作使用指南
這篇文章主要介紹了Android Studio與SVN版本控制程序的協(xié)作使用指南,使用Gradle插件自動(dòng)填寫SVN號(hào)并發(fā)布到指定目錄的方法,需要的朋友可以參考下2016-03-03Android開場動(dòng)畫類完整實(shí)現(xiàn)代碼
這篇文章主要介紹了Android開場動(dòng)畫類完整實(shí)現(xiàn)代碼,是非常實(shí)用的功能,需要的朋友可以參考下2014-07-07android使用ItemDecoration給RecyclerView 添加水印
本篇文章主要介紹了android使用ItemDecoration給RecyclerView 添加水印,介紹了自定義Drawable來完成水印圖片和使用ItemDecoration來布局水印,有興趣的可以了解一下。2017-02-02MacBook M1 Flutter環(huán)境搭建的實(shí)現(xiàn)步驟
本文主要介紹了MacBook M1 Flutter環(huán)境搭建,F(xiàn)lutter官方和各項(xiàng)配套的軟件環(huán)境也還沒有成熟,導(dǎo)致搭建環(huán)境時(shí)碰到了不少坑,本文就詳細(xì)的介紹一下2021-08-08Android 后臺(tái)生成長圖并分享示例(非長截圖)
這篇文章主要介紹了Android 后臺(tái)生成長圖并分享示例(非長截圖),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-08-08Android編程實(shí)現(xiàn)捕獲程序異常退出時(shí)的錯(cuò)誤log信息功能詳解
這篇文章主要介紹了Android編程實(shí)現(xiàn)捕獲程序異常退出時(shí)的錯(cuò)誤log信息功能,結(jié)合實(shí)例形式分析了Android異常信息捕獲與日志操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-08-08