您的位置:首页 > 移动开发 > Android开发

Handler消息机制

2018-03-07 14:48 169 查看

Handler消息机制

@(Android)

Handler消息机制是Android系统提供的一种用于解决线程间通信的方式。Handler主要作用,分发和处理消息,实现将一个任务切换到指定的线程中执行。

为什么使用Handler?

子线程是无法访问UI的。如果允许子线程访问UI且存在并发的情况,会使得控件处于混乱的状态。如果采用加锁来解决并发问题,又会使UI访问的效率降低。因此Android采用单线程模型操作UI,通过handler切换到UI线程,解决子线程无法访问UI的问题。

理解handler,Message,MessageQueue及Looper四者关系,是搞懂Android消息机制的关键。

Message

Message是消息机制中的媒介,包含了描述和任意数据对象,用于发送给handler。handler.postXXX(Runnable r)底层也是将Runnable封装成一个Message,这个Runnable设为Message的callback属性。

public final boolean postDelayed(Runnable r, long delayMillis)
{
return sendMessageDelayed(getPostMessage(r), delayMillis);
}

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


MessageQueue

MessageQueue是一个单链表,用于存放所有handler发送过来的消息。enqueueMessage( )往队列中插入一条消息,next( )是一个无限循环的方法,用于移除消息,如果队列为空,则阻塞。

Looper

Looper是给线程轮询消息的。线程默认是没有Looper的,需要调用Looper.prepare()创建looper,使用Looper.loop()开启消息循环,将next()获取的消息通msg.target.dispatchMessage(msg)回调给handler。如果线程中没有创建Looper,是无法使用handler的。由于Android程序在其入口函数ActivityThread$main(),调用了 Looper.prepareMainLooper(),因此我们可以在主线程中直接使用Handler。

一个线程中只能有一个MessageQueue和Looper,采用ThreadLocal进行保存

Handler

Looper把消息回调到handler的dispatchMessage中进行消息处理:

若该消息有callback,即通过Post(Runnable)的方式投递消息,因为在投递runnable时,把runnable对象赋值给了message的callback。

若handler的mCallback不为空,则交由通过callback创建handler方式去处理。

否则,由最常见创建handler对象的方式,在重写handlerMessage中处理。

子线程中使用Handler

遵循三个步骤

- 创建Looper

- 调用Looper.loop()

- 创建Handler

public class ChildThreadHandlerActivity extends Activity {
private MyThread childThread;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);

childThread = new MyThread();
childThread.start();

Handler childHandler = new Handler(childThread.childLooper){//这样之后,childHandler和childLooper就关联起来了。
public void handleMessage(Message msg) {

};
};
}

private class MyThread extends Thread{
public Looper childLooper;

@Override
public void run() {
Looper.prepare();//创建与当前线程相关的Looper
childLooper = Looper.myLooper();//获取当前线程的Looper对象
Looper.loop();//调用此方法,消息才会循环处理
}
}
}


创建子线程的handler, Handler childHandler = new Handler(childThread.childLooper)后,就可以使用这个childHandler发送消息到子线程中执行。上述代码中由于多线程,childThread.childLooper可能回报空指针错误。

Android提供HandlerThread,简化子线程使用Handler的代码。HandlerThread的run方法中创建Looper,并开启消息循环loop(),

@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}


public Looper getLooper() {
if (!isAlive()) {
return null;
}

// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}


getLooper()中使用wait,以及run()中使用nitifyAll(),解决getLooper()可能为空的问题。

HandlerThread应用示例

public class HandlerThreadActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
TextView textView = (TextView) findViewById(R.id.tv);
textView.setText("HandlerThreadActivity.class");

HandlerThread handlerThread = new Ha
4000
ndlerThread("HandlerThread");
handlerThread.start();

Handler mHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子线程
}
};

Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主线程
mHandler.sendEmptyMessage(1);
}
}


子线程如何切换到UI线程

创建主线程的handler,并发送消息

new Handler(getMainLooper()).post(new Runnable() {
@Override
public void run() {
// 将在UI线程执行下句
Toast.makeText(getApplicationContext(), "Loaded Person from broadcast-receiver->intent-service: " + info, Toast.LENGTH_LONG).show();
}
});


Activity的runOnUiThread和View.postDelayed()内部也是使用handler切换到UI线程执行自己的Runnable。

public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}


public boolean postDelayed(Runnable action, long delayMillis) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.postDelayed(action, delayMillis);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().postDelayed(action, delayMillis);
return true;
}


Handler要注意的坑

使用非静态内部类声明Handler,会造成内存泄漏,推荐静态内部类+弱引用。

onDestory中移除队列中的消息,handler.removeCallbacksAndMessages(null)。

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