线程的应用场景
线程与进程的区别
线程间通信
涉及的类:
线程安全问题
子线程中更新UI的几种方式
Handler机制
1. Handler定义
2. Handler的作用
3 .相关概念
4. Handler工作机制
- Handler初始化: 在主线程中创建Looper、MessageQueue、Handler,创建完成后,Looper则自动进入消息循环状态 。同时,Handler自动绑定Looper和MessageQueue。如果消息队列为空,则线程阻塞。主线程中由于在ActivityThread的main()方法中已经初始化过了,所以就不需要在初始化Looper和MessageQueue了。
- 子线程发送消息:子线程中通过Handler向消息队列MessageQueue中发送消息。
- 消息循环: Looper 循环取出MessageQueue中的Message消息。Looper 将循环取出的消息分发给Handler中的handleMessage()方法。
- 接收处理消息: 在handleMessage(Message msg)方法中处理消息。
- 创建Looper对象
- 创建MessageQueue对象
- 让Looper对象持有当前线程
- Looper.prepare()———为当前线程创建一个Looper;
- Looper.loop() ——— 开启消息循环;
- Looper.prepareMainLooper() ——— 为主线程创建Looper时使用,在ActivityThread有用到。
- Looper.getMainLooper() ——— 通过该方法可以获取主线程的Looper。
- Looper.quit() ——— 退出Looper循环。
- Looper.quitSafely() ——— 自己创建的Looper,在不使用的时候,需要退出。
- 一个Thread线程只能绑定到一个Looper循环器上,但可以有多个Handler实例处理者。
- 一个Looper循环器可以绑定多个Handler实例。比如主线程的main()方法中创建了Looper和Handler对象,但是我们开发过程中仍然可以创建其他Handler对象。
- 一个Handler对象处理者,只能绑定到一个Looper循环器中。
5. Handler的使用
public class MainActivity extends Activity {private static final String TAG = "MainActivity";private Handler mHandler;private Button btnSendeToMainThread;private static final int MSG_SUB_TO_MAIN= 100;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btnSendeToMainThread = (Button) findViewById(R.id.btn_sendto_mainthread);// 1.创建Handler,并重写handleMessage方法mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);// 处理消息switch (msg.what) {case MSG_SUB_TO_MAIN:// 打印出处理消息的线程名和Message.objLog.e(TAG, "接收到消息: " + Thread.currentThread().getName() + ","+ msg.obj);break;default:break;}}};btnSendeToMainThread.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 创建一个子线程,在子线程中发送消息new Thread(new Runnable() {@Overridepublic void run() {Message msg = Message.obtain();msg.what = MSG_SUB_TO_MAIN;msg.obj = "这是一个来自子线程的消息";// 2.发送消息mHandler.sendMessage(msg);}}).start();}});}
}
// 创建一个子线程,并在子线程中创建一个Handler,且重写handleMessage
new Thread(new Runnable() {@Overridepublic void run() {//子线程中创建Handler接收器,就必须创建Looper。Looper.prepare();subHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);// 处理消息switch (msg.what) {case MSG_MAIN_TO_SUB:Log.e(TAG, "接收到消息:" + Thread.currentThread().getName() + ","+ msg.obj);break;default:break;}}};Looper.loop();}
}).start();btnSendToSubThread = (Button) findViewById(R.id.btn_sendto_subthread);
btnSendToSubThread.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Message msg = Message.obtain();msg.what = MSG_MAIN_TO_SUB;msg.obj = "这是一个来自主线程的消息";// 主线程中发送消息subHandler.sendMessage(msg);}
});
6. Handler导致内存泄漏分析
/*** 方式1:新建Handler子类(普通内部类)*/
public class MainActivity extends AppCompatActivity {public static final String TAG = "carson:";private Handler showhandler;// 主线程创建时便自动创建Looper & 对应的MessageQueue// 之后执行Loop()进入消息循环@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1. 实例化自定义的Handler类对象->>分析1//注:此处并无指定Looper,故自动绑定当前线程(主线程)的 Looper、MessageQueueshowhandler = new FHandler();// 2. 启动子线程1new Thread() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg = Message.obtain();msg.what = 1;// 消息标识msg.obj = "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3. 启动子线程2new Thread() {@Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg = Message.obtain();msg.what = 2;// 消息标识msg.obj = "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}// 分析1:自定义Handler子类class FHandler extends Handler {// 通过复写handlerMessage() 从而确定更新UI的操作@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.d(TAG, "收到线程1的消息");break;case 2:Log.d(TAG, " 收到线程2的消息");break;}}}
}
/*** 方式2:匿名Handler内部类*/
public class MainActivity extends AppCompatActivity {public static final String TAG = "carson:";private Handler showhandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1. 通过匿名内部类实例化的Handler类对象showhandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1:Log.d(TAG, "收到线程1的消息");break;case 2:Log.d(TAG, " 收到线程2的消息");break;}}};// 2. 启动子线程1new Thread() {@Overridepublic void run() {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg = Message.obtain();msg.what = 1;// 消息标识msg.obj = "AA";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();// 3. 启动子线程2new Thread() {@Overridepublic void run() {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// a. 定义要发送的消息Message msg = Message.obtain();msg.what = 2;// 消息标识msg.obj = "BB";// 消息存放// b. 传入主线程的Handler & 向其MessageQueue发送消息showhandler.sendMessage(msg);}}.start();}
}
7. Handler中的Looper如何停止
public class LooperActivity extends AppCompatActivity {private static final String TAG = "LooperActivity";private Button btn;private Handler mHandler;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_looper);btn = (Button) findViewById(R.id.btn);// 开启一个子线程,去执行异步任务new Thread(new Runnable() {@Overridepublic void run() {Looper.prepare();mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);}};Log.e(TAG, "Looper.loop之前" );// Looper.loop方法是一个死循环Looper.loop();// 得不到执行Log.e(TAG, "Looper.loop之后" );}}).start();btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 调用Looper的quit方法,停止LoopermHandler.getLooper().quit();}});}
}
8. Looper 死循环为什么不会导致应用卡死?
Launch桌面的图标第一次启动 Activity 时,会最终走到 ActivityThread 的 main 方法,在 main 方法里面创建了 Looper 和 MessageQueue 来处理主线程的消息,然后 Looper.loop 方法就进入死循环。我们的 Activity 的生命周期都是通过 Handler 机制来处理的。在看看看 loop 方法循环:
主线程主要依靠的就是消息循环,一旦退出消息循环,那么应用也就退出了。 Looper.loop() 方法可能会引起主线程的阻塞,但只要它的消息循环没有被阻塞,能一直处理消息事件,就不会产生 ANR 异常。
造成 ANR 异常的不是主线程阻塞,而是主线程中 Looper 的消息处理过程发生了任务阻塞从而无法响应手势操作,不能及时刷新UI。
阻塞与ANR无必然关系,虽然主线程在没有消息可处理的时候是阻塞状态的,但是只要保证有消息的时候能够得到立即处理,程序就不会发生 ANR。
总结:应用卡死跟这个Looper 没有关系,应用在没有消息需要处理的时候,Looper 它是在睡眠,释放线程;卡死是 ANR,而 Looper 是睡眠。
9. MessageQueue源码详解
boolean enqueueMessage(Message msg, long when) {//即不允许添加同步消息屏障,实现异步消息了。if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {//. . . . . .msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;//当队列为空,第一次发来的 message,或者新发来的 message 的时间更小的情况,就走if块,添加到链表头部。if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;//如果此时Looper处于阻塞状态,则唤醒。并循环执行 message 的读取。needWake = mBlocked;} else {// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.// 将发来的 message 插入到 queue 队列的中间或者尾部。// 这种情况下,我们不需要唤醒事件队列。除非有一个消息屏障在队列的头部,并且它是队列中最早的异步消息。// 判断当前发送来的 message 是否是一个异步消息,如果是异步消息,并且队列 queue头部含有 消息屏障// p.target = null, 同时当前 queue是处于阻塞状态。则设为 “需要唤醒队列”。 //这里的 p 消息,也有可能是 postAsyncBarrier()中传到 消息队列里的,因此此处需要处理异步消息的情况needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;// 执行一个 for循环,并通过时间的对比,将新发来的消息,插入到队列中适当的位置。for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}//如果队列头部有了 target=null 的一个消息屏障,并且当前发送过来的 message 是一个异步消息。// 如果添加 message 之前,队列中还有其他异步消息等待处理,则就不唤醒 queue。否则就唤醒。if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p;prev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;
}
2. 源码——Looper
public static void loop() {boolean slowDeliveryDetected = false;for (;;) {Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}try {// 将事件交由 Handler处理msg.target.dispatchMessage(msg);dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}// 回收msg,以便 Message.obtain() 实现复用。msg.recycleUnchecked();}
}
3. 源码——MessageQueue
Message next() {// mPtr,Native层的 MessageQueue。final long ptr = mPtr;if (ptr == 0) {return null;}int nextPollTimeoutMillis = 0;for (;;) {// Native层设置阻塞时间—延迟消息的时间nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// Try to retrieve the next message. Return if found.final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages; // 获取链表头部信息if (msg != null && msg.target == null) {// Stalled by a barrier. Find the next asynchronous message in the queue.// 循环遍历,查找第一个 异步消息。会直接忽略 queue 中的其他消息。do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous());}if (msg != null) {if (now < msg.when) {// Next message is not ready. Set a timeout to wake up when it is ready.// 根据延迟消息,设置阻塞时间nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else {// 返回一个即时消息,或者返回一个已到时间的 延迟消息。mBlocked = false; // 设置为 queue未阻塞状态if (prevMsg != null) {prevMsg.next = msg.next;} else {mMessages = msg.next;}msg.next = null;msg.markInUse();return msg;}} else {// queue 队列中没有消息时,会返回 -1,表示 nativePollOnce() 会一直阻塞 nextPollTimeoutMillis = -1;}}}
}
4. 延迟消息Handler.postDelay(new Runnable(), 2000)
//使用方式
Handler handler = new Handler();
handler.postDelayed(new Runnable() {@Overridepublic void run() {//延迟一秒后,执行这里面的内容}
},1000);
handler.removeCallback();
传入的Runnable,最终存储在了mesage.callback 中。也是一种延迟消息,当到时间时,looper循环取出后,会执行Handler中的dispatchMessage()方法。判断如果 callback 不为空,就执行 callback.run()。callback就是一个Rnnable对象。
Hanlder同步消息屏障
@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);System.out.println("打印: " + msg.what);switch (msg.what){case 999://第四步 异步消息执行完之后必需,移除消息屏障,否则会一直阻塞 queue。removeSyncBarrier();break;}}};new Thread(new Runnable() {int max = 0;@Overridepublic void run() {while (max != 10) {handler.sendEmptyMessageDelayed(max, 5000 + 500 * max);max++;}}}).start();new Thread(new Runnable() {@Overridepublic void run() {Message message = Message.obtain();message.what = 999;// 第一步 设置 message为 异步消息message.setAsynchronous(true);// 第二步 设置消息屏障postSyncBarrier();// 第三步 发送消息handler.sendMessageDelayed(message, 0);}}).start();
}private int token = 0;//设置消息屏障时返回的token,用于删除消息屏障时使用。// 反射执行投递同步屏障
public void postSyncBarrier() {Method method = null;try {method = MessageQueue.class.getDeclaredMethod("postSyncBarrier");token = (int) method.invoke(Looper.getMainLooper().getQueue());} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}
}// 反射执行移除同步屏障
public void removeSyncBarrier() {Method method = null;try {method = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class);method.invoke(Looper.getMainLooper().getQueue(), token);} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}
}
源码分析 --- 消息屏障
// 8.0之后该方法改为 @hide隐藏了,因此需要通过反射获取。
public int postSyncBarrier() {return postSyncBarrier(SystemClock.uptimeMillis());
}
//注意⚠️ 这里添加message的时候,target没有设置,默认为null — — 即消息屏障
private int postSyncBarrier(long when) {// Enqueue a new sync barrier token.// We don't need to wake the queue because the purpose of a barrier is to stall it.synchronized (this) {final int token = mNextBarrierToken++;final Message msg = Message.obtain();msg.markInUse();msg.when = when;msg.arg1 = token;Message prev = null;Message p = mMessages;// 从链表的头部开始,找出队列中最后一个message,或者找出queue 中间的延迟时间晚于当前message延迟时间的if (when != 0) {while (p != null && p.when <= when) {prev = p;p = p.next;}}if (prev != null) { // 如果queue队列前面有其他message,则将当前 message插入到这两个 message中间 msg.next = p;prev.next = msg;} else { // 如果前面没有 message,则将当前的 message设置为最头部的 message。msg.next = p;mMessages = msg;}// 返回一个 token,用于接下来的 移除工作。return token;}
}
步骤三、发送消息
public void removeSyncBarrier(int token) {// Remove a sync barrier token from the queue.// If the queue is no longer stalled by a barrier then wake it.synchronized (this) {Message prev = null;Message p = mMessages;while (p != null && (p.target != null || p.arg1 != token)) {prev = p;p = p.next;}if (p == null) {throw new IllegalStateException("The specified message queue synchronization "+ " barrier token has not been posted or has already been removed.");}final boolean needWake;if (prev != null) {prev.next = p.next;needWake = false;} else {mMessages = p.next;needWake = mMessages == null || mMessages.target != null;}p.recycleUnchecked();// If the loop is quitting then it is already awake.// We can assume mPtr != 0 when mQuitting is false.if (needWake && !mQuitting) {nativeWake(mPtr);}}
}