Android的handler详解

来源:互联网 发布:access 导入sqlserver 编辑:程序博客网 时间:2024/05/21 17:00

handler

handler是什么?

handler是android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。

handler两个主要用途:

  1. 安排消息或runnable对象在未来某个时间点执行。

  2. 安排动作在不同于自己的线程执行。

为什么要去使用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的原理是什么?

  1. Handler封装了消息的发送(主要是包括消息发送给谁,target) //发送给自己???

  2. Looper内部包含一个消息队列(MessageQueue),所有的handler发送的消息都走向这个消息队列.

  3. Looper.loop 方法,就是一个死循环,不断的从MessageQueue取消息,有消息就处理消息,没有消息就阻塞.

  4. MessageQueue就是一个消息队列,可以添加消息,并处理消息.

  5. Handler内部会跟Looper进行关联,也就是在Handler内部可以找到Looper,也就找到了MessageQueue,在Handler中发送消息,其实就是向MessageQueue队列中发送消息.

  6. 总结: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的方法

  1. 主线程定义handler对象,子线程利用handler对象向主线程发送消息,如果多个类直接相互调用,不是非常方便,需要传递handler对象.(或者接口)

    handler.post(Runnable);

    handler.sendMessage();

  2. activity的runOnUiThread(Runnable),需要传递activity对象.

    runOnUiThread(Runnable action); 具体如下:

    public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) { mHandler.post(action);} else { action.run();}}
  3. 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 laterViewRootImpl.getRunQueue().post(action);return true;}
  4. Asynctask.

适用的场景

  1. 如果是后台任务,像是下载任务等,就需要使用AsyncTask。

  2. 如果需要传递状态值等信息,像是蓝牙编程中的socket连接,就需要利用状态值来提示连接状态以及做相应的处理,就需要使用Handler + Thread的方式;(短信验证码提取)

  3. 需要另开线程处理数据以免阻塞UI线程,像是IO操作或者是循环,可以使用Activity.runOnUiThread();

  4. 如果只是单纯的想要更新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));      }
0 0
原创粉丝点击