Handler使用详解

Handler的作用

我们通常总是说Android的UI线程不能处理超过5s的耗时任务,也不能处理网络操作。因为为了性能,设计的时候并没有考虑线程安全(就是多线程操作可能同时更改同一数据从而让结果混乱)。所以限定规则,只能在主线程操作UI组件。那么我们平时开发肯定会涉及后台任务修改UI数据的,那么Handler的消息传递机制就是用其他线程处理事务逻辑,通知主线程修改UI操作的中间人。

Handler、Message、Loop、MessageQueue概念

Message、Loop、MessageQueue这三个就像是Handler的小弟一样,关系要好,缺一不可。

  • Handler:其他线程中发送消息、主线程中获取处理消息。
  • Message:Handler发送或处理的具体消息对象。
  • Looper:每个线程只能有一个Looper,不断从MessageQueue读取消息传递到Handler。
  • MessageQueue:消息队列,先进先出的方式管理Message、Looper创建时会绑定与之关联的MessageQueue

四者的关系缺一不可

Handler想要正常工作,需要MessageQueue管理Message,而MessageQueue是由Looper管理的,所以Looper也是必要的。因此他们四个是相辅相成,缺一不可的关系。UI线程系统已经初始化过默认Looper所以我们平常使用未指定Looper也用了Handler是这个原因。但我们如果在自己的线程中就要指定Looper对象了。需压注意的是使用Handler前要先调用prepare()方法实例化Looper。

MessageQueue对象由Looper()方法构建:

private Looper(){
    mQueue = new MessageQueue();
    mRun = true;
    mThread = Thread.currentThread();
}

创建Looper使用它的prepare()方法即可,一个线程只能有一个Looper存在:

public static final void prepare(){
    if(sThreadLocal.get() != null){
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper());
}

然后调用Looper.loop()方法运行消息队列。loop()方法执行一个死循环来处理消息队列,如果没有消息进入阻塞状态。

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
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

            // 使用final保证分发消息过程中,线程标识符不会被修改
            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();
        }

所以我们在子线程中使用Handler处理事务时,只要注意Looper的实例化和Message的处理即可。

Handler实例

自定义线程处理Handler操作:

  1. 调用Looper.loop()实例化looper对象,looper构造时自动创建MessageQueue对象
  2. 使用Handler实例化,覆写handleMessage(Message msg)处理自己的事务
  3. 调用Looper.loop()方法启动消息循环机制

MainActivity.java

public class MainActivity extends AppCompatActivity {
    private Button mButton;

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

        //启动线程监听handler消息
        final MyThread myThread = new MyThread();
        myThread.start();

        mButton = (Button) findViewById(R.id.btn_click);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送空消息what类型为1,这个无法传递数据,捕获到1既可作相关操作
                //myThread.mHandler.sendEmptyMessage(1);

                //或者发送Message类型的消息,根据需求选择即可Message可携带的数据类型更丰富
                Message message = new Message();
                message.what = 1;
                Bundle bundle = new Bundle();
                bundle.putString("test","hello");
                message.setData(bundle);
                myThread.mHandler.sendMessage(message);
            }
        });

    }

    //自定义线程处理Handler信息
    class MyThread extends Thread{
        private Handler mHandler;
        @Override
        public void run() {
            //在handler之前调用,保证Looper对象创建好,因为子线程是不会自动帮我们创建Looper对象的
            Looper.prepare();
            mHandler = new Handler(){
                @Override
                public void handleMessage(Message msg) {
                    //获取到消息的what类型
                    if (msg.what == 1){

                        //Message 携带消息获取
                        Bundle result = msg.getData();
                        String s = result.getString("test");
                        Toast.makeText(MainActivity.this,s,Toast.LENGTH_SHORT).show();
                    }
                }
            };
            //循环处理开始
            Looper.loop();
        }
    }
}

布局文件也只是一个按钮,不贴了,运行后点击按钮就可以toast显示出hello字样。
这样处理逻辑我们新开了线程,在里边添加Thread.sleep(8888);模拟耗时操作也不会阻塞主线程引发ANR了。

Handler常用的方法:

  • hasMessages(int what) :检查队列中是否含what属性值的消息
  • hasMessages(int what, Object object):检查队列是否含指定What属性值且object指定对象的消息
  • obtainMessage() :获取消息
  • sendEmptyMessage(int what) :发送只含what属性的空消息
  • sendMessage(Message msg) :立即发送消息
  • sendMessageDelayed(Message msg, long delayMillis):指定多少毫秒后发送消息
  • handleMessage(Message msg) :处理收到的消息,通常覆写该方法实现自己的需求
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值