美文网首页
Handler源码分析及示例(一)

Handler源码分析及示例(一)

作者: 甲虫007 | 来源:发表于2017-09-16 17:29 被阅读17次

    Hanlder是安卓中重要的一个类,自己分析一下,也供大家参考。

    Handler源码分析及示例(二)

    Handler是什么:

    1. 一个handler允许你发送和传递消息和Runnable对象,并且和一个MessageQueue相联系
    2. 每个handler实例和一个单线程并且和此线程的MessageQueue相关联
    3. 当你创建一个handler的时候,它就和这个线程绑定起来,也和这个线程所创建的MessageQueue绑定起来(从此时起,他将会调度Message和Runable 到MessageQueue消息队列,当他们从消息队列出来的时候,将执行他们。)

    Handler两个主要作用是什么:

    1. 编排消息和runnables以便将来执行
    2. 在与您自己的线程不同的线程上执行一个操作。

    首先打开handler源码,我们先弄清里面的包含的方法:
    里面一个Callback接口

         /**
         * Callback interface you can use when instantiating a Handler to avoid
         * having to implement your own subclass of Handler.
         * Callback接口是,当你实例化一个handler的时候,为了避免你自己实现Handler的子类
         * 
         * @return True 如果不需要进一步的处理
         */
        public interface Callback {
            public boolean handleMessage(Message msg);
        }
    

    七个构造方法,即一个空的构造方法以及Looper,Callback,boolean三个类型参数的任意组合,2的3次方个。但其中,带boolean参数的构造方法,都被加了@hide隐藏掉了,所以我们基本没自己用到这3种构造方式。但暴露的构造方法最终都会调用
    public Handler(Callback callback, boolean async)或者(Looper looper, Callback callback, boolean async)三个参数的方法,其中callback参数可以为null,而looper的参数不能为null;

    • 区别在于用的是默认的Looper.myLopper()去获取一个Looper对象,还是我们自己创建的Looper对象,此方法到底有什么作用?以及什么时候需要我们自己创建Looper对象?

    • 如果looper为null,将抛出异常信息“Can't create handler inside thread that has not called Looper.prepare()”,此处就会有疑问,为什么平常我们从子线程发送消息到主线程的时候,我也没有创建Looper对象,怎么没报异常。答案是,在ActivityThread主线程中,系统其实已经帮我们调用了

    • 此处需要注意的是构造方法中有一句mQueue = mLooper.mQueue;告诉我们,Handler中的MessageQueue实际调用的是Looper当中的MessageQueue

    • 那么Looper到底是什么。以及他的作用是什么。继续分析。

        
        public void Handler(){
           this(null, false);
        };
        public void Handler(Callback callback){
            this(callback,false)
        };
        public void Handler(Looper looper){
            this(looper,null,false)
        };
        
        public void Handler(boolean){};
            
        public Handler(Callback callback, boolean async) {
            ...
            ...
            //主要行,通过Looper.myLooper();
            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;
        }
        
        //使用{@link Looper}作为当前线程,并设置处理程序是否应该是异步的。
        public Handler(Looper looper, Callback callback, boolean async) {
            mLooper = looper;
            mQueue = looper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    

    Looper是什么

    • 用于为线程运行消息循环的一个类
    • 如果需要创建一个,需要在轮训消息循环的线程中用prepare()方法创建一个,然后调用Looper.loop()方法处理消息,直到停止轮训操作。

    Looper.prepare()方法:

    • 通过sThreadLocal.set(new Looper(quitAllowed)),会调用Looper的构造方法创建一个looper对象,并添加到Threadlocal中。
        public static void prepare() {
            prepare(true);
        }
    
        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的构造方法中(这是一个私有的构造方法),创建了一个MessageQueue消息队列,并将mThread指定为当前创建此Looper的线程。
       private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    

    Looper.myLooper()方法

    • 此方法的作用是从threadlocal中获取一个Looper对象
        public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    Looper.loop()方法:

    • 可以看到此方法中, 通过取出一个Looper对象me,通过looper对象拿到消息队列queue,再遍历队列,取出消息msg,通过与msg关联的target,也就是handler对象,最终调用disaptchMessage方法。
    ...
    final Looper me = myLooper();
    ...
    final MessageQueue queue = me.mQueue;
    ...
    Message msg = queue.next();
    ..
    msg.target.dispatchMessage(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;
                }
    
                // 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);
                }
    
                // Make sure that during the course of dispatching the
                // identity of the thread wasn't corrupted.
                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的dispathchMessage(Message msg)方法:

    • 如果构造handler时callback参数不为null,会调用callback接口里面的方法,返回值为false的情况下,还会调用handler自身的handleMessage(msg)方法。如果msg的callback不为空,则只调用msg的callback.
     public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    关于Message对象的创建发送问题

    Message创建发送方式.png
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    遗留问题:延迟发送收到消息是怎么实现的

    例子

    • 测试在子线程中创建handler,就需要Looper.prepare(),以及最末调用looper.loop方法;不加会报错。
    • 通过使用子线程的handler,在主线程以及子线程中每隔1秒发送消息,子线程中的handler能够收到。
    • 至于在主线程中创建handler,以及在子线程中或主线程中发送消息的情况是,我们经常使用的。就不写例子了,强调的地方是这些情况不需要looper.prepare以及looper.loop方法了,系统已经加上这两句。
    public class MainActivity extends AppCompatActivity {
        private static final String TAG="TestHandler";
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
        }
    
        public void onClick(View view) {
            SubHandlerThread subHandlerThread=new SubHandlerThread();
            subHandlerThread.start();
            //主线程中用子线程的handler每隔一秒发送消息。
            while (true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if(subHandlerThread.handler!=null){
                    Message message=new Message();
                    message.obj=Thread.currentThread().getName();
                    subHandlerThread.handler.sendMessageDelayed(message,1000);
                }
    
            }
        }
        //在子线程中创建Handler
        public class SubHandlerThread extends Thread {
            public Handler handler;
    
            @Override
            public void run() {
                setName("SubHandlerThread");
                Looper.prepare();
                handler = new Handler(new Handler.Callback() {
                    @Override
                    public boolean handleMessage(Message msg) {
                        Log.d(TAG, "handleMessage in Callback: " + msg.obj);
                        return false;
                    }
                }) {
                    @Override
                    public void handleMessage(Message msg) {
                        super.handleMessage(msg);
                        Log.d(TAG, "handleMessage: Received: " + msg.obj);
                    }
                };
                
                //方式一
                //Runnable runnable=new RepeatMessage(handler);
                //runnable.run();
                //方式二
                Thread subThread = new Thread(new RepeatMessage(handler), "sub thread");
                subThread.start();
                Looper.loop();
            }
        }
        
        //子线程中用子线程的handler每隔一秒发送消息到handler.
        public class RepeatMessage implements Runnable{
            private Handler handler;
            public RepeatMessage(Handler h){
                this.handler=h;
            }
    
            @Override
            public void run() {
                //让子线程每隔一秒发送一个消息的方式一
    //            while (true){
    //                try {
    //                    Thread.sleep(1000);
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //                }
    //                if(handler!=null) {
    //                    Message msg = new Message();
    //                    handler.sendMessage(msg);
    //                    Log.d(TAG, "run: "+count);
    //                }
    //            }
    
                //让子线程每隔一秒发送消息的方式二
                if(handler!=null) {
                    Message msg = new Message();
                    msg.obj=Thread.currentThread().getName();
                    handler.sendMessage(msg);
                    handler.postDelayed(RepeatMessage.this,1000);
                }
            }
        }
    
    }
    

    总结

    1. handler既可以在子线程创建,也可以在主线程创建。
    2. 在创建handler之前需要调用Looper.prepare()方法准备一个looper对象。在主线程不需要,系统已经创建好了。
    3. 在发送消息后面调用looper.loop方法。主线程不需要,系统已调用。
    4. handler中的messagequeue是拿的looper中的messagequeue。这就解释了为什么需要先创建looper对象。
    5. looper.prepare()方法调用looper构造方法,构造方法中创建的messagequeue.
    6. handler发送的消息会加入messagequeue,looper.loop方法会轮询messagequeue,取得一个message,该message的target去处理消息
    7. 一个message的target就是一个handler。
      8.发送Runnable对象时,都会将Runnable封装成一个Message
      9.发送消息最终都会走enqueueMessage方法。

    相关文章

      网友评论

          本文标题:Handler源码分析及示例(一)

          本文链接:https://www.haomeiwen.com/subject/xkwysxtx.html