handler
handler是什么?
handler是android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。
handler两个主要用途:
安排消息或runnable对象在未来某个时间点执行。
安排动作在不同于自己的线程执行。
为什么要去使用handler
android在设计的时候,就封装了一套消息创建,传递,处理机制,如果不遵循这样的机制,就没办法更新UI信息,就会有异常。(子线程中更新UI是不安全的,多个线程去更新它会造成UI错乱)
如何使用handler
handler会绑定到创建它的线程中,线程中管理着一个Looper,Looper管理着MessageQueue,handler可以给MessageQueue发送Message,Looper轮询,等到处理这个Message时候调用message.target.dispatchMessage(msg)方法,target就是handler自己.
post(Runnbale) 直接调用runnable中的run,可以操作UI。
postDelayed(Runnable,long) 延迟第二个参数(毫秒)执行。
removeCallbacks(myRunnable) 移除handler的回调方法。
sendMessage(Message) 把message发送给handler,重写handler的handleMessage(Message)方法。
private Handler handler = new Handler(new Handler.Callback() {
//这里构造方法里填一个callback,用来截获message。
//假如这个callback里的handleMessage return true 下面的将不会执行,反之则执行。
//一般用于截获符合它条件的message
@Override,
public boolean handleMessage(Message message) {
Toast.makeText(getApplicationContext(),""+1,Toast.LENGTH_SHORT).show();
return true;
}
}){
@Override
public void handleMessage(Message msg) {
Toast.makeText(getApplicationContext(),""+2,Toast.LENGTH_SHORT).show();
}
};
为什么要设计只能通过handler机制来更新UI
最根本的目的就是解决多线程并发的问题(),假设在一个activity中,有多个线程去更新UI,并且都没有加锁机制,就会导致更新界面错乱.
如果对更新UI的操作都进行加锁处理又会怎么样? 会导致性能下降.
处于以上的考虑,android给我们提供一套更新UI的机制,而不用过多考虑并发的问题.所有的更新UI操作,都在主线程的消息队列中去处理.
handler的原理是什么?
Handler封装了消息的发送(主要是包括消息发送给谁,target) //发送给自己???
Looper内部包含一个消息队列(MessageQueue),所有的handler发送的消息都走向这个消息队列.
Looper.loop 方法,就是一个死循环,不断的从MessageQueue取消息,有消息就处理消息,没有消息就阻塞.
MessageQueue就是一个消息队列,可以添加消息,并处理消息.
Handler内部会跟Looper进行关联,也就是在Handler内部可以找到Looper,也就找到了MessageQueue,在Handler中发送消息,其实就是向MessageQueue队列中发送消息.
总结:Handler负责发送消息,Looper负责接收Handler发送的消息,并直接把消息回传给handler自己,MessageQueue就是一个存储消息的容器.
handler解析
在构造方法中
通过 mLooper = Looper.myLooper();
拿到当前线程的Looper对象.
public static @Nullable Looper myLooper() {
//UI线程在创建的时候会set一个Looper对象进去.
return sThreadLocal.get(); //取当前线程的Looper对象.
}
拿Looper对象是为了拿它的MessageQueue,即mQueue = mLooper.mQueue;方便发Message给MessageQueue.
handler处理消息的方法 post postAtTime postDelayed sendMessage sendEmptyMessage sendEmptyMessageDelayed sendEmptyMessageAtTime sendMessageDelayed 基本都是return另一个方法. 追踪到最后都是调用sendMessageAtTime方法.函数间转化,会经常用到getPostMessage
把post方法里面的runnable参数转成message
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r; //回调方法设置为r
return m;
}
sendMessageAtTime的实现 取出消息队列,为空抛异常,不为空就调用enqueueMessage(queue, msg, uptimeMillis);
enqueueMessage的实现
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);//把消息放到消息队列中去
}
消息队列一直处于loop状态,for死循环,从队列中取消息,没消息就会退出.有消息就执行msg.target.dispatchMessage(msg);target默认是handler自己,
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
// handler的obtainMessage()方法,调用message.obtain()方法,并把message的target设置为此handler,其它重载的大同小异
// message最好是调用Message.obtain().防止资源浪费,减少内存的开销.
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
**/
//threadlocal用于在线程中保存变量,set get
//message的what变量 方便接收者辨识这消息是关于什么的.
//默认应用程序通过ActivityThread进行创建,并回调各种方法,它默认有个线程:main. 默认又有一个Looper,Looper中又会有一个MessageQueue.
自定义与线程相关的handler
class MyThread extends Thread{
public Handler handler;
@Override
public void run() {
Looper.prepare();
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
Log.e("current thread:",Thread.currentThread().getName());
}
};
Looper.loop();
}
}
private MyThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
TextView textView = (TextView) findViewById(R.id.text1);
textView.setText("hello handler");
thread = new MyThread();
thread.start();
try {
//Log.e("current thread:",Thread.currentThread().getName());
Thread.sleep(500);
//Log.e("current thread:",Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.handler.sendEmptyMessage(1);
}
HandlerThread的使用
多线程并发的时候,用到的looper对象可能没有创建,则会抛出空指针异常.使用handlerThread时候,调用getLooper方法会等待至Looper创建,不会出现Looper异常的现象.
主线程和子线程之间的通信
在主线程和子线程各定义一个handler,主线程可以通过子线程的handler向子线程发消息,同样子线程可以通过主线程的handler向主线程发消息.
几种更新UI的方法
主线程定义handler对象,子线程利用handler对象向主线程发送消息,如果多个类直接相互调用,不是非常方便,需要传递handler对象.(或者接口)
handler.post(Runnable);
handler.sendMessage();
activity的runOnUiThread(Runnable),需要传递activity对象.
runOnUiThread(Runnable action); 具体如下:
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
view的post方法,需要传递view的对象过去.
view.post(runnable)
attachInfo用来判断view的所属窗口.
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }
- Asynctask.
适用的场景
如果是后台任务,像是下载任务等,就需要使用AsyncTask。
如果需要传递状态值等信息,像是蓝牙编程中的socket连接,就需要利用状态值来提示连接状态以及做相应的处理,就需要使用Handler + Thread的方式;(短信验证码提取)
需要另开线程处理数据以免阻塞UI线程,像是IO操作或者是循环,可以使用Activity.runOnUiThread();
如果只是单纯的想要更新UI而不涉及到多线程的话,使用View.post()就可以了;
非UI线程真的不能更新UI吗?
在oncreate方法里new一个Thread在它的run里面直接更新UI有时候是可以的,但是先让线程休眠2秒后就会报错. 更新UI时候会调用invalidate方法,里面有个ViewParent.invalidateChild(this, damage),判断是否是UI线程. viewRootImp初始化,在activity的onresume方法里.
handler使用时候的问题
在子线程中创建handler,没有关联looper,调用Looper.prepare(),抛出
Can’t create handler inside thread that has not called Looper.prepare()
这个异常.默认创建子线程是没有looper的.
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));
}