您的位置:首页 > 其它

Handler异步处理机制

2015-08-27 18:16 211 查看
Handler使用场景:在非UI线程中对UI线程中的View进行操作,必须使用Handler,否则会报错。

错误使用示例:

public class MainActivity extends Activity {
TextView text;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.text);
new Thread(runnable).start();

}

Handler handler = new Handler(new Callback() {

@Override
public boolean handleMessage(Message msg) {
return false;
}
});
boolean isToRun = true;
int count = 0;

Runnable runnable = new Runnable() {

@Override
public void run() {
while(isToRun){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
count++;
if(count%2==0){
//在非UI线程中直接访问view
text.setBackgroundColor(Color.RED);
}else{
text.setBackgroundColor(Color.BLUE);
}
}

}
};

protected void onStart() {
super.onStart();
isToRun = true;
};
@Override
protected void onStop() {
super.onStop();
isToRun = false;
};

}
运行如上代码时,报如下异常:

Only the original thread that created a view hierarchy can touch its views.

正确使用示例:

public class MainActivity extends Activity {
TextView text;
Thread mThread;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
mThread = new Thread(runnable);
mThread.start();

}

private static final int CHANGE_TEXT_BACKGROUD_COLOR = 1;
Handler handler = new Handler(new Callback() {

@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case CHANGE_TEXT_BACKGROUD_COLOR:
if (count % 2 == 0) {
// 在非UI线程中直接访问view
text.setBackgroundColor(Color.RED);
} else {
text.setBackgroundColor(Color.BLUE);
}
break;
default:
break;
}
return false;
}
});
boolean isToRun = true;
int count = 0;

Runnable runnable = new Runnable() {

@Override
public void run() {
while (isToRun) {
Log.e("Handler", "thread is running");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
count++;
// 每隔一秒改变text的背景颜色
handler.sendEmptyMessage(CHANGE_TEXT_BACKGROUD_COLOR);
}

}
};

protected void onStart() {
super.onStart();
isToRun = true;
};

}


我们从代码上分析下原理,为什么在handler中对主UI view操作可以。既然UI VIEW只能在UI线程中进行操作,那么handler是否将对View的操作传递到了UI线程中,这样,从原理上是说的通的。下面分析下:

1.handler.sendEmptyMessage(CHANGE_TEXT_BACKGROUD_COLOR);
跟踪sendEmpthMessage调用

public final boolean sendEmptyMessage(int what)
{
return sendEmptyMessageDelayed(what, 0);
}

public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}

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

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
....
return enqueueMessage(queue, msg, uptimeMillis);
}

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


如上代码所示,Handler将CHANGE_TEXT_BACKGROUD_COLOR组装成一个Message,放到了一个Looper的队列(MessageQueue mQueue)中,这个时候我们应该想到,那么这个Looper对象应该是每个对象都有一个,并且每个Looper对象拥有一个队列,用与存储Handler发送的对象。

所以,现在我们来看是否每个线程含有独立的一个Looper对象???

-----------------------------------------------------------------------------------------------------

继续根据代码往下分析,我们看下mQueue对象是怎么实例化的。

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


上述代码实在UI线程执行的,示例得到一个Looper对象

if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}

由以上代码可知,在Looper调用myLooper()方法之前,肯定在某个地方调用了Looper.prepare()(在哪个地方调用呢,我们把这个问题放到后面)。

我们来看下Looper.prepare()

Looper.java

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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));
}


代码很好理解,就是在sThreadLocal中放入了一个Looper,在需要的时候通过Looper.myLooper()方法取出来。
另外注意sThreadLocal是静态的,说明说有的Looper对象共享这个对象。

在ThreadLocal的set方法中,代码获取当前线程(本例中,当前线程是UI主线程,因为我们的Handler是在UI主线程中示例化的)Values对象,并将创建的Looper存入了Values的一个Map数据结构中。

至此,我们知道,通过当前线程我们可以获取当前线程的Values对象,然后通过Values对象,可以获取每一个线程独有的Looper对象。每个线程拥有独立的Looper的对象,

也就相当于每一个线程拥有独立的一个队里MessageQueue(每一个Looper有一个MessageQueue对象)。

ThreadLocal.java

public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);//得到当前线程的Values,每一个线程的Values都是唯一的
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}

return (T) values.getAfterMiss(this);
}


综上所示:
Handler在当前线程中初始化时,会为当前线程创建一个独一无二的Looper,这个Looper中有一个队里MessageQueue,用与存储Handler发送的消息,并在合适的十几,按照先后顺序执行这些消息对应的操作。

------------------------------------------------------------------------------------------------------------------

现在我们来讨论,到底在什么时候才会执行这行消息对应的操作的,因为并没有看到调用这些消息的代码。

我们先来开心在自定义的一个线程中,Handler的操作。

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

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


如上所示,在我们初始化Handler之前,必须调用Looper.prepare()方法,创建Looper对象;并在初始化Handler之后调用Looper.loop()方。

loop()的方法如下,代码非常明了,就是便利MessageQueue对象中的消息,然后通过每个消息得到对应的Handler对象,调用dispatchMessage方法,处理消息对应的操作。

在dispatchMessage是调用了你自己实现的handleMessage方法

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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
msg.target.dispatchMessage(msg);

msg.recycleUnchecked();
}
}


我们在Activity的主UI线程中,并没有显式的调用Looper.prepare()和Looper.loop()方法,那系统在何时何地调用呢。

有一个类叫ActivityThread.java,它的main方法是这样的

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

...
Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
}

每一个activity都是通过它进行启动的,因此,在启动Activity之前,已经调用了prepareMainLooper方法初始化Looper对象。

-----------------------------------------------------------------------------------------------------------------------------------------------
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: