Android中多线程通信:Handler的理解

Android中的Handler

Android中Handler在我理解主要是为了解决线程间通信。
使用Android的Handler机制主要要了解几个类:

  • Looper
    一个线程对应一个或者0个Looper,主线程在ActivityThread的时候会默认创建一个Looper,非主线程中需要先通过Looper.prepare()创建,并且通过Looper.loop()开启。
  • Message
    线程间通信的消息载体,Handler利用Message来携带信息给另一个线程
  • MessageQueue
    消息队列,与Looper一一对应,每个Looper中维护一个消息队列。
  • Handler
    有点像是一个工厂里的机器人,不断从这个线程中发送Message或者是Runnable给另一个线程,并放入MessageQueue中

线程分为有消息队列和没有消息队列两种,没有消息队列则线程启动完成执行完操作就结束了 ,而有消息循环队列的时候,线程可以通过循环调度消息队列的方式来执行消息队列中的每一个任务,Android中使用Handler进行线程间通信时,Handler必须指定要通信线程所持有的Looper。
准确的说,其实是Handler需要做的事情是向要通信的线程所持有的消息队列中加入任务。
在这里插入图片描述
如果你在Handler构造函数中没有指定Looper,那么调用Looper.myLooper()句柄来获取当前线程所对应的Looper,如果这时该线程还未初始化Looper对象,这时就会抛出一个异常, 这就是为什么在子线程时我们创建Handler前需要先调用Looper.prepare()方法的原因。
跟进Looper.myLooper()看一下:
在这里插入图片描述
调用Looper.myLooper我们看到其实是从sThreadLocal变量中get一个Looper对象,
ThreadLocal对象按我的理解其实是一个以线程作为key值来保存线程所对应的Looper的数据结构,
ThreadLocal在Looper中是以静态变量的形式存在的,所以这里可以断定的是,整个程序执行过程中只有一个ThreadLocal对象,并且它贯穿整个程序的生命周期,将所有带有消息循环的线程的Looper都和该线程一一对应地保存下来以确保每一个线程对应一个Looper。

Android在构造Handler时构造方法如果没有传递Looper对象进去,那么该Handler就是默认向其所在的线程的消息队列增加任务。从刚才上面的分析可知这时我们必须保证该Handler所在的线程在创建Handler前已经调用过Looper.prepare()创建了Looper对象,否则会抛异常。

但是我们有个疑问:在主线程中我们没有手动调用过Looper.prepare()方法,却也没见有什么问题?
原因在于那是因为在主线程中Google已经帮我们做了这些操作,不信看看整个Android应用程序的主入口:ActivityThread的main()方法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Looper.prepareMainLooper()中调用了Looper.prepare(false)来进行创建一个Looper
prepare传递的参数是false保证这个Looper是不可退出的,因为主线程要一直不断循环直到程序结束。

private static void prepare(boolean quitAllowed)做的就是创建一个Looper,在创建完Looper之后将该Looper设置给ThreadLocal缓存起来。

从方法中抛出的异常可以看出来,每个线程只能允许创建一个Looper,如果已经创建过了再创建就会抛异常,不管是主线程还是子线程。

紧接着回到ActivityThread的main()方法

调用完Looper.prepareMainLooper()方法创建完主线程的Looper之后,应该要开启消息循环来获取每一个消息任务并且执行。
所以我们看Looper.loop();这个句柄:
在这里插入图片描述
loop()方法首先也是取本线程的Looper,在我们分析的主线程中那就是主线程对应的Looper,紧接着也是判空抛异常,一目了然

final MessageQueue queue = me.mQueue;

这句中将Looper中维护的消息队列取出来,如果这时候有Handler使用了该looper对象作为参数的话,那么该Handler不管是post或者是sendMessage等等,

发送的Message都是添加到该MessageQueue中,post内部其实也是创建一个Message,并且将post的Runnable对象赋值给Message的callback,这个后面说。

有了消息队列后,开启一个无限循环来取队列中的每一个任务,如果这时候队列中取出来的已经空了,那么就返回

如果取出来的Message是个有效地消息,那么,关键来了:
msg.target.dispatchMessage(msg);
msg.target就是该消息中指定的Handler对象,你如果要说啊我每次创建Message的时候都直接obtain()一个,没有指定target呀,其实并不是这样的:

当我们通过Handler发送一个Message给消息队列的时候,最终都是要调用到enqueueMessage来入队:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)
在这里插入图片描述
这里再往深一点想,我们调用loop()方法,loop()方法中调用msg.target.dispatchMessage(msg);这时分两种情况:
1.传递的消息不手动指定target,那么我们要实现想要的意图:线程间通信,例如线程A与线程B通信,那么这时又区分出两种方式:

  • 在线程B中调用Looper.myLooper(),比如是:looperB,
    接着在线程A中用这个Looper来创建Handler,Handler handlerB = new Handler(looperB);
    所以在线程A中执行handlerB.sendMessage(msg)发送的消息就是发送到B线程所对应的looperB中的消息队列中了
  • 在线程B中先调用Looper.prepare()来初始化一下线程的Looper对象,这点原因上面说过了,创建Handler,Handler handlerB = new Handler();
    这时Handler的内部是调用Looper.myLooper()来指定给该Handler,并且会将该looper的消息队列指定给该Handler:mQueue = mLooper.mQueue;
    所以在线程A中执行handlerB.sendMessage(msg)发送的消息就是发送到B线程所对应的looper中的消息队列中了

2.给消息指定target对象,还是以线程A与线程B通信为例子:
A中:

Looper.prepare();
Message msg = Message.obtain();
msg.what = 111;
	...
// msg.target = handlerB;前面我们说过,Handler中将消息压进消息队列的时候到最后都是调用到Handler的enqueueMessage方法中,而该方法中都会先执行msg.target = this;,所以这里设置的target到最后都会在消息入队前被改成this,所以应该在post或者sendMessage()方法后面调用。
Handler handlerA = new Handler();
handlerA.sendMessage(msg);
msg.setTarget(handlerB);

B中:

Looper.prepare();
Handler handlerB = new Handler(){
	@Override
	public void handleMessage(Message msg) {
		if(msg.what == 111){
			...
		}
	}
}

其实,说这一段是因为我自己的一个疑惑,为什么Handler机制中要默认把msg.target = this;这样子的话开放出setTarget和getTarget方法有什么意义呢?
确实我还是不大明白为什么要这样子设计,但是我想用法除了上面这种我感觉非正常的应用之外可能还有一个场景就是当两个线程之间需要互相通信的时候也可以用
,比如下面这段代码:

Handler handlerB = null;
    private void init() {

        SubThreadB threadB = new SubThreadB();
        threadB.start();
        findViewById(R.id.hello).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SubThreadA threadA = new SubThreadA(handlerB);
                threadA.start();
            }
        });


    }

    public class SubThreadA extends Thread {
        private Handler handlerB;//我们需要某些操作在线程B中执行,并且在线程B执行完成之后给我们的线程一个回执结果

        public SubThreadA(Handler hb) {
            this.handlerB = hb;
        }

        @Override
        public void run() {
            Looper.prepare();
            Handler handlerA = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 222) {
                        Log.e("lianwenhong", "receive a reply from ThreadB:" + msg.obj);
                    }
                }
            };
            Message msg = Message.obtain();
            msg.what = 111;
            msg.obj = "你好";
            handlerB.sendMessage(msg);
            msg.setTarget(handlerA);
            Looper.loop();
        }
    }

    public class SubThreadB extends Thread {
        @Override
        public void run() {
            Looper.prepare();
            handlerB = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    if (msg.what == 111) {
                        Log.e("lianwenhong", "receive a message from ThreadA:" + msg.arg1);
                        Message reply = Message.obtain();
                        reply.what = 222;
                        reply.obj = "你也好";
                        msg.getTarget().sendMessage(reply);
                    }
                }
            };
            Looper.loop();
        }
    }

上面这个例子意思是想让ThreadA通过handerB把某些操作放给ThreadB去做,并且当ThreadB做完之后给ThreadA一个回执,这时将ThreadA中创建的Handler
放在HandlerB的target中,当ThreadB要发送回执的时候直接使用放在HanderB的target中的对象来向ThreadA的消息队列中加任务。

但是,其实上面这个例子是很烂的一段代码我自己感觉,因为两个线程间要通信确实没有必要这么繁琐
我们只要记住一点最重要的:只要两个线程要进行通信,那么只要在一个线程中持有另一个线程中创建的Handler,也就是持有了另一个线程的消息队列,这时直接用这个handler去向另一个线程的消息队列增加任务,我感觉这就是中心的思想。

Android中主线程刷新UI就是这个原理。全局生成一个主线程的Handler,不管在哪个线程中需要做UI操作都只要通过这个Handler发送任务就OK

接下来就剩一个事情了,那就是Handler回调到sidpatchMessaage(msg)方法之后做了一些什么事情:


首先:

if (msg.callback != null) {
	handleCallback(msg);
} 
private static void handleCallback(Message message) {
        message.callback.run();
}

这一行其实在我们生成一个Message的时候,可以指定该Message的Callback,这时候如果有赋值的话,那会先得到执行,这里的Callback其实是一个Runnable,所以这里走的是Runnable的run()方法,其他的handleMessage都走不到了。
创建带有Callback的Message代码如下:

	/**
     * Same as {@link #obtain(Handler)}, but assigns a callback Runnable on
     * the Message that is returned.
     * @param h  Handler to assign to the returned Message object's <em>target</em> member.
     * @param callback Runnable that will execute when the message is handled.
     * @return A Message object from the global pool.
     */
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }

还有一种场景就是当我们使用handler.post(Runnable r)方法的时候,参数就是一个Runnable对象,这时候我们跟进源码看到,这种方式向消息队列增加一个任务时,会obtain一个空的Message,然后将刚才传递的参数Runnable赋值给Message的callback属性:

public final boolean post(Runnable r){
       return  sendMessageDelayed(getPostMessage(r), 0);
}

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
}

紧接着那么在什么情况下 if(mCallback.handleMessage(msg)) 会成立呢?

if (mCallback != null) {
    if (mCallback.handleMessage(msg)) {
         return;
     }
}

当我们使用下面这个构造方法构造一个Handler的时候:

public Handler(Callback callback) {
	this(callback, false);
}

这里的Callback其实就是一个接口:

public interface Callback {
	/**
	* @param msg A {@link android.os.Message Message} object
	* @return True if no further handling is desired
	*/
	public boolean handleMessage(Message msg);
    }

这时就会将构造传递进来的参数赋值给Handler类的mCallback属性。
如果以上两种都走不到,那么走的就是另一种逻辑,就是Handler中的

	/**
	* Subclasses must implement this to receive messages.
	*/
	public void handleMessage(Message msg) {
	}

这个方法,这个方法是一个空方法,我们在构造Handler的时候实现它,我感觉这也是最常用的一种方式。

上述只是记录了自己看源码时的一个记录,很乱,等过段时间再看一遍的时候希望能有一个新的理解,并能加以重新改进。

之前看到一个特别好的线程间通信并且考虑到线程同步的代码,把链接记录下来:https://www.cnblogs.com/sipher/articles/2601511.html ,接下来应该再看一下进程间通信,加油不犯懒了啊啊啊啊啊啊啊啊啊啊啊!!!

AIDL,Messenger等

猜你喜欢

转载自blog.csdn.net/wenhonglian/article/details/84932874
今日推荐