本文分两部分:Handler部分和HandlerThread部分。
一、handler知识:
情境一:主线程发消息,主线程接收。
UI如下:
代码如下:
public class MainAct extends Activity {
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
text.setText("文案又改了");
break;
}
}
};
private TextView text;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
text = (TextView) findViewById(R.id.text);
}
public void clickme(View view){
handler.post(new Runnable() {
@Override
public void run() {
text.setText("文案改变");
}
});
}
public void clickme2(View view){
handler.sendEmptyMessage(1);
}
}
点击后效果如下:
handler的两种用法:一是通过post(runnable),一是sendEmptyMessage(),需要知道的事,不管是runnable还是sendEmptyMessage传递消息后的handleMessage方法都是在主线程中执行的。
情景二、子线程发消息,主线程接收:
又称为线程间通信。
常见例子举例:发送验证码倒计时处理。
代码如下:
public class MainAct2 extends Activity {
private boolean flag = false;
private static int count = 10;
private TextView send_message;
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1:
send_message.setText(count + "");
count--;
break;
case 2:
send_message.setText("重新发送");
break;
}
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout2);
send_message = (TextView) findViewById(R.id.send_message);
send_message.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (flag == false) {
flag = true;
count = 10;
//子线程启动
new Thread(new Runnable() {
@Override
public void run() {
while (flag) {
handler.sendEmptyMessage(1);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (count == 0) {
flag = false;
handler.sendEmptyMessage(2);
}
}
}
}).start();
}
}
});
}
}
此处启动一个子线程,子线程中有一个while死循环,通过flag来判断是否阻塞。点击判断是否阻塞,不阻塞时发送消息给主线程,让主线程更新UI每一秒钟更新一次。
此处只有一个子线程,子线程中有一个死循环。
二、HanlderThread知识:
HanlderThread就类似这种模式。HandlerThread继承自Thread,所以HandlerThread的start方法同样会启动一个子线程,并执行自身的run方法(源码1)。而不同与Thread的是,HandlerThread中初始化了Looper,然后开启了Loop 轮训。
对上述描述的源码分析如下:HandlerThread的run方法如下(源码1)
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
Looper.prepare(),方法初始化了Looper。如果有对handler源码理解的可以知道,handler初始化的时候会默认当前线程的Looper(如果当前线程是主线程,则获取UI线程的Looper;如果是子线程,则获取子线程Looper,一般情况下子线程需要通过Looper.prepare()方法自己初始化自己的Looper。),通过mLooper
= Looper.myLooper();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
假如是主线程,一个app应用程序启动会有这么一个流程:在ActivityManager启动startActivity,执行ActivityThread的main方法中,执行Looper.prepareMainLooper(),初始化主线程的Looper。
Looper.prepareMainLooper()会通过prepare(false)创建一个不可退出的轮询。
回到我们这里,我们Looper.prepare()会执行prepare(true)创建一个可以退出且需要退出的轮询。
public static void prepare() {
prepare(true);
}
结合下面代码:quitAllowed为true则可以退出,如果为false则不可退出。
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));
}
使用构造方法new Looper(quitAllowed),创建一个独属于子线程自身的Looper轮询。
看一个HandlerThread的例子:每两秒更新一下主UI线程的数据。
UI如下:
代码如下:
public class MainAct3 extends Activity {
private TextView send_message;
private android.os.HandlerThread handlerThread;
private Handler mainHandler = new Handler();
private Handler sonHandler;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout3);
handlerThread =new HandlerThread("mythread");
handlerThread.start();
Log.e("Thread","1、"+Thread.currentThread().getName());
//使用子线程的Looper,减轻主UI线程的压力。通过sonHandler.sendEmptyMessage(1)自己发消息给自己实现while死循环效果
sonHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//此Handler不在主UI线程中,在子线程中
Log.e("Thread","2、"+Thread.currentThread().getName());
update();
sonHandler.sendEmptyMessage(1);
}
};
send_message = (TextView) findViewById(R.id.send_message);
//主线程触发子线程开始更新
send_message.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sonHandler.sendEmptyMessage(1);
}
});
}
//子线程中执行休眠,休眠后发送消息给主UI线程
public void update(){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mainHandler.post(new Runnable() {
@Override
public void run() {
String result = "每隔2秒更新一下数据:";
result += Math.random();
send_message.setText(result);
Log.e("Thread","3、"+Thread.currentThread().getName());
}
});
}
}
代码说明:此例不同于上面的Handler+thread的地方就在于此处使用的Looper是子线程的Looper(见此代码handlerThread.getLooper()),所以Handler的handleMessage方法也在子线程中。
sonHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
//此Handler不在主UI线程中,在子线程中
Log.e("Thread","2、"+Thread.currentThread().getName());
update();
sonHandler.sendEmptyMessage(1);
}
};
所以update方法是在子线程中执行的休眠操作。再通过主UI线程的Handler发送消息给主线程实现更新UI。
最后看看打印的日志来确定当前线程:可以看出sonHandler的handleMessage的确就在子线程mythread中。
01-11 18:21:07.244 8610-8610/haibo.com.helloworld E/Thread: 1、main
01-11 18:21:09.234 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:11.244 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:11.244 8610-8610/haibo.com.helloworld E/Thread: 3、main
01-11 18:21:13.244 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:13.244 8610-8610/haibo.com.helloworld E/Thread: 3、main
01-11 18:21:15.244 8610-8630/haibo.com.helloworld E/Thread: 2、mythread
01-11 18:21:15.254 8610-8610/haibo.com.helloworld E/Thread: 3、main