您的位置:首页 > 其它

Handler

2015-12-16 16:39 169 查看
Handler

比如总公司就好比是main线程(UI线程) , 分公司就好比是处理地方业务的子线程,

那么总公司和地方分公司之间的衔接通讯, 就需要办事员, 这个办事员就好比是handler ,

办事员是很灵活的, 既能给总公司办事, 也能给分公司办事,

同样的道理,有时交给handler的任务是在UI线程里调用, 有的时候是在子线程调用..

于是就需要看办事员进公司时候给他的职位, 也就是handler创建的时候是绑定的哪个线程

比如我们在Activity里面创建handler = new Handler(); 那么这个handler就和主线程绑定了 ,

简单说, 调用不带参数的构造方法创建的handler默认和当前线程绑定.

比如, 我们创建了个子线程完成一些工作处理完后需要更新UI, 这时就可以用到主线程关联的handler, 因为交给主线程关联的handler的任务就在主线程中运行.

handler = new Handler();

new Thread(){

public void run(){
....
handler.post(Runnable r);
}

}.start();


这个例子中在主线程里调用了不带参数的构造方法 ,于是handler就和主线程关联起来了,

然后我们创建一个子线程去完成一些任务…完成后, 现在需要更新UI, 那么通过主线程关联的handler.post的任务 ,

handler.post(Runnable r), Runnable r这段代码是在主线程中被执行. 于是顺利更新UI.

前面说到办事员很灵活, 上面是他在分公司中(子线程)中替总公司办事, 交给他的任务是在总公司中被执行.

那么办事员的任务究竟在总公司还是分公司执行由什么决定呢? 这个必须看他进公司时候是怎么关联的, 到底是关联的总公司还是分公司..

如果他进公司的时候(创建的时候)是被分配为总公司的(在主线程中调用不带参数的构造函数), 那么post交给这个办事员的方法就在总公司执行

他进公司的时候,被关联在分公司, 则交给他的任务就在分公司执行

那下面再看个例子, handler这个办事员, 进公司被创建的时候被分配为分公司的情况:

HandlerThread hThread = new HandlerThread(THREAD_NAME);
hThread.start();
mTaskHandler = new Handler(hThread.getLooper())


这里用了一个不同的构造方法, 也就是让hendler不再是和当前线程关联, 而是让handler关联一个自定义的线程

使用这个构造方法Handler(Looper looper) , 它需要传递参数looper , 其实looper就是属于HandlerThread的一部分.

调用这个构造方法, 将handler和loop关联起来了, 其实就是把handler和handlerThread关联起来了

至于looper是什么几乎可以不用管, 因为它基本上就是透明的, 有和没有对于我们写代码没有关系, 只是对我们理解原理有帮助

looper就相当于给Thread增加了附加功能, 让Thread可以实现了消息循环的功能, 于是这样的带有looper的thread就是HandlerThread

经过上面创建好handler后, 所有通过post方法传递给handler的runnable对象都是在HandlerThread子线程中运行的…

mTaskHandler.post(new Runnable() {
@Override
public void run() {
doSth(mInst);
}
});


那么这个例子就好比是办事员创建的时候, 就分配好他属于分公司, 然后post给这个handler的方法, 都在分公司子线程中运行, 比如上面例子中的匿名内部类里面的run方法都在子线程中运行.

如果需要和总部通信的话, 就重写handleMessage方法,

上面总是说到总公司分公司, 可以看出handler和总公司关联, post给他的任务就是总公司执行, handler和分公司关联, post给他的任务就是分公司执行.

有的时候我们需要让一个任务重复执行, 或者定时执行, 可以用Timer或者是AlarmManager, 那么handler其实也可以实现, 比如使用postDelayed, 或者发消息sendMessageDelayed

private static Handler mHandler = new Handler() {

@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what) {
case MSGWHAT_TIME_TO_START:
start((Context) msg.obj);
break;
default:
break;
}
}

};

public static void start(Context ctx) {
.......
Message msg = mHandler.obtainMessage(MSGWHAT_TIME_TO_START);
msg.obj = ctx;
mHandler.sendMessageDelayed(msg, TIME_TO_START);
}


=========================================

os.Looper.java

Looper 类

ThreadLocal 相当于是一个令牌

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


prepare 方法里首先判断令牌ThreadLocal是否已经存在(非空), 那说明当前线程已经存在一个对应的Looper对象

如果令牌不存在, 那么首先创建当前线程的Looper对象, 并设置ThreadLocal这个令牌为非空;

而调用的Looper对象的构造函数, new Looper(quitAllowed) 其实就是生成一个消息队列对象mQueue , 并关联当前线程mThread

也就是说, 运行了Looper的prepare方法后, 当前线程就会关联一个MessageQueue消息队列

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


ThreadLocal 的令牌功能的实现, static final ThreadLocal sThreadLocal = new ThreadLocal();

首先ThreadLocal是静态的, 和类相关的有且只有一个,

ThreadLocal 表示存储Looper对象

然后通过get() set(obj) 方法判断是否已经存在Looper对象

os.Handler.java

Handler

handler 构造函数调用 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;


先看看myLooper() , 调用后返回的就是当前线程对应的Looper对象, 这个Looper对象创建时生成的mQueue作为当前线程(生成handler对象)的消息队列

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


于是Handler, Looper 都对应起来了, 只要是生成了Handler对象, 就会有对应的Looper对象, 并且这个Looper对象有唯一的消息队列

默认情况下worker线程是没有Looper对象的, 当然也就没有消息队列了, 于是worker线程如果要处理消息的话, 就必须先创建looper对象

* Class used to run a message loop for a thread.  Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
*  class LooperThread extends Thread {
*      public Handler mHandler;
*
*      public void run() {
*          Looper.prepare();
*
*          mHandler = new Handler() {
*              public void handleMessage(Message msg) {
*                  // process incoming messages here
*              }
*          };
*
*          Looper.loop();
*      }
*  }


//另外需要注意的是 Activity的MainUI线程默认是有消息队列的。所以在Main Activity中新建Handler时,不需要先调用Looper.prepare() 也不需要调用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
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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}

msg.target.dispatchMessage(msg);


先通过myLooper得到当前线程的looper对象从而获得消息队列

然后一个for无限循环, 从队列里取出next msg , queue.next() 实际是从消息池里取出消息, 而且可能阻塞 .

如果当前没有msg则退出, 正常获取消息后进行了一些其他操作

然后执行msg.target.dispatchMessage(msg);

那么target是什么呢?我们查看

Message.java 中的定义

/*package*/ Handler target;


可见target就是Handler, 那么Handler和msg的target是怎么联系起来的呢?

我们生成消息的时候不是直接调用Message的构造函数而是通过

Message msg = handler.obtainMessage();


//Handler类obtainMessage 方法如下, 它间接帮我们调用Message的构造函数(通过Message.obtain()方法), 并且传递了参数this对象

public final Message obtainMessage()
{
return Message.obtain(this);
}


//Message类 obtain(Handler h)

public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}


// 可见handler的obtainMessage 在调用Message.obtain(this);方法时, 把this对象,也就是本身handler对象传递给了msg

// 于是msg对象的target赋值为handler, 也就是生成msg的这个handler对象

// handler 可以生成多个msg , 一个handler可以对应多个msg

// 消息池中查看是否有msg没有就生成新的msg

synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}


msg和handler建立了对应关系后, 我们继续看looper对象的loop方法里面 msg.target.dispatchMessage(msg);

也就是handler的dispatchMessage方法

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


首先判断msg是否有回调函数, 有的话就执行msg的回调函数

没有msg回调函数的话, 就判断是否有handler的回调函数, 有就执行

msg,handler 的回调函数都没有的话, 就执行handleMessage方法

所以我们生成handler类时, 要求 Override handleMessage方法, 这样就保证了msg一定被处理

纵观整个的过程, 为了实现worker线程的消息循环,需要有消息队列, 消息循环, 消息处理

looper 和消息队列是直接关系, looper里面生成线程对应的唯一的消息队列对象

handler 和looper是直接关系, handler的构造函数里面直接赋值looper对象为一个成员变量,

handler获取到looper对象后, 也就获得了looper对象所生成的线程的消息队列, handler在哪创建, looper就在哪个线程?

message 和Handler是直接关系, 因为message是通过handler的 obtainMessage()方法得到的, msg的target成员变量就是生成msg的handler

于是流程如下 ,

looper 生成mQueue 也就是线程消息队列, 确保当前线程只有一个消息队列, 只有一个Looper对象

创建handler , 对象生成时, 绑定已经生成的looper , 获取到looper的消息队列mQueue

有了上面两步就已经有了消息队列了, 紧接着就由handler创建msg, 并让msg绑定本身handler, 然后通过sendMessage把msg放入looper的消息队列,

looper 执行消息循环loop() 取出mQueue里的消息, 并按msg的回调函数,handler的回调函数,handler类handleMessage方法的次序去处理消息

比如在主线程中创建了handler, 存在默认的looper, 不需要去调用prepare.

handler创建后就会自动绑定main线程的looper,

然后我们启动了子线程, 在子线程中进行了一些操作得到数据等等, 这时就可以在子线程里调用handler.obtainMessage(), 来生成消息, 然后给消息赋值, 并sendMessage(msg)

handler在子线程里对消息的操作, 最终都是把消息放到了主线程的消息队列中, 因为handler是绑定了其所在的线程的looper,

然后主线程会自动去调用loop()方法取出消息, 并去执行主线程中handler定义的handleMessage方法

于是实现了子线程和主线程之间的通讯

//

// handler 的 post方法

线程的实现, 一个是继承Thread类 , 另外是实现Runnable接口,,

Thread类就直接代表一个线程 , 而实现Runnable接口 生成Runnable对象的话,

Runnable 对象不是线程, 是一个线程体, 不能单独直接运行, 只能传递给线程对象, 通过线程对象来启动运行 比如

Thread t = new Thread(r);
t.start();
//Hander.java  post(Runnable r) 方法:
public final boolean post(Runnable r)
{
return  sendMessageDelayed(getPostMessage(r), 0);
}


//Hander.java getPostMessage方法:

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


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

//Hander.java

private static void handleCallback(Message message) {
message.callback.run();
}


post(Runnable r) 方法实际上, 首先是调用obtain生成一个message , 然后把给这个msg赋值回调函数 m.callback = r; 最后把带有Runnable对象的msg放入Looper的mQueue消息队列

为什么要这么做呢? 常规的手段我们是通过msg来让worker线程和main线程通讯, 那么就存在一个数据传递, 那么能不能在子线程里, 在一些数据处理完成后直接对main线程进行操作呢?

于是就可以通过这个带有Runnable对象的Message来实现, 把需要对main线程的操作放在runnable对象r里, 主线程里创建handler对象, 绑定主线程的looper, 然后在子线程里, 数据操作完成后,

通过handler的post(r)方法, 生成一个回调函数为r的msg, 并放到handler对应的消息队列中, 也就是主线程中, 然后main线程会调用loop() 并执行这个消息对象的回调函数

比如下面的这个例子, main线程里直接定义了一个handler变量, 然后通过按钮去启动一个TestThread , TestThread里面的run方法里面执行一些worker thread的数据操作后, 生成一个匿名Runnable对象

这个runnable对象最后通过handler.post(r)方法传递给消息队列,然后这个r对象虽然是在子线程里面创建的, 但是实际上这个r对象的run方法是在main线程里被执行, 因为消息都是被handler操作来操作去,

而这个handler是在main线程里创建的, handler绑定的消息队列是main的消息队列, 因此, 消息循环也就在main线程 并按msg的回调函数,handler的回调函数,handler类handleMessage方法的次序去处理消息

private Handler handler = new Handler();

class TestThread extends Thread {
@Override
public void run() {

// 子线程中一些数据操作

Runnable r = new Runnable() {
@Override
public void run() {
String currentThreadName = Thread.currentThread().getName();
System.out.println("runnable 对象当前运行的线程名称是 ====>"
+ currentThreadName);
// runnable对象通过handler的post方法赋值给了msg的callback
// 于是 runnable对象被放在主线程里通过msg的回调函数被执行 , 因此可以直接修改UI
tv.setText("子线程已运行");
}
};
handler.post(r);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: