Handler与HandlerThread的理解
1.什么是Handler:
Handler是android给我们提供用来更新UI的一套机制(某一点),也是一套消息处理的机制,我们可以发送消息,也可以通过它来处理消息。
首先我们来看下我们平时在子线程中发送消息给UI线程更新我们的UI组件。
public class MainActivity extends AppCompatActivity {
private TextView textView=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView=new TextView(this);
textView.setTextSize(20);
setContentView(textView);
new mThread().start();
}
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
String str = (String) msg.obj;
textView.setText(str);
}
};
class mThread extends Thread{
@Override
public void run() {
super.run();
try {
Thread.sleep(2000);
Message message = handler.obtainMessage();
message.obj="hello";
handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
很简单,这就是我们平时利用handler来更新UI的代码。
我们要讲的是我们的handler在这里是怎么处理我们发送的消息的。这里就涉及到了一个叫做Looper的对象,那么它是怎么做的呢?我们用一张图片来展示。
可以看到我们通过handler来发送我们的消息到MessageQueue(消息队列)中,然后我们的Looper通过loop()方法不断的去取出我们MessageQueue中的消息,然后再交给handler自己处理。这就是Handler的一个主要工作过程;
到这里,我们就对我们的子线程通过handler发送给主线程更新ui的过程有一个大概的理解了。由于我们上面的代码的handler是在我们的主线程中创建了,根本看不到Looper的身影,那么这是为什么呢?因为我们UI线程的特殊性,其内部已经对Looper有了一个封装(感兴趣的可以去跟踪下源码)。接下来我们就来在子线程中创建handler,看看怎么使用我们的Looper;
public class MainActivity extends AppCompatActivity {
private TextView textView = null;
private mThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("hello");
setContentView(textView);
thread = new mThread();
thread.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.handler.sendEmptyMessage(0);
}
class mThread extends Thread {
Handler handler;
@Override
public void run() {
Looper.prepare();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// Toast.makeText(MainActivity.this,Thread.currentThread()+"",Toast.LENGTH_SHORT).show();
Log.i("xy", "current:" + Thread.currentThread());
}
};
Looper.loop();
}
}
}
这样我们就将handler作用在了我们的子线程上了。
注意我们onCreate()里面的Thread.sleep(500),这里如果不这样做就会报出一个空指针的异常,原因:
子线程中 handler一开始是没有实例化的 实例化在start()中进行,所以在执行thread.start()后,直接执行thread.handler.send...方法此时也许thread.start()中还未执行到给handler实例化的那一步,自然抛出异常,解决方法之二就是在thread中定义handler时候直接实例化。
OK,接着我们在主线程中再来创建一个handler,但是我们的Looper对象是我们刚才的子线程。
public class MainActivity extends AppCompatActivity {
private TextView textView = null;
private mThread thread;
private Handler handlers;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText("hello");
setContentView(textView);
thread = new mThread();
thread.start();
//这里我们的handler是运行在主线程中的,同理,我们的Looper也就运行在主线程中的handler里面了
handlers=new Handler(thread.looper){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg);
}
};
//主线程发送消息到子线程中的Looper里面
handlers.sendEmptyMessage(1);
}
class mThread extends Thread {
Handler handler;
Looper looper;
@Override
public void run() {
Looper.prepare();
looper=Looper.myLooper();
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// Toast.makeText(MainActivity.this,Thread.currentThread()+"",Toast.LENGTH_SHORT).show();
Log.i("xy", "current:" + Thread.currentThread());
}
};
Looper.loop();
}
}
}
我们运行我们的程序会发现,此时我们的程序崩溃了。这是因为当多线程对Looper作用的时候,由于我们的Looper是在子线程中创建的,而我们的主线程现在要作用在其之上,可是此时我们的Looper对象还没有创建,这样当然就会抛出一个空指针的异常。(其實解決這個問題只需要在.start()方法之后給其一個緩存的方法。Thread.sleep(500)就可以了,和上面的那個情況差不多)
在我们自定义Looper时会经常碰到这样的问题,那么我们怎样去处理这个问题呢?这里就要用到我们的HandlerThread了。
由于篇幅过长,我们将在下一张中来对我们HandlerThread做一个详细的讲解。