Handler是什么
每个Handler在创建的时候,都会与一个Looper绑定,UI线程有一个默认的Looper,所以在Activity类的字段中中直接创建Handler,也就是在UI线程中创建Handler就会默认绑定UI线程的Looper,绑定了哪个Looper,就只能给哪个looper发送消息,如果没有绑定Looper那么调用Handler的sendmessage等类似的发送消息的方法就会报错,当然,Handler绑定的哪个looper发送消息的回调也就在那个looper所在线程的handleMessage方法,这一点很重要。
处理机制
通俗的讲Handler的处理机制,你在开会的时候需要上厕所了,然后就报告你的上司,当然自己就相当于Handler,而你的上司就相当于Looper,你的上司考虑了一下,说去吧,然后你就在handleMessage方法中接收到老板的消息,并且起身去了厕所。这样讲会不会比较形象。然后Message只不过是一个封装了信息的容器而已。
总结
>Handler负责发送消息,Looper负责接收Handler发送的消息,并且传回给Handler自己,MessageQueue就是一个存储消息的容器
目录
HandlerThread的作用
其实主要作用是用来在主线程中给子线程发送消息,让子线程处理比较耗时的任务,一般情况下Handler都是在子线程给主线程发送消息,这样来更新UI的,当我们要让Handler在一个子线程中创建处理一些事情的时候,就需要把Handler与一个子线程想关联,这里有两种办法,其中一种不用HandlerThread(其实这并不是一种正确的写法,因为效率太低),是这样写的:
首先定义一个子线程
package com.liran.handlertest;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
/**
* Created by Lenovo on 2015-09-22.
*/
public class MyThrad extends Thread {
private String TAG="handlerTest";
public Handler handler;
public Looper looper;
@Override
public void run() {
super.run();
Looper.prepare();
looper=Looper.myLooper();
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage 当前线程是:"+Thread.currentThread());
}
};
Looper.loop();
}
}
然后再主线程中这样使用
MyThrad myThrad=new MyThrad();
myThrad.start();
myThrad.handler.sendEmptyMessage(1);
然后就闪退了,报的这个错误:
Attempt to invoke virtual method ‘boolean android.os.Handler.sendEmptyMessage(int)’ on a null object reference
具体原因我一会再说,先稍微改一下。
//在start之后线程休眠500毫秒
MyThrad myThrad=new MyThrad();
myThrad.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThrad.handler.sendEmptyMessage(1);
这一次就非常正常的运行并且输出了结果
handleMessage 当前线程是:Thread[Thread-187,5,main]
这是一个子线程。具体是为什么呢?
因为myThrad.handler.sendEmptyMessage(1); 这一句是在UI线程中执行的,上面的start之后两条线程同时执行,当执行到了sendEmptyMessage的时候,此时的myThrad可能还没有初始化,所以就会出现空指针异常了,当然,如果此时的Handler没有关联一个Looper也会出现异常,直接就是闪退级别的。这个时候HandlerThread就派上用场了。再看完全正确的写法:
//为了清楚的看到区别,我在Oncreate中输出了主线程的信息
//声明变量
private HandlerThread handlerThread;
private Handler handlerTest;
handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
//handlerThread的Looper绑定
handlerTest = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage 当前线程是:"+Thread.currentThread());
}
};
handlerTest.sendEmptyMessage(1);
运行的结果是:
oncreat 当前线程是:Thread[main,5,main]
handleMessage 当前线程是:Thread[handlerThread,5,main]
完全正确的结果。
也就是说完全可以用HandlerThread实现一个异步任务。
四种更新UI的方法
private void updateUI2() {
textView.post(new Runnable() {
@Override
public void run() {
textView.setText("textView.post");
}
});
}
private void updateUI1() {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("runOnUiThread");
}
});
}
private void Handler2() {
handler.sendEmptyMessage(1);
}
private void Handler1() {
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("handler.post");
}
});
}
下面是这个Activity的完整代码:
package com.liran.handlertest;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
/**
* 四种在子线程中更新UI的方法
*/
public class MainActivity extends Activity {
private String TAG = "handlerTest";
private TextView textView;
private HandlerThread handlerThread;
private Handler handlerTest;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textView.setText("handleMessage");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textview1);
Log.d(TAG + "main", "oncreat 当前线程是:" + Thread.currentThread());
/*MyThrad myThrad=new MyThrad();
myThrad.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThrad.handler.sendEmptyMessage(1);*/
/*
handlerThread = new HandlerThread("handlerThread");
handlerThread.start();
//handlerThread的Looper绑定
handlerTest = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage 当前线程是:"+Thread.currentThread());
}
};
handlerTest.sendEmptyMessage(1);*/
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**如果前面休眠两秒钟的话,执行下面直接settext就是报异常,如果不休眠直接修改就没有这个异常
* 因为安卓底层对非UI线程更新UI的判断放在onResume中进行,OnCreat在这个方法之前,所以如果、
* 不进行睡眠,就一定可以更新成功,这算是凑巧在非ui线程中更新了UI。*/
// textView.setText("试试看看");
Handler1();
// Handler2();
// updateUI1();
// updateUI2();
}
}).start();
}
private void updateUI2() {
textView.post(new Runnable() {
@Override
public void run() {
textView.setText("textView.post");
}
});
}
private void updateUI1() {
runOnUiThread(new Runnable() {
@Override
public void run() {
textView.setText("runOnUiThread");
}
});
}
private void Handler2() {
handler.sendEmptyMessage(1);
}
private void Handler1() {
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("handler.post");
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
结束语
总之,所有的东西都在我的github上,每天更新新的东西。地址:我的github地址