Handler消息机制

前言

你只有努力奔跑,才能留在原地。

handler基本使用方法

入口Activity代码如下:

public class HandlerDemoActivity extends Activity {
private TextView mTv;
private Thread mThread;

private Handler mHander = new Handler(new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        // 处理接收的消息
        if (msg.what == 1) {
            String content = msg.getData().getString("content");
            Logger.d(content);
            mTv.setText(content);

        }
        return false;
    }
});

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

    mTv = findViewById(R.id.tv_test_handler);
    mThread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                for (int i = 0; i < 100; i++) {
                    // 模拟处理数据
                    Thread.sleep(1000);

                    // 处理完数据更新ui界面
                    Message message = Message.obtain();
                    message.what = 1;
                    Bundle bundle = new Bundle();
                    bundle.putString("content", "我是处理后的内容");
                    message.setData(bundle);
                    mHander.sendMessage(message);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
    mThread.start();
}

@Override
protected void onDestroy() {
    super.onDestroy();
    mThread.interrupt();
    mHander.removeCallbacksAndMessages(null); // 防止内存泄漏
    }
}

源码解析

Handler MessageQueue Looper

发送消息的方法最终走的下面两个方法其中一个。我们以第一个方法进行讲解:
1. 由2行,mQueue对象是哪里来的?
创建Handler对象通过mQueue = mLooper.mQueue得到的;
2.mLooper对象是哪里来的?
根据上面的例子,我们知道handler是在ui线程创建,系统默认在ui线程通过ThreadLocal对象保存了一个looper实例;而looper中的mQueue对象是创建Looper的时候生成的。
得到了当前线程的mQueue对象后,继而调用enqueueMessage(queue, msg, uptimeMillis)方法,由97行将handler对象封装在message对象中,接着调用消息队列mQueue的enqueueMessage(Message msg, long when)方法;

  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);
    }

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

由上,发送消息到消息队列,mQueue的enqueueMessage(Message msg, long when)方法以单链表数据结构保存消息;

boolean enqueueMessage(Message msg, long when) {
        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(TAG, 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.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                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;
    }

处理(本实例是ui线程)Looper.loop循环取出的消息,由14-19行,for无限循环,由上知道queue.next()方法出消息队列取出消息,当消息队列为空时才退出循环,这里可能发生线程堵塞;由21行,通过msg.target.dispatchMessage(msg)处理消息;msg.target对象是Handler,在handler调用消息队列的enqueueMessage(Message msg, long when)方法前,由将handler自身封装到msg,在前几个步骤有提及;


    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;
            }
                ...

                msg.target.dispatchMessage(msg);
                .
                .
                .
    }



> 我们回到handler,看看dispatchMessage(Message msg) 方法,由36-38行,如果条件成立,handleCallback(msg)方法最终会执行runnable.run()方法;由39-43行,如果条件成立,会执行用户创建handler对象时传入的Callback对象的handlerMessage(msg)接口回调;由44行,用户通过48-562种方式创建handler并实现了handleMessage(msg),那就会执行handler实例的handleMessage(msg)方法;

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {  // msg.callback 是post method 传入的回调
            handleCallback(msg);     // 该方法执行message.callback.run(); 
        } else {
            if (mCallback != null) {     //public Handler(Callback callback, boolean async) 构造Handler的时候  mCallback = callback;  
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

    public Handler(boolean async) {
        this(null, async);
    }

    public Handler() {
        this(null, false);
    }

ThreadLocal

ThreadLocal一般用来保存每个Thread特有的对象或者属性;
Looper对象就是保存在ThreadLocal里面的,通过ThreadLocal.set() save
Looper对象,ThreadLocal.get() get Looper对象; 线程中只能有不超过一个Looper对象,并且Thread
跟Looper对象一对一的关系;这样用ThreadLocal来存取Looper,
就不用一个类型LooperManager类来管理所有Thread的Looper对象了。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

验证一下ThreadLocal.get(), ThreadLocal.set()
方法,以及ThreadLocal在多个Thread中是怎么保持自我的。

 private static void testThreadLocal() {
        final ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
        new Thread() {
            @Override
            public void run() {
                super.run();
                threadLocal.set(100);
                System.out.println("Thread name: " + Thread.currentThread() + " threadLocal value: " + threadLocal.get());
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                super.run();
                threadLocal.set(200);
                System.out.println("Thread name: " + Thread.currentThread() + " threadLocal value: " + threadLocal.get());
            }
        }.start();

        System.out.println("Thread name: " + Thread.currentThread() + " threadLocal value: " + threadLocal.get());
    }

log:

Thread name: Thread[Thread-0,5,main] threadLocal value: 100
Thread name: Thread[Thread-1,5,main] threadLocal value: 200
Thread name: Thread[main,5,main] threadLocal value: null

五. 使用中可能会遇到的问题

  1. 没有处理好内存泄漏,导致内存溢出;
  2. 在非主线程(UI线程)使用创建Handler,要先通过Looper.prepare()创建属于本线程的looper实例,否则会报 Can’t create handler inside thread that has not called Looper.prepare()异常;
  3. 再补充…

实例下载地址

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值