Android开发你应该懂的:Handler

1.什么是Handler?
Handler是Android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以通过它发送消息,也可以通过它处理消息。
2.Handler的工作原理是什么?
Android中的异步消息处理涉及以下几个概
Handler
Looper
Message
MessageQueue
1.Looper
Looper主要有prepare()和loop()两个方法
一个线程中只有一个Looper实例
在线程中必须先调用Looper.prepare()方法,才能创建Handler对象loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理
2.Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据
3. MessageQueue
MessageQueue是由Looper创建的一个消息队列,它主要是用于存放所有的Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只有一个MessageQueue对象。它被创建后,Looper进入一个无限循环体不断从该MessageQueue中读取消息。
4. Handler
当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程管理界面中的UI控件, 进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭"。 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的, 也就是说,更新UI只能在主线程中更新,子线程中操作是危险的。 这个时候,Handler就出现了。
5. 那么它们是怎么合作完成复杂的异步消息处理机制的呢

从图中我们可以清晰的看出Handler的工作原理,Handler是运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据。Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:
(1)安排消息或Runnable 在某个主线程中某个地方执行;
(2)安排一个动作在不同的线程中执行。
Handler中有以下一些分发消息的方法:
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post类方法允许你排列一个Runnable对象到主线程队列中,
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新。
代码下发如下:
public class MyHandlerActivity extends Activity {
Button btnHandler;
MyHandler myHandler;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myhandler);
btnHandler = (Button) findViewById(R.id.btnHandler);
myHandler = new MyHandler();
// 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据
// Handler有两个作用, (1) : 定时执行Message和Runnalbe 对象
// (2): 让一个动作,在不同的线程中执行。
// 它安排消息,用以下方法
// post(Runnable)
// postAtTime(Runnable,long)
// postDelayed(Runnable,long)
// sendEmptyMessage(int)
// sendMessage(Message);
// sendMessageAtTime(Message,long)
// sendMessageDelayed(Message,long)
// 以上方法以 post开头的允许你处理Runnable对象
//sendMessage()允许你处理Message对象(Message里可以包含数据,)
MyThread m = new MyThread();
new Thread(m).start();
}
/**
* 接受消息,处理消息 ,此Handler会与当前主线程一块运行
* */
class MyHandler extends Handler {
public MyHandler() {
}
public MyHandler(Looper L) {
super(L);
}
// 子类必须重写此方法,接受数据
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
Log。d("MyHandler", "handleMessage。。。。。。");
super.handleMessage(msg);
// 此处可以更新UI
Bundle b = msg.getData();
String color = b.getString("color");
MyHandlerActivity.this.btnHandler.append(color);
}
}
class MyThread implements Runnable {
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Message msg = new Message();
Bundle b = new Bundle();// 存放数据
b.putString("color", "我的");
msg.setData(b);
MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI
}
}
}
另外除了发送消息之外,我们还有以下几种方法可以在子线程中进行UI操作:
- Handler的post()方法
- View的post()方法
- Activity的runOnUiThread()方法
需要注意的是:
public class MainActivity extends Activity {
private Handler handler1;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler1 = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler();
}
}).start();
}
}
运行上面的代码,程序就会崩溃,提示的错误信息为
Can't create handler inside thread that has not called Looper.prepare()
崩溃的原因是在子线程创建了Handler,而没有调用Looper.prepare(), 上面我们也讲过是因为在线程中必须先调用Looper.prepare()方法,才能创建Handler对象loop()方法
Google文档写法如下:
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();
}
}
那么问题就来了
为什么主线程中的Handler也没有调用Looper.prepare()方法,就没有崩溃呢
ActivityThread中的main()方法源码如下:
public static void main(String[] args) {
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
EventLogger.setReporter(new EventLoggingReporter());
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
AsyncTask.init();
if (false) {
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));
}
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
源码中可以看到在程序启动的时候,系统已经自动调用了Looper.prepareMainLooper(),为主线程创建了Looper,然后thread.getHandler(),保存了主线程的Handler,最后Looper.loop();进入消息循环。这样就可以解释我们上面的疑问了。
3.Handler和AsycnTask有什么关系呢
GoogleDeveloper解释如下:
AsyncTask is designed to be a helper class around Thread
and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time
意思就是AsyncTask是围绕Thread和Handler设计的辅助类,理想情况下,AsyncTasks应用于短操作(最多几秒钟)
也就是AsyncTask会自动帮我们创建一个线程,执行一个耗时操作,并随时报告执行进度给UI线程,执行完成后将结果报告给UI线程
AsyncTask有四个重要方法,当一个异步任务被执行时,要经历四步:
1.onPreExecute(),在UI线程中执行,它会在异步任务开始前执行,一般用来设置任务参数;
2.doInBackground, 最重要的方法,在子线程中执行(事实上,只有它在子线程中执行,其他方法都在UI线程中执行)。当onPreExecute结束后,本方法立刻执行,它用来进行后台的耗时计算,异步任务的参数会被传给它,执行完成的结果会被送给第四步;执行途中,它还可以调用publishProgress 方法来通知UI线程当前执行的进度;
3.onProgressUpdate, 当publishProgress 被调用后,它在UI线程中执行,刷新任务进度,一般用来刷新进度条等UI部件;
4.onPostExecute, 当后台的异步任务完成后,它会在UI线程中被调用,并获取异步任务执行完成的结果。
如有错误和遗漏,欢迎指正