Handler是一套 Android 消息传递机制,主要用于线程间通信。
用最简单的话描述: handler其实就是主线程在起了一个子线程,子线程运行并生成Message,Looper获取message并传递给Handler,Handler逐个获取子线程中的Message.
Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信
可以说只要有异步线程与主线程通信的地方就一定会有 Handler。
在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理
使用Handler消息传递机制主要是为了多个线程并发更新UI的同时,保证线程安全
Handler、Message、Message Queue、Looper
消息机制的模型:
消息机制的架构
Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。
这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。
解决方案:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
public class HandlerActivity extends AppCompatActivity {
private Button bt_handler_send;
private static class MyHandler extends Handler {
//弱引用持有HandlerActivity , GC 回收时会被回收掉
private WeakReference weakReference;
public MyHandler(HandlerActivity activity) {
this.weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
//执行业务逻辑
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.HandlerActivity);
//创建 Handler
final MyHandler handler = new MyHandler(this);
bt_handler_send = findViewById(R.id.bt_handler_send);
bt_handler_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//使用 handler 发送空消息
handler.sendEmptyMessage(0);
}
}).start();
}
});
}
@Override
protected void onDestroy() {
//移除所有回调及消息
myHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
获取 Message 大概有如下几种方式:
Message message = myHandler.obtainMessage(); //通过 Handler 实例获取
Message message1 = Message.obtain(); //通过 Message 获取
Message message2 = new Message(); //直接创建新的 Message 实例
通过查看源码可知,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
通过查看 Message 的 obtain 方法
public static Message obtain(Handler h) {
//调用下面的方法获取 Message
Message m = obtain();
//将当前 Handler 指定给 message 的 target ,用来区分是哪个 Handler 的消息
m.target = h;
return m;
}
//从消息池中拿取 Message,如果有则返回,否则创建新的 Message
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
为了节省开销,我们在使用的时候尽量复用 Message,使用前两种方式进行创建。
Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列,post方法需要传入一个Runnalbe对象 ,我们来看看post方法源码
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。
以 sendEmptyMessage(int) 方法为例:
//Handler
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
-> sendMessageAtTime(Message,long)
-> enqueueMessage(MessageQueue,Message,long)
-> queue.enqueueMessage(Message, long);
从中可以发现 MessageQueue 这个消息队列,负责消息的入队,出队。
首先我们在MainActivity中添加一个静态内部类,并重写其handleMessage方法。
private static class MyHandler extends Handler {
private final WeakReference mTarget;
public MyHandler(MainActivity activity) {
mTarget = new WeakReference(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
HandlerActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
//执行业务逻辑
if (msg.what == 0) {
Log.e("myhandler", "change textview");
MainActivity ma = mTarget.get();
ma.textView.setText("hahah");
}
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
然后创建一个类型为MyHandler的私有属性:
private Handler handler1 = new MyHandler(this);
最后在onCreate回调中创建一个线程,用于接收发送消息:
new Thread(new Runnable() {
@Override
public void run() {
handler1.sendEmptyMessage(0);
}
}).start();
总结一下:
首先创建一个MyHandler类。
private static class MyHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
Log.e("child thread", "receive msg from main thread");
}
}
}
声明一个Handler类型的私有变量,进行默认初始化为null。
private Handler handler1;
创建子线程,使handler指向新创建的MyHandler对象。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler1 = new MyHandler();
Looper.loop();
Log.e("child thread", "child thread end");
}
}).start();
在主线程中向子线程中发送消息
while (handler1 == null) {
}
handler1.sendEmptyMessage(0);
handler1.getLooper().quitSafely();
普通的线程是没有looper的,如果需要looper对象,那么必须要先调用Looper.prepare方法,而且一个线程只能有一个looper
Handler是如何完成跨线程通信的?
- 关于管道,简单来说,管道就是一个文件
- 在管道的两端,分别是两个打开文件文件描述符,这两个打开文件描述符都是对应同一个文件,其中一个是用来读的,别一个是用来写的
- 调用JNI函数,初始化NativeMessageQueue对象。NativeMessageQueue则会初始化Looper对象
- Looper的作用就是,当Java层的消息队列中没有消息时,就使Android应用程序主线程进入等待状态,而当Java层的消息队列中来了新的消息后,就唤醒Android应用程序的主线程来处理这个消息
管道,其本质是也是文件,但又和普通的文件会有所不同:管道缓冲区大小一般为1页,即4K字节。管道分为读端和写端,读端负责从管道拿数据,当数据为空时则阻塞;写端向管道写数据,当管道缓存区满时则阻塞。
为什么采用管道而非Binder?
Handler是Android消息机制的上层接口。Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI
如下就是使用消息机制的一个简单实例:
public class Activity extends android.app.Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg.what);
}
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
...............耗时操作
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}
再举个例子:怎么从主线程发送消息到子线程?(虽然这种应用场景很少)
Thread thread = new Thread(){
@Override
public void run() {
super.run();
//初始化Looper,一定要写在Handler初始化之前
Looper.prepare();
//在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//所有的事情处理完成后要退出looper,即终止Looper循环
//这两个方法都可以,有关这两个方法的区别自行寻找答案
handler.getLooper().quit();
handler.getLooper().quitSafely();
}
};
//启动Looper循环,否则Handler无法收到消息
Looper.loop();
}
};
thread.start();
//在主线程中发送消息
handler.sendMessage();
先来解释一下第一行代码Looper.prepare();,先看看Handler构造方法
//空参的构造方法,这个方法调用了两个参数的构造方法
public Handler() {
this(null, false);
}
//两个参数的构造方法
public Handler(Callback callback, boolean async) {
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;
}
第二行代码初始化了Hanlder并且重写HandleMessage()方法,没啥好讲的。
然后调用了Looper.loop()方法,后面会解释。
我们先来看看最后一行代码handler.sendMessage(message) 的主要作用:
先看看这行代码之后的代码执行流程:
sendMessage()之后代码通过图中所示的几个方法,最终执行到了MessageQueue的enqueueMessage()方法,我们来就看看它:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
发送消息的工作已经完成,那么Looper是什么时候取的消息,来看看:
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
看到这里我们应该自然会想到,message.target.是哪个对象?dispatchMessage有什么作用?
public final class Message implements Parcelable {
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
}
//Handler的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
接着看handler的dispatchMessage()方法
/**
*handler的方法
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
看到这里面一个我们非常熟悉到方法了没有?—handleMessage()方法,也是我们处理消息时候的逻辑。
最后再来一个系统的工作示意图:
使用java:
new Thread( new Runnable() {
public void run() {
myView.invalidate();
}
}).start();
可以实现功能,刷新UI界面。但是这样是不行的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
Thread+Handler:
Handler来根据接收的消息,处理UI更新。Thread线程发出Handler消息,通知更新UI。
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case TestHandler.GUIUPDATEIDENTIFIER:
myBounceView.invalidate();
break;
}
super.handleMessage(msg);
}
};
class myThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = TestHandler.GUIUPDATEIDENTIFIER;
TestHandler.this.myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
使用java:
使用Java上自带的TimerTask类,TimerTask相对于Thread来说对于资源消耗的更低,引入import java.util.Timer; 和 import java.util.TimerTask;
public class JavaTimer extends Activity {
Timer timer = new Timer();
TimerTask task = new TimerTask(){
public void run() {
setTitle("hear me?");
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000); // 延迟delay毫秒后,执行一次task。
}
}
TimerTask + Handler:
public class TestTimer extends Activity {
Timer timer = new Timer();
Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
setTitle("hear me?");
break;
}
super.handleMessage(msg);
}
};
TimerTask task = new TimerTask(){
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000); // 延迟delay毫秒后,执行一次task。
}
}
Runnable + Handler.postDelayed(runnable,time):
private Handler handler = new Handler();
private Runnable myRunnable= new Runnable() {
public void run() {
if (run) {
handler.postDelayed(this, 1000);
count++;
}
tvCounter.setText("Count: " + count);
}
};
然后在其他地方调用
handler.post(myRunnable);
handler.post(myRunnable,time);
Handler延迟2s执行一个runnable
Handler handler=new Handler();
Runnable runnable=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
if(xsLayout.getVisibility()==View.VISIBLE){
xsLayout.setVisibility(View.GONE);
}
}
};
handler.postDelayed(runnable, 2000);
在runnable被执行之前取消这个定时任务
handler.removeCallbacks(runnable);
Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。
Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。
线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决
(1)一个线程有几个Handler?
(2)一个线程有几个Looper?如何保证?
(3)Handler内存泄漏的原因?
(4)为何主线程可以new Handler?如果想要在子线程中new Handler要做些什么准备?
- android-28的SystemServer类中:
- main方法是整个android应用的入口,在子线程中调用Looper.prepare()是为了创建一个Looper对象,并将该对象存储在当前线程的ThreadLocal中,每个线程都会有一个ThreadLocal,它为每个线程提供了一个本地的副本变量机制,实现了和其它线程隔离,并且这种变量只在本线程的生命周期内起作用,可以减少同一个线程内多个方法之间的公共变量传递的复杂度。Looper.loop()方法是为了取出消息队列中的消息并将消息发送给指定的handler,通过msg.target.dispatchMassage()方法
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
private void run() {
......
Looper.prepareMainLooper();
......
}
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare(); //初始化Looper
new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Looper.loop();
}
})
(5)子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?
(6)内部是如何确保线程安全的?
(7)使用Message时应该如何创建它?
(8)使用Handler的postDelay后消息队列会有什么变化?
(9)Looper死循环为什么不会导致应用卡死?
- 1、在5s内没有响应输入的事件(例如按键,触摸等),
- 2、BroadcastReceiver在10s内没有执行完毕。
在Android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的。
HandlerThread是Google帮我们封装好的,可以用来执行多个耗时操作,而不需要多次开启线程,里面是采用Handler和Looper实现的。
HanderThread实际上就是一个线程
怎么使用?
//创建实例对象,该参数表示线程的名字
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
//启动我们创建的HandlerThread线程
handlerThread.start();
//怎样将Handler与线程对象绑定在一起
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//发生myHandlerThread线程中
checkForUpdate();
if(isUpdate){
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
public class MainActivity extends AppCompatActivity {
private static final int MSG_UPDATE_INFO = 0x100;
Handler mMainHandler = new Handler();
private TextView mTv;
private Handler mThreadHandler;
private HandlerThread mHandlerThread;
private boolean isUpdate = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv = (TextView) findViewById(R.id.tv);
initHandlerThread();
}
private void initHandlerThread() {
mHandlerThread = new HandlerThread("xujun");
mHandlerThread.start();
mThreadHandler = new Handler(mHandlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
checkForUpdate();
if (isUpdate) {
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
}
};
}
/**
* 模拟从服务器解析数据
*/
private void checkForUpdate() {
try {
//模拟耗时
Thread.sleep(1200);
mMainHandler.post(new Runnable() {
@Override
public void run() {
String result = "实时更新中,当前股票行情:%d";
result = String.format(result, (int) (Math.random() * 5000 + 1000));
mTv.setText(Html.fromHtml(result));
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected void onResume() {
isUpdate = true;
super.onResume();
mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
}
@Override
protected void onPause() {
super.onPause();
isUpdate = false;
mThreadHandler.removeMessages(MSG_UPDATE_INFO);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandlerThread.quit();
mMainHandler.removeCallbacksAndMessages(null);
}
}
运行以上测试代码,将可以看到如下效果图(例子不太恰当,主要使用场景是在handleMessage中执行耗时操作)
基于sdk23的,可以看到,只有一百多行代码:
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
Looper mLooper;
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* Call back method that can be explicitly overridden if nee
ded to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* Returns the identifier of this thread. See Process.myTid(
).
*/
public int getThreadId() {
return mTid;
}
}
(1)首先看看构造函数
public HandlerThread(String name) {
super(name);
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
一个参数的和两个参数的,name代表当前线程的名称,priority为线程的优先级别
(2)看一下run()方法
在run方法里面我们可以看到我们会初始化一个Looper,并设置线程的优先级别
public void run() {
mTid = Process.myTid();
Looper.prepare();
//持有锁机制来获得当前线程的Looper对象
synchronized (this) {
mLooper = Looper.myLooper();
//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
notifyAll();
}
//设置线程的优先级别
Process.setThreadPriority(mPriority);
//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
onLooperPrepared();
Looper.loop();
mTid = -1;
}
前面我们说到使用HandlerThread的时候必须调用 start() 方法,接着才可以将我们的HandlerThread和我们的handler绑定在一起
为什么要使用锁机制和 notifyAll() :
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
总结:在获得mLooper对象的时候存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完成同步问题。
(3)接着我们来看一下quit方法和quitSafe方法
public boolean quit() {
Looper looper = getLooper();
if (looper != null) {
looper.quit();
return true;
}
return false;
}
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
跟踪这两个方法容易知道只两个方法最终都会调用MessageQueue的 quit(boolean safe) 方法
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowedto quit.");
}
synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;
//安全退出调用这个方法
if (safe) {
removeAllFutureMessagesLocked();
} else {//不安全退出调用这个方法
removeAllMessagesLocked();
}
// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
不安全的会调用 removeAllMessagesLocked(); ,我们来看这个方法是怎样处理的,其实就是遍历Message链表,移除所有信息的回调,并重置为null。
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
安全地会调用 removeAllFutureMessagesLocked(); 这个方法,它会根据Message.when这个属性,判断我们当前消息队列是否正在处理消息,没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环。因此说 quitSafe() 是安全的,而 quit() 方法是不安全的,因为quit方法不管是否正在处理消息,直接移除所有回调
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
//判断当前队列中的消息是否正在处理这个消息,没有的话,直接移除所有回调
if (p.when > now) {
removeAllMessagesLocked();
} else {
//正在处理的话,等待该消息处理处理完毕再退出该循环
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
p = n;
}
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}