Android Handler消息机制

一、什么是Handler?

Handler简单来说是消息处理机制。具体点是用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具。每一个Handler实例之后会关联一个线程和该线程的消息队列。当你创建一个Handler的时候,从这时开始,它就会自动关联到所在的线程/消息队列,然后它就会陆续把Message/Runnalbe分发到消息队列,并在它们出队的时候处理调掉。

二、为什么要有Handler?

众所周知Android的主线程不能处理耗时的任务,子线程不能用来更新UI操作。那这样的话,我们就必须在子线程中处理耗时的任务,然后在主线程中更新UI。但是我们怎么知道子线程中的任务何时完成,又该什么时候更新UI呢?为了解决这个问题,Android为我们提供了一个消息机制Handler。根源目的是为了UI操作是线程安全的。

三、Handler的基本使用?

Handler的作用:

将工作线程需操作UI的消息 传递 到主线程,使得主线程可根据工作线程的需求 更新UI,从而避免线程操作不安全的问题。

eg:使用Thread创建一个子线程,让线程睡一秒来模仿耗时的任务,当耗时的任务处理处理完之后,Handler会发送一个消息。然后在Handler的HandleMessage方法中得到这个消息,去更新UI。

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mBtn;
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if (msg.what == 1) {
                Toast.makeText(HandlerActivity.this, "更新UI", Toast.LENGTH_SHORT).show();
            }
            return true;
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        initView();


    }

    private void initView() {
        mBtn = findViewById(R.id.btn_handler);
        mBtn.setOnClickListener(this);
    }


   @Override
    public void onClick(View v) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //在子线程中模拟耗时操作,由Handler发送消息至主线程去更新UI
                try {
                    Thread.sleep(1000);
                    Message message=new Message();
                    message.what=1;
                    mHandler.sendMessage(message);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

这里有两个疑问①Handler在子线程中发的消息怎么会跑到主线程中了呢?

                         ②handleMessage又是怎么接收到消息的呢?

在子线程中Handler在发送消息的时候已经把自己与当前的message进行了绑定,在通过Looper.loop()开启轮询message的时候,当获得message的时候会调用 与之绑定的Handler的**handleMessage(Message msg)**方法,由于Handler是在主线程创建的,所以自然就由子线程切换到了主线程。

Handler.post()的使用:

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {
    private Handler handler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        initView();
    }

    private void initView() {
        handler=new Handler();
        new Thread(){
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(HandlerActivity.this,"执行线程UI操作!!!",Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }.start();
    }
}


四、Handler的工作原理?

在使用Handler之前必须要调用Looper.prepare()这句代码,这句代码的作用是将Looper与当前的线程进行绑定,在实例化Handler的时候,通过Looper.myLooper()获取Looper,然后再获得Looper中的MessageQueue。在子线程中调用Handler的sendMessage方法就是将Message放入MessageQueue中,然后调用Looper.loop()方法来从MessageQueue中取出Message,在取到Message的时候,执行 **msg.target.dispatchMessage(msg);**这句代码,这句代码就是从当前的Message中取出Handler然后执行Handler的handleMessage方法。

工作原理图:


五、Handler、Message、MessageQueue、以及Looper之间的关系?

Handler:是Message的处理器,同时也负责消息的发送和移除工作。

                                                           即时消息                                                  延迟消息
             发送空消息:            sendEmptyMessage(int what)        sendEmptyMessageDelayed(int what,long millitime)
             发送非空消息:        sendMessage(message)                sendMessageDelayed(message,long millitime)
             

             处理消息 (谁发送谁处理)handleMessage()

             移除消息
            removeMessages(int what);移除指定id的所消息
            removeCallbacksAndMessages(null);移除所的消息

Message:可理解为线程间通讯的数据单元,可通过Message携带数据

MessageQueue:消息队列,用来存放通过Handler发送的消息,它是一个按Message的what排序的优先级队列。(what:代表被处理的时间)

Looper:循环器,负责循环取出MessageQueue里面的当前需要处理的Message

                           交给Handler进行处理

                            处理完之后,将Message缓存到消息池中,以备复用。

 

六、如何在子线程中使用Handler?

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {
    private Button mBtn;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        initView();

    }

    private void initView() {
        mBtn = findViewById(R.id.btn_handler);
        mBtn.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        new Thread(new Runnable() {

            @Override
            public void run() {
               //在子线程中模拟耗时操作,由Handler发送消息至主线程去更新UI
                Looper.prepare();

                handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        if (msg.what == 1) {
                            Toast.makeText(HandlerActivity.this, "更新UI了", Toast.LENGTH_SHORT).show();
                        }
                        return false;
                    }
                });

                Message message = new Message();
                message.what = 1;
                handler.sendMessage(message);

                Looper.loop();

            }
        }).start();
    }
}

七、简单从源码的角度分析Handler

  Handler的构造方法,源码中,handler的无参构造调用了有参构造

作用:初始化handler对象&绑定线程,也就是说handler需要绑定线程才能使用,绑定之后,handler的消息处理会在绑定的线程中执行。 怎么绑定呢?就是先指定Looper对象,从而绑定了Looper对象所绑定的线程(因为Looer对象已经绑定了对应的线程)

即:指定了handler对象的Looper对象=绑定到了Looper对象所在的线程。

接着绑定消息队列对象(MessageQueue)

mLooper = Looper.myLooper(); 获取Looper对象中保存的消息队列对象,这也保证了handler对象关联上Looper对象中的MessageQueue.

由此上边贴出的两段源码中可以看出:当创建Handler对象时,则通过 构造方法 自动关联当前线程的Looper对象 & 对应的消息队列对象(MessageQueue),从而 自动绑定了 实现创建Handler对象操作的线程。

那当前线程的looper对象和对应的消息队列对象(MessageQueue)是什么时候创建的呢?

https://i-blog.csdnimg.cn/blog_migrate/ee509d9a65c2f950e7b7411cdba5b8e8.webp?x-image-process=image/format,png

判断sThreadLocal是否为null,否则抛出异常。Looper.prepare()方法不能被调用两次=1个线程中只能对应1个Looper实例。

sThreadLocal:1个ThreadLocal对象,用于存储线程变量。
sThreadLocal.set(new Looper(quitAllowed));若为初次创建Looper对象,Looper对象存放在Thread线程中,线程又存放在ThreadLocal中。

Looper的构造方法:

分析:在Looper的构造方法中创建了1个消息队列。即当创建一个Looper实例的时候会自动创建一个与之配对的消息队列对象。

作用就是为主线程创建1个循环器对象(Looper),同时也生成了1个消息对象(MessageQueue)该方法在主线程(UI线程)。

该方法在主线程(UI线程)创建时自动调用,即主线程的Looper对象自动生成,不需手动生成。

为什么我们平常在使用Handler的时候,不需要手动创建Looper对像呢?

Android应用在进程启动时,会默认创建1个主线程(AtivityThread,也叫UI线程),创建时,会自动调用ActivityThread的1个静态main()方法,它的内部会调用Looper.prepareMainLooper()为主线程生成1个Looper对象。

  • 创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象
  1. 即 主线程的Looper对象自动生成,不需手动生成;而子线程的Looper对象则需手动通过Looper.prepare()创建
  2. 在子线程若不手动创建Looper对象 则无法生成Handler对象

Looper和MessageQueue对象生成之后,则会自动进入消息循环,Looper.loop()


public static void loop() {
        final Looper me = myLooper(); //获取当前Looper的消息队列
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
//myLooper:返回threadLocal存储的Looper实例,
        final MessageQueue queue = me.mQueue; //获取Looper实例中的消息队列对象(MessageQueue)

        // 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(); //从消息队列中取出消息
            if (msg == null) {  //若取出的消息为空,则线程堵塞
                // No message indicates that the message queue is quitting.
                return;
            }

          
            try {
                msg.target.dispatchMessage(msg); //派发消息对应的Handler
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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(); //释放消息占据的资源
        }
    }

分发消息,

若msg.callback属性不为空,则代表使用了post(Runnable r)发送的消息,则执行handleCallBack(msg),回调Runnable对象里复写的run方法。若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handlerMessage(msg).

消息对象:Message

获取消息,Message msg=Message.obtain();

   /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
       用于创建消息对象,可用new或Message.obtain()
     */
    public static Message obtain() {
//Message内部维护了一个消息池,用于存储消息对象以备复用
        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(); //若消息池中没有消息,则还是使用关键字new创建的
    }

通过handler是怎样发送消息到消息队列中的?

handler.sendMessage(message);分析,无论是sendMessage还是sendMessageDelayed()最终都会调用sendMessageAtTime()
  public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        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) {
        msg.target = this; //把当前的handler实例对象最为msg的target属性
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
//queue.enqueueMessage()入队,根据时间将消息放入队列中,采用的是单链表,提高了插入和删除的效率。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值