Android_Handler机制

在安卓中,在不同的线程间通信,可是使用Handler来轻松实现,本文就详细的介绍Handler处理消息的原理。

1,Message

讲Handler机制,我们就不得不涉及到一个很重要的对象Message,Handler使用Message对象作为传输对象,所以Message对象中,可以携带一些对象,直接在另一个线程(handler对象所绑定的线程)中去处理。一个典型的案例就是我们在获取网络图片时必须单开线程,而后通过Handler发送一个Message通知UI线程去更新图片,这里就可以将得到的bitmap作为对象放在Message中,在UI线程中去得到这个bitmap,然后显示在ImageView控件上。

public class MainActivity extends Activity {

    String m_image_url = "https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superplus/img/logo_white_ee663702.png";

    private Handler m_handler;
    private ImageView m_imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        m_imageView = (ImageView) findViewById(R.id.imageview_temp);

        //在UI线程中new Handler()这样,处理消息时,也会在主线程中
        m_handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                if (msg.what == 0x11) {
                    //获得非UI线程传递过来的Bitmap
                    Bitmap bitmap = (Bitmap) msg.obj;
                    if (m_image_url != null) {
                        m_imageView.setImageBitmap(bitmap);
                    }
                }
                return true;//表示拦截消息事件,不会下发到handler的handerMessage()了
            }
        });
    }

    //按钮的触发事件
    public void click_fetch_image(View view) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
// 请求网络,注意添加网络权限
                    URL url = new URL(m_image_url);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setDoInput(true);
                    connection.connect();
                    InputStream inputStream = connection.getInputStream();
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    if (bitmap != null) {
// 得到一个Message对象的方法,官方推荐1,2方法
// 1 new Message();2 Message.obtain();3 handler.obtainMessage();
// Message message = Message.obtain();
                        Message message = m_handler.obtainMessage();
// 设置message传递的信息
// 2个int类型 message.arg1 .arg2 一个obj类型 message.obj
                        message.what = 0x11;
                        message.obj = bitmap;
// 发送消息给指定的handler
// 1 通过Message自己发送
                        message.setTarget(m_handler);
                        message.sendToTarget();
// 2 通过Handler发送
// m_handler.sendMessage(message);
                        Log.d("", "子线程消息发送成功!");
                    }else {
                        Log.d("", "网络图片返回为null");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

代码中已经讲得很清楚怎么去获取一个Message对象,如果给利用message传递参数,如何将一个通过handler发送message.当然messge还有一些其他知识点,这里就先不讲。

2,Looper

第二个重要的类,当然是Looper了。
Looper其实就是一个在线程的处理MessageQueue的一个类,注意这里的线程是任意线程,不要被UI线程中存在looper就以为它只能在UI线程中存在,只是当运行应用时,系统会默认的在UI线程中去创建Looper而已。一个线程中只能有一个looper去处理消息(其实是分发消息给handler去处理)。想要在子线程中使用handler机制,就必须在子线程中创建looper对象。
一个典型的实例如下:

class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }

跟进Looper源码中看到Lopper.prapare()最终调用了

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

它的私有构造方法,去创建一个MessageQueue,并绑定当前线程,此时我们线程中就有了looper对象了(或许应该反过来)。

看下looper.loop()做了哪些事情:

    /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

在第6行,得到当前线程的looper
第10行,得到消息队列,
从第17行开始是一个执行死循环,
18行从消息队列里获得消息,
31行调用handler(message.target就是message绑定的handler)的dispatchMessage()方法,对msg处理message消息

3,Handler

终于言归正传了,因为一般我们都是在UI线程中使用Handler所以这里只讲UI线程中的Handler的用法。

创建一个Handler

先创建一个Handler,我们一般声明的时候就创建,或者在Activity的onCreate()中创建:

Handler handler = new Handler();//这个handler就不具有处理消息的能力
Handler handler= new Handler(new Handler.Callback() {//这个handler可以处理消息,callback方式
            @Override
            public boolean handleMessage(Message msg) {
                //use msg code
                return true;
            }
        });
Handler handler= new Handler(){//这个handler可以处理消息,匿名内部类方式
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };

Handler的两个最大的用处就是1,在另一个线程中发送message,或者runnable将他们入队(MessageQueue);2,处理message或者runnable。

使用handler发送消息

1,Message方式
前面讲Message时,讲过了,就是调用m_handler.sendMessage()方法发送消息
2,Runnable方式
如果不需要传递数据给另一个线程,只需要通知handler线程执行某个方法,比如切换一个Button的状态,这时候,就应该用Runnable方式:

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                //handler所在线程执行的代码
                //code
            }
        });

前面去面试一家公司,面试官问我:handler发送消息的Message和Runnable方式有什么区别?苦逼的我当时没研究过handler源码,没答上来。
现在分析一下,就应该去看一下这个post()方法怎么去处理这个Runnable的,先看一下handler的post()方法

    public final boolean post(Runnable r)
    {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

看到这个方法名,就有点懂了,其实还是去发送了个Message消息,因为这个sendMessageDelayed()的第一个参数是一个Message对象,下面跟进getPostMessage()

    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

看到这里就明白了,当使用Runnable形式发送消息时,也是发送一个Message对象,只是记录在了message.callback中。

使用Handler处理消息

在处理之前,先看一下,handler把是如何把消息发送出去,然后消息又回到handler中去处理的

    Class Handler

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        /** mQueue为Looper中的MessageQueue */
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    /** 设置当前消息的handler对象 */
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        /** messageQueue将msg加入队列 */
        return queue.enqueueMessage(msg, uptimeMillis);
    }
Class MessageQueue

boolean enqueueMessage(Message msg, long when) {
        /** 没有找到发送者(handler) */
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w("MessageQueue", e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                /** 当前没有消息处理,将msg加入队列 */
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue. Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

通过分析Handler和MessageQueue的源码,可以发现,Handler发送一条消息之后,是加入到Looper的MessageQueue中的,这样Looper.loop()就能实时的从MessageQueue中去获得消息了,通过调用msg.target.dispatchMessage(msg);将当前的msg消息发送给原来的发送者target来处理。
现在message又回到了Handler中,分析一下handler.dispatchMessage():

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
        /** 表明使用Runnable方式发送 */
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    /** 如果存在callback拦截消息 ,并返回true */
                    return;
                }
            }
            /** 这就是我们常重写的handlerMessage() */
            handleMessage(msg);
        }
    }

到这里,就恍然大悟了,msg已经到我们的handlerMessage()碗里来了。不过还是要看一下handlerCallBack()是怎么处理Runnable的

    private static void handleCallback(Message message) {
        message.callback.run();
    }

很简单的一行代码,就是去跑一个run()方法,而不是start()。
这里分析一下,线程中绑定的是Looper对象,也就是说当前线程一直有一个looper在轮询消息,当looper轮询到一条消息时,就通知handler去处理,注意这是在同一个线程中,也就是looper所在的线程中。handler如果去调用一个实现runnable接口的start()方法岂不是又新开启了一个线程了,所以这里直接调用了run()方法,在当前线程就执行就行了。

4,使用Handler完成定时任务

Handler除了上面提到的基本用法外,还有一个常用功能就是在UI线程执行一个定时的任务,比如让一个TextView自增长。

    private Handler m_handler;

    public void click_start(View view) {
        m_handler = new Handler();
        //发送一个runnable,延迟一秒执行
        m_handler.postDelayed(m_runnable, 1000);
    }

    public void click_cancel(View view){
        //从消息队列中移除runnable
        m_handler.removeCallbacks(m_runnable);
    }

    Runnable m_runnable = new Runnable() {
        int i = 1;
        @Override
        public void run() {
            m_increment_textview.setText("" + i++);
            //在run()中又去发送一个runnable,就是一个循环调用的过程
            m_handler.postDelayed(this, 1000);
        }
    };

你可能感兴趣的:(android)