(十一)Handle 源码分析以及手写实现

版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。

一、源码分析

1.消息入队

我们比较常用的 Handle 的方法是 sendMessage 这个方法,查看其源码。

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendMessage 直接调用了 sendMessageDelayed 这个方法,继续查看 sendMessageDelayed 方法源码。

    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

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

sendMessageAtTime 里面调用了 enqueueMessage 方法。

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

enqueueMessage 中调用了成员变量的 MessageQueue 的 enqueueMessage 方法,在这里实现了消息入队

2.消息出队

在 Handle 的成员变量里有一个 Looper (轮询器),Looper 里面主要的方法是 loop,

Looper 的 loop:

    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
            
            ...
            
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            ...
        }
    }

在里面使用了一个死循环进行轮询,不停的调用 queue 的 next 方法获取下一个,根据注解这个方法可能被阻塞,然后调用了 msg.target.dispatchMessage(msg), msg.target 就是我们创建的那个 Handle,即又调用了 Handle 的 dispatchMessage 方法。

Handle 的 dispatchMessage :

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

创建 Handle 的时候是可以传递一个回调,如果这个回调存在的话则调用该回调方法,正常情况下我们是没有进行设置回调,所以调用到了 handleMessage 这个方法。

Handle 的 handleMessage:

    public void handleMessage(Message msg) {
    }

Handle 的 handleMessage 是一个空实现,所以我们使用 Handle 的时候,需要对这个方法进行重写。

3.大体流程

这里写图片描述

Handler 发送消息,保存到 MessageQueue,通过轮询器 Looper 不停的进行轮询,获取 MessageQueue 中的消息,传递给 Handler,调用 Handler 的 handleMessage 方法。

这是一个消息的机制,并不是说 Handler 这个消息机制是用来更新 UI,主要的作用是在主线程与子线程之间进行通信,经典例子就是在子线程中调用 UI 主线程,进而更新 UI 界面。

4.Handler 与 Looper、MessageQueue

每一个 Handler 下有一个 Looper mLooper 和 MessageQueue mQueue,这个是在 Handler 的构造函数中获取到的,即一创建 Handler 就有的。

Handler 构造函数:

    public Handler() {
        this(null, false);
    }
 
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

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

Looper 是通过 Looper.myLooper() 获取到的。

Looper 的 myLooper:


    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

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

sThreadLocal 是一个 ThreadLocal,ThreadLocal 是用来进行线程数据隔离,保证各个线程中 ThreadLocal.get() 获取到的是当前线程的对象。

在定义 sThreadLocal 上面有个备注,调用 ThreadLocal.get() 之前必须要先调用 prepare() 方法,否则将会返回 null。

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

sThreadLocal 的 set 是在 prepare 里面被调用的,即在这里完成 Looper 的初始化。

在 Looper 的 loop()方法中有使用到 Looper,所以 loop()方法肯定是在 prepare()之后调用

 public static void loop() {
        final Looper me = myLooper();
        ...
 }

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

loop()方法跟 prepare()方法的调用是在 ActivityThread (安卓程序的入口) 中的 main 函数被调用。

ActivityThread 的 main:

    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        ...
        Looper.loop();
    }

Looper 的 prepareMainLooper:

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

ActivityThread 的 main 先调用了 Looper 的 prepareMainLooper,在这里调用了 Looper 的 prepare 方法,然后再调用了 Looper 的 loop 方法。

注: 主线程创建的时候调用 Looper 的 prepare 方法,则创建了第一个 Looper,即安卓程序至少有一个 Looper。

Looper 有个 MessageQueue 的成员变量,这个在构造函数中初始化的,Handler 中的 MessageQueue 直接获取的是 Looper 中的,两个指的是同一个对象。

二、手写实现

根据上面的源码分析进行简单的手写实现 Handler 机制,这边不需要新建安卓项目,直接 java 项目即可。

1.Message

public class Message {

	public int what;
	public Object object;
	Handler target;
	
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return object.toString();
	}
}

Message 只存储三个数据,what、object 和 Handler,在安卓的 Handler 下 Message 还有进行复用机制的处理,可以一定程度上减小内存的损耗,优化性能。

2.Handler

public class Handler {

	//轮询器
    final Looper mLooper;
	//消息队列
    final MessageQueue mQueue;
	
	public Handler() {
		//获取当前线程的 Looper
		this.mLooper = Looper.myLooper();
		this.mQueue = mLooper.mQueue;		
	}
	
	/**
	 * 发送消息,压入队列
	 */
	public void sendMessage(Message msg) {
		msg.target = this;
		mQueue.enqueueMessage(msg);
	}

	public void dispatchMessage(Message msg) {
		handleMessage(msg);
	}
    public void handleMessage(Message msg) {
    }
}

Handler 拥有两个属性,Looper 和 MessageQueue,根据源代码的实现,Looper 和 MessageQueue 在构造函数的时候初始化,Looper 为当前线程的 Looper(这个会在 Looper 这个类体现),MessageQueue 获取的就是 Looper 下的 MessageQueue 。

同时,还提供了主要调用的方法 sendMessage,和 Looper 出栈后消息回调的方法 handleMessage。在入栈的时候指定消息所属的 Handler 为当前 Handler。

3.Looper

public final class Looper {

	//每一个线程都有一个 Looper
	//Looper 对象保存在 ThreadLocal 中,保证线程数据隔离
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    //一个 Looper 有一个消息队列
    final MessageQueue mQueue;
    
    private Looper() {
        mQueue = new MessageQueue();
    }
    
    public static void prepare(){
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper());
    }
    
    /**
     * 获取当前线程的 Looper
     * @return
     */
    public static Looper myLooper() {
        return sThreadLocal.get();
    }
    
    /**
     * 轮询消息队列
     */
    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;
        
        for (;;) {
        	 Message msg = queue.next(); // might block
             if (msg == null) {
                 continue;
             }

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

Looper 使用 ThreadLocal 进行 Looper 的线程数据隔离,拥有一个 MessageQueue 属性,在构造函数的时候初始化。同时,Looper 在方法 prepare 中进行初始化,获取到当前线程的 Looper 只能通过方法 myLooper 进行获取。

核心代码是 loop 方法,先获取当前线程的 Looper,并判断不为空,保证在调用 loop 之前必须要调用 prepare 方法。然后就是一个死循环,不停的获取消息队列的下一个消息进行转发给 Handle 进行处理。

4.MessageQueue

public class MessageQueue {

	//这边采用数组方式进行存储消息
	Message[] mMessages;
	
	//存放的索引
	int putIndex;
	//获取的索引
	int getIndex;
	//消息的个数
	int count;
	
	//互斥锁
	private Lock lock;
	//条件变量
	private Condition noEmpty;
	private Condition noFull;
	
	public MessageQueue() {
		//这边写死大小
		this.mMessages = new Message[50];
		
		this.lock = new ReentrantLock();
		this.noEmpty = this.lock.newCondition();
		this.noFull = this.lock.newCondition();
	}
	
	/**
	 * 加入队列
	 * @param msg 
	 */
	public void enqueueMessage(Message msg) {
		try {
			lock.lock();
			//消息队列满了,子线程停止发送消息,阻塞
			while (count == mMessages.length) {
				try {
					noFull.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			mMessages[putIndex] = msg;
			//等于 50,置 0
			putIndex = (++putIndex == mMessages.length) ? 0 : putIndex;
			count ++;
			
			//添加了消息,通知 Looper 进行轮询
			noEmpty.signal();
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 出队列
	 */
	public Message next() {
		Message msg;
		try {
			lock.lock();
			//消息队列为空,子线程 Looper 停止轮询,阻塞
			while (count == 0) {
				try {
					noEmpty.await();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			msg = mMessages[getIndex];
			//取完之后,消息置空
			mMessages[getIndex] = null;
			//等于 50,置 0
			getIndex = (++getIndex == mMessages.length) ? 0 : getIndex;
			count --;			
			
			//消费了消息,通知 Handler 可以继续添加
			noFull.signal();
			
		} finally {
			lock.unlock();
		}
		return msg;
	}
}

MessageQueue 跟源码的实现有点不一样。这边消息的存储使用一个数组进行实现,源码使用的是一个链表的结构实现 Message。

为了避免消息无限制的添加,内存不停增大,对消息队列大小进行限制,这边写死为50。

提供了 enqueueMessage 和 next 两个方法,分别实现了消息的入队和出队功能。按消息数组索引进行循环存储和读取。

考虑到两种特殊的情况:1.消息队列满了,子线程停止发送消息。2.消息队列为空,子线程 Looper 停止轮询。这两种情况下需要进行线程的堵塞,等待消息的入队和出队,使用了一个互斥锁进行管理实现。

5.HandleTest

public class HandleTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		final Random random = new Random();
		
		//轮询器初始化
        Looper.prepare();
        
        //Handler 存放于主线程
        final Handler handler = new Handler(){
        	
        	@Override
        	public void handleMessage(Message msg) {
        		System.out.println(Thread.currentThread().getName()
        				+ "received " + msg.toString());
        	};
        };
        
        //在子线程进行调用 Handler
        for (int i = 0; i < 10; i ++){
        	new Thread(){
        		
        		@Override
        		public void run() {
        			while(true){
        				Message msg = new Message();
            			msg.what = 1;
            			msg.object = Thread.currentThread().getName()
            					+ "send message " + random.nextInt(100);
            			System.out.println(msg);
            			
            			handler.sendMessage(msg);
            			
            			try {
							Thread.sleep(1000);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
        			}
        		};
        	}.start();
        }
        
        //轮询器开启轮询
        Looper.loop();
	}
}

这是带 main 函数的类,入口,模拟 ActivityThread,先调用 Looper.prepare() ,再调用 Looper.loop(),中间进行新建子线程发送消息。可以在结果中看到,对应的 sendMessage 方法发送消息都有对应的 handleMessage 方法接收到并处理。

结果:
这里写图片描述

三、附

代码链接:http://download.csdn.net/download/qq_18983205/10237523

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值