Android(安卓)Handler 消息传递机制
出于性能优化考虑,Android 的 UI 操作并不是线程安全的,这意味着如果多个线程并发操作 UI 组件,可能导致线程安全问题。为了解决这个问题,Android 制定了一条简单的规则:只允许 UI 线程修改 Activity 里的 UI 组件。
当一个程序启动时,Android 会同时启动一个主线程(Main Thread),主线程主要负责处理与 UI 相关的事件,如用户的按键事件、触屏事件及屏幕绘制事件,并把相关的事件分发到对应的组件进行处理。所以主线程也称为 UI 线程。
Android 的消息传递机制是另一种形式的“事件处理”,这种机制主要是为了解决 Android 应用的多线程问题 —— Android 平台只允许 UI 线程修改 Activity 里的 UI 组件,这样就会导致新启动的线程无法动态改变界面组件的属性值。此时,就需要借助于 Handler 的消息传递机制来实现新启动的线程改变界面组件的属性值。
Handler 类简介###
Handler 类的主要作用有两个:
1.在子线程中发送消息;
2.在主线程中获取、处理消息。
例如,下面的使用示例,自动播放图片:
主布局文件的内容:
<?xml version="1.0" encoding="utf-8"?>
主程序文件的代码:
package com.toby.personal.testlistview;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.widget.ImageView;import java.util.Timer;import java.util.TimerTask;public class MainActivity extends AppCompatActivity { final private static String TAG = "Toby_Test"; final private static int MsgWhat = 20170326; final private static int Duration = 1200; private int currentImageId = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final int[] imageIds = new int[]{ R.drawable.dog_001, R.drawable.dog_002, R.drawable.dog_003, R.drawable.dog_004, R.drawable.dog_005, }; final ImageView imageView = (ImageView) findViewById(R.id.imageView); final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == MsgWhat) { imageView.setImageResource(imageIds[currentImageId++ % imageIds.length]); } super.handleMessage(msg); } }; new Timer().schedule(new TimerTask() { @Override public void run() { handler.sendEmptyMessage(MsgWhat); } }, 0, Duration); }}
程序运行之后,将开始自动播放图片,具体使用效果,各位可自行尝试运行。
Handler、Loop、MessageQueue 的工作原理###
与 Handler 一起工作的几个组件:
Message —— Handler 接收和处理的消息对象;
Looper —— 每个线程只能拥有一个 Looper。它的 loop 方法负责读取 MessageQueue 中的消息,读到消息之后就把消息交给发送该消息的 Handler 进行处理。
MessageQueue —— 消息队列,它采用先进先出的方式来管理 Message,程序创建 Looper 对象时会在它的构造器中创建 Looper 对象。Looper 提供的构造器源代码如下:
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread();}
Handler 的作用是发送和处理消息,程序使用 Handler 发送消息,消息需要使用 MessageQueue 对象进行保存,而 MessageQueue 是由 Looper 进行管理的,因此必须在当前线程中有一个 Looper 对象。可分为如下两种情况:
主线程中 —— 系统已经初始化了 Looper 对象,因此程序直接创建 Handler 即可,然后就可以通过 Handler 来发送和处理消息;
子线程中 —— 必须自己创建一个 Looper 对象,并启动它:创建 Looper 对象并调用它的 prepare() 方法即可。
prepare() 方法保证每个线程最多只有一个 Looper 对象。prepare() 方法的源代码如下:
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 的静态 loop() 方法来启动它。loop() 方法使用一个死循环不断取出 MessageQueue 中的消息,并将取出的消息分给该消息对应的 Handler 进行处理。下面是 Looper 类的 loop() 方法的源代码:
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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }
总结一下,Looper、MessageQueue、Handler 各自的作用如下。
Looper:每个线程只有一个 Looper,它负责管理 MessageQueue,会不断地从 MessageQueue 中取出消息,并将消息分给对应的 Handler 处理。
MessageQueue:由 Looper 负责管理。它采用先进先出的方式管理 Message。
Handler:它能把消息发送给 Looper 管理的 MessageQueue,并负责处理 Looper 分给它的消息。
在线程中使用 Handler 的步骤如下。
1.调用 Looper 的 prepare() 方法为当前线程创建 Looper 对象,创建 Looper 对象时,它的构造器会创建与之配套的 MessageQueue。
2.有了 Looper 之后,创建 Handler 子类的实例,重写 HandleMessage() 方法,该方法负责处理来自于其他线程的消息。
3.调用 Looper 的 loop() 方法启动 Looper。
Looper 与 Handler 的用法实例,使用新线程计算质数
布局文件:
<?xml version="1.0" encoding="utf-8"?>
主程序文件的代码如下:
package com.toby.personal.testlistview;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.EditText;import android.widget.Toast;import java.util.ArrayList;import java.util.List;public class MainActivity extends AppCompatActivity { final private static String TAG = "Toby_Test"; final private static String KEY_NUM = "KEY_NUM_TEST"; final private static int MsgWhat = 20170326; private EditText editText = null; private CalculationThread calculationThread = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = (EditText) findViewById(R.id.editText); calculationThread = new CalculationThread(); calculationThread.start(); } public void calculation(View view) { Message message = new Message(); message.what = MsgWhat; Bundle bundle = new Bundle(); String numb = editText.getText().toString(); bundle.putInt(KEY_NUM, Integer.parseInt(numb.isEmpty() ? "100" : numb)); message.setData(bundle); calculationThread.handler.sendMessage(message); } private class CalculationThread extends Thread { Handler handler; @Override public void run() { Looper.prepare(); handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == MsgWhat) { final int upper = msg.getData().getInt(KEY_NUM); List numbs = new ArrayList<>(); outer: for (int i = 2; i <= upper; ++i) { for (int j = 2; j <= Math.sqrt(i); ++j) { if (i != 2 && i % j == 0) { continue outer; } } numbs.add(i); } Toast.makeText(MainActivity.this, numbs.toString(), Toast.LENGTH_LONG).show(); } super.handleMessage(msg); } }; Looper.loop(); super.run(); } }}
该示例的运行效果如下图所示:
显示效果
参考文献:《疯狂Android讲义(第2版)》
更多相关文章
- 教你如何在 Android(安卓)使用多线程下载文件
- Android(安卓)多线程编程:IntentService & HandlerThread
- Android的消息机制
- Android使用http协议与服务器通信
- 学习Android(安卓)Handler消息传递机制
- Android串口通信(Android(安卓)Studio)
- Andriod数据推送方案
- Android(安卓)GLES多线程处理
- Android输入分析