Android_Handler机制

Posted 杨迈1949

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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_Handler机制的主要内容,如果未能解决你的问题,请参考以下文章

android_handler

Android--多线程之Handler

一个场合, 事件捕获派上了用处

注意力机制+ReLU激活函数=自适应参数化ReLU

python进阶(28)import导入机制原理

从零开始理解JAVA事件处理机制