Handler机制解析

609 阅读5分钟

handler机制:其实就是Android消息传递机制。概括起来就是通过handler的sendMessage或者postDelayed方法将消息发送到messageQueue中,然后通过lLooper.loop()方法从中取出message进行处理。下面具体说说 从handler的使用说起:初始化阶段

private static class TestHandler extends Handler {
    private final WeakReference<TestFragment> fragmentWeakReference;

    private TestHandler(TestFragment fragment) {
        fragmentWeakReference = new WeakReference<>(fragment);
    }

    @Override
    public void handleMessage(Message msg) {
        AnchorPerspectiveLiveRoomFragment fragment = fragmentWeakReference.get(); 
                super.handleMessage(msg);
        }
    }

}

mHandler = new TestHandler(this);

这里是在fragment中,所以采用弱引用fragment的方式定义(防内存泄漏),创建handler时候一般是Looper.prepare();new Handler();Looper.loop();这里之所以没有写prepare和loop,是因为我这里是在主线程中创建的handler,而且要往主线程的looper中发送消息,主线程的这两个方法在ActivityThread类中的main函数中已经调用了。接下来是发送消息,之所以是采用obtainMessage的方式创建消息,主要是为了利用系统自动的创建和回收这个message,发送则直接send。

Message msg = mHandler.obtainMessage();
msg.obj = "test";
mHandler.sendMessage(msg);

上面基本的使用,接下来我们看看到底干了什么,首先从Looper.prepare()开始

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

在这个方法中创建了一个looper,并把它保存在了threadLocal中。这里的threadLocal其实就是一个类,这个类保存了looper的相关信息。紧接着是looper的具体创建

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

就是创建了一个messageQueue并获取当前线程。然后是Handler创建,这里的new Handler,虽然没有传递参数,但是它重写了handleMessage方法,这个放到后面讲(如果采用handler.post(runnable)的话其实是创建了一个有callback的handler(它的回调就是runnable),那么它源码如下

public Handler(Callback callback, boolean async) {
  ...

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

就是prepare中创建的那个looper,而mQueue也是刚才创建looper时新建的messageQueue,callback则是传进来的。 重点就这么几行,为looper赋值:它的具体实现则是

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

接着就是发送消息,他最终会调用Handler.java中下面的方法

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

这里的enqueueMessage其实就是向消息队里里插入一条message,具体实现则是修改链表的指针。由于已经调用了looper.loop方法,所以我们看看这里做了什么

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;	
  ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        try {
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
    
    }
}

重点就这么几行,在这个for循环中不断的取消息,然后通过dispatchMessage(handler中的)方法将消息发送到相应回调中。之所以是handler中的方法,是因为Message中的变量

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

可以看出,如果没有callback,也就是那个runnable,那么就调用重写的handleMessage方法。到这里基本上是完事了。但是细心的可能会发现,在loop方法中有个死循环(for(;;)),他不会造成ANR吗,答案是不会的。原因在于

  • ANR的定义:当前事件没有得到机会处理(有可能是因为主线程正常处理其他的事件,而不能及时处理当前的事件)。 当前事件正在处理了,但是没有及时处理完成。

  • Android中所有事件都是通过loop来传递的,比如生命周期的回调。所以在for循环中取出消息来执行,如果卡顿了,可能会造成ANR,从而阻塞了loop的循环,而不是loop的循环阻塞了事件的处理。换句话说,通过定义可以看出,要想产生ANR首先得拿到处理的消息,无限循环中拿不到消息,肯定也就不会产生ANR

可以用如下的图来大致的表示一下流程

接下来是几个易混淆的概念吧:

ThreadLocal:线程本地变量,ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。它里面比较重要的方法就是set,get,比如下面的ThreadLocal.set

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

就是向map中插入数据

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

这里需要说明的ThreadLocalMap,其实他是一个定制化的hashmap,只是用来存储线程的本地变量值,key是ThreadLocal,值为Object,这里为looper。

HandlerThread:它是继承自Thread的类,本质上是一个线程,只不过它内部封装了一个handler,并且在run方法中主动调用了prepare和loop方法

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

所以使用场景就是:如果需要在子线程中使用handler就直接用。也就是子线程与主线程通信。避免多线程并发导致空指针问题,利用它获取looper对象,下面是一个简单的小例子

/**
 * 子线程与主线程进行信息交互步骤
 * 1.创建主线程handler
 * 2.创建handlerThread获取looper对象
 * 3.利用looper创建子线程handler
 * 4.在主线程中利用子线程的handler向子线程发送消息
 * 5.在子线程中利用主线程的handler向主线程发送消息
 */
public class Main2Activity extends AppCompatActivity implements View.OnClickListener {
 
    private Button mBtn1,mBtn2;
 
    //主线程handler
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Message message = new Message();
            System.out.println("main Handler");
            //给子线程发送消息
            threadHandler.sendMessageDelayed(message,1000);
        }
    };
    //子线程handler
    private Handler threadHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
 
        HandlerThread thread = new HandlerThread("handlerThread");
        thread.start();
        threadHandler = new Handler(thread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                Message message = new Message();
                System.out.println("thread Handler");
                //给主线程发送消息
                handler.sendMessageDelayed(message,1000);
            }
        };
 
        mBtn1 = (Button) findViewById(R.id.button2);
        mBtn2 = (Button) findViewById(R.id.button3);
        mBtn1.setOnClickListener(this);
        mBtn2.setOnClickListener(this);
 
    }
 
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button1:
                handler.sendEmptyMessage(1);
                break;
            case R.id.button2:
                handler.removeMessages(1);
                break;
            default:
                break;
        }
 
    }
}