Handler消息机制

Handler消息机制

@(Android)

Handler消息机制是Android系统提供的一种用于解决线程间通信的方式。Handler主要作用,分发和处理消息,实现将一个任务切换到指定的线程中执行

为什么使用Handler?

子线程是无法访问UI的。如果允许子线程访问UI且存在并发的情况,会使得控件处于混乱的状态。如果采用加锁来解决并发问题,又会使UI访问的效率降低。因此Android采用单线程模型操作UI,通过handler切换到UI线程,解决子线程无法访问UI的问题。

理解handler,Message,MessageQueue及Looper四者关系,是搞懂Android消息机制的关键。

Message

Message是消息机制中的媒介,包含了描述和任意数据对象,用于发送给handler。handler.postXXX(Runnable r)底层也是将Runnable封装成一个Message,这个Runnable设为Message的callback属性。

public final boolean postDelayed(Runnable r, long delayMillis)
    {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

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

MessageQueue

MessageQueue是一个单链表,用于存放所有handler发送过来的消息。enqueueMessage( )往队列中插入一条消息,next( )是一个无限循环的方法,用于移除消息,如果队列为空,则阻塞。

Looper

Looper是给线程轮询消息的。线程默认是没有Looper的,需要调用Looper.prepare()创建looper,使用Looper.loop()开启消息循环,将next()获取的消息通msg.target.dispatchMessage(msg)回调给handler。如果线程中没有创建Looper,是无法使用handler的。由于Android程序在其入口函数ActivityThread$main(),调用了 Looper.prepareMainLooper(),因此我们可以在主线程中直接使用Handler。

一个线程中只能有一个MessageQueue和Looper,采用ThreadLocal进行保存

Handler

Looper把消息回调到handler的dispatchMessage中进行消息处理:

  • 若该消息有callback,即通过Post(Runnable)的方式投递消息,因为在投递runnable时,把runnable对象赋值给了message的callback。
  • 若handler的mCallback不为空,则交由通过callback创建handler方式去处理。
  • 否则,由最常见创建handler对象的方式,在重写handlerMessage中处理。

子线程中使用Handler

遵循三个步骤
- 创建Looper
- 调用Looper.loop()
- 创建Handler

public class ChildThreadHandlerActivity extends Activity {
    private MyThread childThread;

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

        childThread = new MyThread();
        childThread.start();

        Handler childHandler = new Handler(childThread.childLooper){//这样之后,childHandler和childLooper就关联起来了。
            public void handleMessage(Message msg) {

            };
        };
    }

    private class MyThread extends Thread{
        public Looper childLooper;

        @Override
        public void run() {
            Looper.prepare();//创建与当前线程相关的Looper
            childLooper = Looper.myLooper();//获取当前线程的Looper对象
            Looper.loop();//调用此方法,消息才会循环处理
        }
    }
}

创建子线程的handler, Handler childHandler = new Handler(childThread.childLooper)后,就可以使用这个childHandler发送消息到子线程中执行。上述代码中由于多线程,childThread.childLooper可能回报空指针错误。

Android提供HandlerThread,简化子线程使用Handler的代码。HandlerThread的run方法中创建Looper,并开启消息循环loop(),

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
 public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }

        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

getLooper()中使用wait,以及run()中使用nitifyAll(),解决getLooper()可能为空的问题。
HandlerThread应用示例

public class HandlerThreadActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        TextView textView = (TextView) findViewById(R.id.tv);
        textView.setText("HandlerThreadActivity.class");

        HandlerThread handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();

        Handler mHandler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
            }
        };

        Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
        mHandler.sendEmptyMessage(1);
    }
}

子线程如何切换到UI线程

创建主线程的handler,并发送消息

new Handler(getMainLooper()).post(new Runnable() {  
                   @Override  
                   public void run() {  
                       // 将在UI线程执行下句 
                       Toast.makeText(getApplicationContext(), "Loaded Person from broadcast-receiver->intent-service: " + info, Toast.LENGTH_LONG).show();  
                   }  
               }); 

Activity的runOnUiThread和View.postDelayed()内部也是使用handler切换到UI线程执行自己的Runnable。

public final void runOnUiThread(Runnable action) {
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
 public boolean postDelayed(Runnable action, long delayMillis) {
            final AttachInfo attachInfo = mAttachInfo;
            if (attachInfo != null) {
                return attachInfo.mHandler.postDelayed(action, delayMillis);
            }
            // Assume that post will succeed later
            ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
            return true;
        }

Handler要注意的坑

  1. 使用非静态内部类声明Handler,会造成内存泄漏,推荐静态内部类+弱引用。
  2. onDestory中移除队列中的消息,handler.removeCallbacksAndMessages(null)。

al-and-quick-reference

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值