前面在
《Android多线程消息处理机制》讲过looper、thread的原理。
Main线程已经有了looper且已启动,意味着包含了消息队列。
所有没有指定looper对象而创建的handler,使用的都是main的looper。
这样的handler发送的message都放到了main线程的looper管理的消息队列中了。
发现有HandlerThread这个类,以前没用过,总以为是在Thread中内置了一个Handler。
在面试中也总是被问到,很心烦,于是索性来研究一下,正好前面也整体分析了looper,handler等技术。
Main线程已经有了looper且已启动,意味着包含了消息队列。
所有没有指定looper对象而创建的handler,使用的都是main的looper。
这样的handler发送的message都放到了main线程的looper管理的消息队列中了。
发现有HandlerThread这个类,以前没用过,总以为是在Thread中内置了一个Handler。
在面试中也总是被问到,很心烦,于是索性来研究一下,正好前面也整体分析了looper,handler等技术。
先来看下源码吧:
/**
* 创建一个新的线程类,同时包含有一个Looper. 然后可以使用这个Looper创建一个Handler.
* Note:包含有一个Looper意味着什么?
* 启动线程时启动looper消息循环,looper内置有messageQueue。这个线程专用来处理消息的了
* 看文章:Android多线程消息处理机制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773
*/
public class HandlerThread extends Thread {
int mPriority;//线程优先级
int mTid = -1;//进程id
Looper mLooper;//核心对象
public HandlerThread(String name) {
super(name);
//获取线程默认的优先级,0
mPriority = Process.THREAD_PRIORITY_DEFAULT;
}
/**
* 构造函数
* @param name 线程名称
* @param priority 线程优先级. 必须从android.os.Process获取,而不能从java.lang.Thread获取.
*/
public HandlerThread(String name, int priority) {
super(name);
mPriority = priority;
}
/**
* 如果需要在looper循环前执行一些操作时,可以显式的重写此方法
*/
protected void onLooperPrepared() {
}
/**
* 线程的run方法,在线程start()后,就开始执行run方法,
* 在run里面Looper.prepare()创建looper...
* 此内容参考:Android多线程消息处理机制,http://blog.csdn.net/fesdgasdgasdg/article/details/52081773
*/
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();//循环前的回调
Looper.loop();
mTid = -1;
}
/**
* 此方法返回当前线程关联的looper,
* 如果当前线程没有启动或者已停止等原因,此方法返回null
* 如果此方法已经启动,此方法将阻塞知道looper被初始化
* @return The looper.
*/
public Looper getLooper() {
//如果当前线程不处在运行状态,则返回null
if (!isAlive()) {
return null;
}
// 如果此方法已经启动,此方法将阻塞知道looper被初始化
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
/**
* 退出线程的looper循环.
* 使looper线程终止消息循环,不再处理消息队列中的任何消息
* 当looper调用quit之后,任何尝试post message到messageQueue中的操作都会失败
* 比喻:Handler的sendMessage(Message)方法会返回false
* 使用这个方法可能不安全,因为在looper停止之前可能有message没有被执行。
* 考虑使用quitSafely()方法代替quit()方法,以确保所有待完成的工作有序完成。
* @return 如果looper调用了quit,则返回true。如果线程还没有开始执行则返回false
*
* @see #安全退出
*/
public boolean quit() {
Looper looper = getLooper();//获取当前线程的looper
if (looper != null) {
looper.quit();
return true;//成功的调用了quit,返回true
}
return false;
}
/**
* 安全的退出线程looper循环。
* 当messageQueue中所有已到期的message都处理完后,终止looper线程的消息循环。
* 但是由于延时的消息需要等待,故这些消息将不会被处理。
* Pending delayed messages with due times in the future will not be delivered.
* 当looper调用quit之后,任何尝试post message到messageQueue中的操作都会失败
* 比喻:Handler的sendMessage(Message)方法会返回false
* 如果线程没有被启动,或者已经结束则返回false。否则在调用quitSafely()之后返回true。
*/
public boolean quitSafely() {
Looper looper = getLooper();
if (looper != null) {
looper.quitSafely();
return true;
}
return false;
}
/**
* 返回此线程的标识符. 参考Process.myTid().
*/
public int getThreadId() {
return mTid;
}
}
这么一分析,是否都清楚了?无非就是个Thread,在里面内置了一个Looper,looper会自带一个MessageQueue。
在Thread的run方法中使用looper.prepqre()创建了looper。然后赋给当前线程的looper成员变量,供外面的handler使用。
接着调用looper.loop()方法启动消息循环。
前面也讲过可以自己创建LooperThread线程,HandlerThread就是google提供的经典实例,只不过HandlerThread里面提供了线程安全访问,退出消息循环等方法。
具体的用法简单的1C, 下面就用自己创建的LooperThread为例,如果你不喜欢的话,直接把LooperThread替换成现有的HandlerThread类即可。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Handler mHandler;
private LooperThread thread;
private TextView show;
private Button start;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
show = (TextView) findViewById(R.id.show);
start = (Button) findViewById(R.id.start);
start.setOnClickListener(this);
init();
}
private void init() {
thread = new LooperThread();
thread.start();
while (thread.getLooper() == null) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mHandler = new Handler(thread.getLooper()) {
@Override
public void handleMessage(Message msg) {
//处理消息
Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what);
//修改textview会报错的。
//show.setText("");
}
};
}
@Override
public void onClick(View v) {
mHandler.sendEmptyMessage(200);
}
/**
* Looper线程
*/
class LooperThread extends Thread {
private Looper looper;
public Looper getLooper() {
return looper;
}
@Override
public void run() {
super.run();
Looper.prepare();
looper = Looper.myLooper();
Looper.loop();
}
}
}
初始化的时候先创建LooperThread线程,且启动。
然后里面就会有looper了。
在创建handler的时候传入上面的looper。那么此时的handler已和LooperThread的消息队列绑定了。这个handler所发送和处理的消息只经过LooperThread的消息循环,
跟UI线程已有的消息循环没关系了。
最后有一点需要注意:
public void handleMessage(Message msg) {
//处理消息
Log.v("looper的handler", Thread.currentThread().getName() + " - " + msg.what);
//修改textview会报错的。
//show.setText("");
}
这个处理消息的方法,不能再处理UI线程创建的UI控件了。如果你真心想修改某一个ui,那的保证这个ui必须在LooperThread里面创建和添加到window中去。
不多说了,该去关心下宝宝。