关于Android的多线程机制,面试的时候总是问到,今天专门写这个博客,目的是把handler说清楚。
分别从下面四个方向说清楚。
由来
问题:为什么有handler?
我们知道java是支持多线程的,而一个APP只有一个UI(即屏幕只有一个),如果每个线程都可以更新UI,呵呵,估计我们的APP就乱套了,所以Android的设计者就想着,更新UI只能在主线程中,子线程是不能更新UI的,子线程如果更新UI,系统就会抛出异常。所以Android就需要提供一种机制,子线程通过这种机制可以在主线程中更新UI。
那么java有没有提供在子线程之间实时通讯的机制??? 由于线程的特性,一个进程内的所有线程够共享同一存储空间,即在多线程中,线程是可以访问同一内存单元,这样很可能出现数据不同步的问题,因此java提供了 synchronized关键字 和 Object 类的wait和notify方法(当然java还提供别的方法),但是此方法对Android更新UI这种特殊场合不是很适用。
因此Android专门提供了异步消息处理机制,用来解决在Android子线程中更新UI的操作。即handler机制。
应用
handler机制是为了在子线程中更新UI的,但是handler的作用不仅仅是为了更新UI的,准确的说是为了异步消息处理,因此从以下三种情况说明消息是如何发送和接受的。
子线程向主线程发送消息
Android是基于Java的,所以也分主线程,子线程!
(1)主线程:实现业务逻辑、UI绘制更新、各子线程串连,类似于将军;
(2)子线程:完成耗时(联网取数据、SD卡数据加载、后台长时间运行)操作,类似于小兵;
对于使用者来说,代码默认执行在主线程中,子线程的代码在new thread内执行的代码。
第一步:在主线程中创建一个handler
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_TEXT:
// 在这里可以进行UI操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
第二步:开启一个子线程,在子线程里直接使用Handler发送消息即可
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); // 将Message对象发送出去
}
}).start();
主线程向子线程发送消息
分两种情况:
方法1在子线程里初始化Looper
主线程向子线程发送消息的话,我们需要在子线程里初始化Looper,并在主线程里创建的Handler引用子线程的Looper(Handler中引用的是哪个线程的Looper,就在哪个线程里处理消息),下面看代码:
//方法1
public void method1(){
MyThread thread = new MyThread();
thread.start();//千万别忘记开启这个线程
// delay(1000); //在Handler初始化的时候,thread.looper还没有初始化,所以加一个延时
// 或者使用 wait() 方法。
synchronized (thread) {
if(thread.looper == null) {
try{
thread.wait();
}catch (Exception e){
e.printStackTrace();
}
}
}
handler1 = new Handler(thread.looper){
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE:
// 在这里可以进行UI操作
Log.d("当前子线程是--1--->",Thread.currentThread()+"");
break;
default:
break;
}
};
};
}
//子线程
class MyThread extends Thread{
private Looper looper;//取出该子线程的Looper
public void run() {
Looper.prepare();//创建该子线程的Looper
looper = Looper.myLooper();//取出该子线程的Looper
// 使用 notify() 方法唤醒。
synchronized (this) {
this.notify();
}
Looper.loop();//只要调用了该方法才能不断循环取出消息
}
}
主线程发送消息:
//下面是主线程发送消息
Message message1 = new Message();
message1.what = UPDATE;
handler1.sendMessage(message1);
break;
这种方法有个问题,就是Handler初始化的时候,thread.looper还没有初始化,会导致Crash,上面的代码中加了一个延时解决问题了,或者使用 wait()和notify()方法解决,但有更好的解决办法,就是使用HandlerThread。
方法2使用HandlerThread
Android提供了HandlerThread类,子线程中有handler,代码如下:
//方法2
public void method2(){
//实例化一个特殊的线程HandlerThread,必须给其指定一个名字
HandlerThread thread = new HandlerThread("handler thread");
thread.start();//千万不要忘记开启这个线程
//将mHandler与thread相关联
handler2 = new Handler(thread.getLooper()){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case UPDATE:
// 在这里可以进行UI操作
Log.d("当前子线程是--2--->",Thread.currentThread()+"");
break;
default:
break;
}
};
};
}
子线程向子线程发送消息
创建子线程,该子线程接受并处理消息,(注意,该子线程的创建也可以应用上面的情况)
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
childHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SEND:
// 在这里可以进行UI操作
Log.d(TAG,"这个消息是从-->>" + msg.obj + "过来的,在" + Thread.currentThread()+ "子线程当中执行的");
break;
default:
break;
}
}
};
Looper.loop();//开始轮循
}
}).start();
发送消息线程:
new Thread(new Runnable() {
@Override
public void run() {
Message msg = childHandler.obtainMessage();
msg.what = SEND;
msg.obj = ""+ Thread.currentThread();
childHandler.sendMessage(msg);
}
}).start();
以上分析主要参考Android使用Handler实现子线程与子线程、主线程之间通信,但是这偏文章没有写好的源代码,在总结这篇文章的基础上,实现代码并上传github,可直接下载运行。
原理
原理分析,主要查看郭霖的《第一行代码,第二版》。
(1)ActivityThread.main() 函数中,调用 Looper.prepareMainLooper();
(2)Looper.prepareMainLooper(); 的作用是初始化当前线程作为一个主线程。具体内容是,创建一个Looper对象,并将该对象赋值给sThreadLocal变量。
(2.1)在创建Looper对象的时候,其构造函数会创建MessageQueue对象。
(3)最后,ActivityThread.main() 会调用Looper.loop(); 函数。
(4)而Looper.loop();函数会从队列中取出message,并调用Handler. dispatchMessage()函数。
从上面可以看出,其关键是Looper对象。
java实现
理解了handler机制后,我们可不可以用java手写一个handler机制呢?
参考:
Android 消息处理机制(Looper、Handler、MessageQueue,Message)