Handler源码分析及示例(一)

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方法。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 210,914评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 89,935评论 2 383
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,531评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,309评论 1 282
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,381评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,730评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,882评论 3 404
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,643评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,095评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,448评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,566评论 1 339
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,253评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,829评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,715评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,945评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,248评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,440评论 2 348

推荐阅读更多精彩内容