1.概述
当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程),主线程为管理界面中的UI控件,进行事件分发。如果此时需要一个耗时的操作,例如: 访问网络读取数据,或者读取本地较大的一个文件的时候,不要放在主线程中操作,如果主线程5秒钟还没有完成,界面会出现ANR假死现象,会收到Android系统的一个错误提示"强制关闭".故我们需要把这些耗时的操作,放在一个子线程中去完成,更新UI只能在主线程中更新,子线程中操作是危险的.由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。
2.主要成员
(1).Message
消息对象,内部封装需要传递的消息对象,主要有public字段arg1 arg2 obj,Message对象会关联一个MessageQueue实例对象中,该Message对象由Handler实例对象进行管理。为得到Message实例对象,可以使用Message.obtain() or Handler.obtainMessage(),不要使用构造函数来创建,使用消息池进行统一管理!
与MessageQueue对象关联的Message对象,都能够得到一个将该Message对象 关联的Handler对象.在处理messages时,会执行与该条message对象handler的 handleMessage(Message msg)方法!
//得到Message实例对象的2种方法 Message msg = Message.obtain() Message msg = Handler.obtainMessage()
(2).Looper
负责管理当前线程下的MessageQueue实例对象,除了主线程有默认的Looper对象 (UI线程的Looper被安卓环境创建),其它线程默认是没有Looper对象。调用Looper.prepare();方法会创建一个Looper和一个MessageQueue对象
class WorkerThread extends Thread { @Override public void run() { //1.创建与当前想关联的Looper对象和MessageQueue对象 Looper.prepare(); { /* * 该区域为prepare()内部实现过程 * 功能:为该线程创建一个Looper对象以及与该looper对应的MessageQueue对象 */ //1.1prepare() public static void prepare() { prepare(true); } //1.2prepare(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)); } //1.3new Looper(quitAllowed) private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); } //得到创建Looper对象和MessageQueue对象 } //2.创建handler对象 handler = new Handler() { @Override public void handleMessage(Message msg) { // process incoming messages here } }; { /** * new Handler()内部实现过程 */ public Handler(Callback callback, boolean async) { mLooper = Looper.myLooper(); // 得到当前线程的looper对象 if (mLooper == null) { //如果looper对象不存在,则报异常,可以调用Looper.prepare();完成looper对象的创建 throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; //为该looper指定MessageQueue mCallback = callback; mAsynchronous = async; } } //3.循环取出looper对象下的MessageQueue对应的消息 Looper.loop(); { //Looper.loop();内部实现过程 //死循环取出MessageQueue中的消息 for (;;) { Message msg = queue.next(); //该方法将 会阻塞 { //queue.next()内部实现 for (;;) { synchronized (this) { if (mQuiting) { //如果调用了quit()方法,此处为true return null;//返回为null,loop循环会退出 } } //如果存在Message对象,将会返回Message对象 } } if (msg == null) { return; //如果调用了Looper的quit()方法,将会退出 } //... ... msg.target.dispatchMessage(msg); { //msg.target.dispatchMessage(msg);内部实现 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } //将会执行handler子类的handleMessage()方法 handleMessage(msg); } } } //... ... msg.recycle(); } } //4.looper退出死循环 Looper.myLooper().quit(); //则推出死循环,当前线程处于死亡状态 } }
(3).Handler
处理Message和Runnable回掉函数。主要功能:
1)to schedule messages and runnables to be executed as some point in the future;
2)to enqueue an action to be performed on a different thread than your own.
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { /** * handler.getLooper(),通过looper得到MessageQueue向里面放入添加消息 */ Message msg = Message.obtain(); msg.obj = result; handler.sendMessage(msg); { //handler.sendMessage(msg)内部执行操作 queue.enqueueMessage(msg, uptimeMillis); { /* * enqueueMessage内部操作,完成项消息队列插入消息 */ final boolean enqueueMessage(Message msg, long when) { //... ... msg.when = when; Message p = mMessages; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { //... ... for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } } } } } });
(4).MessageQueue
消息队列,用以关联message
总结:一个应用application可能会存在很多Looper对象,在Looper,中有2个静态的字段
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); final MessageQueue mQueue;
该sThreadLocal用于存储当前线程的Looper对象,一个线程只能存在一个Looper对象,Looper.myLooper()能够得到当前线程下的looper。通过Looper.prepare()完成Looper和MessageQueue对象的创建,一个唯一的looper对应唯一的messageQueue。handler就是通过Looper.myLooper()得到当前下的looper,并通过looper找到MessageQueue对象,并将message与messageQueue关联!
3.传递Message的方式
(1).sendMessage()
//发送消息,发送结果数据 Message msg = handler.obtainMessage(); handler.sendMessage(msg);
(2).post(Runnable r)
post()方法主要是发送一些操作,sendMessage()发送的是数据结果,都是同一个消息队列的message,只不过通过post方式message的callback不为null,从而方法得到优先执行,具体可看下面的源代码。
class WorkerThread extends Thread { @Override public void run() { //在工作线程的run方法中,实现下面操作 handler.post(new Runnable() { //Runnable子对象不是线程,而是一个回掉接口 @Override public void run() { //从而在工作线程编写的代码,在主线程调用能够得到执行,从而完成ui界面的更新 textView.setText("更新了UI"); } }); //handler.post(r)内部执行操作 { /* * 首先会生成一个Message对象,然后将post的runnable对象 * 通过 getPostMessage,赋值给message的callback属性 */ public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); //getPostMessage(r)内部执行操作 { private static Message getPostMessage(Runnable r, Object token) { Message m = Message.obtain(); m.obj = token; m.callback = r; //赋值操作Runnable对象 return m; } } } } } }
//主线程的looper中loop循环消息的时候会执行 //... ... msg.target.dispatchMessage(msg); { //dispatchMessage()内部执行过程 public void dispatchMessage(Message msg) { if (msg.callback != null) {//此时的callback就不为null handleCallback(msg);//会执行该操作 { //handleCallback()内部实现 private static void handleCallback(Message message) { message.callback.run(); //在工作线程中创建,在main线程中引用,利用回调机制,从而执行run()还是在主线程中,从而可以完成UI的更新 //故在工作线程创建的Runnable对象,在主线程的looper.loop()循环迭代是时候会取执行runnable的run()方法 } } } else { //... ... handleMessage(msg); } } }
4.Looper重要性
定义一个工作线程复写run方法用于发送消息,并为当期的工作线程定义一个handler成员变量并接收主线程的looper对象,此时可以完成UI界面的更新。其中最主要的原因是在工作线程中定义的handler有了指定的looper,即getMainLooper()得到的对象,只不过更新UI的操作实在主线程中被调用,从而可以完成UI的更新!
/** * 在子线程中发送消息,并在在子线程中定义一个handler成员变量,也可以完成UI的更新, * 其中一个最重要的不同就为其指定了looper对象 */ class WorkerThread extends Thread { /* * 在handler构造函数中为其指定Main Looper对象,handler.mLooper = getMainLooper();对象 */ private Handler handler = new Handler(getMainLooper()){ @Override public void handleMessage(Message msg) { //完成UI界面的更新 textView.setText(msg.obj.toString()); } }; @Override public void run() { while(true){ Message msg = handler.obtainMessage(); msg.obj = "my:"+System.currentTimeMillis(); /* * 根据handler.mLooper.mQueue ,将当前消息发送到Main MessageQueue中 * 并在sendMessage时,为当前发送的msg指定当前的handler * * mainLooper.loop()在得到消息后,调用msg.target.dispatchMessage(msg); * 即handler的handleMessage()在主线程中调用执行,故可以完成UI的更新 */ handler.sendMessage(msg); try { Thread.sleep(3*1000); } catch (Exception e) { e.printStackTrace(); } } } }
5.特殊UI更新
更新UI在工作线程发送一个Message,在主线程的handler完成UI的更新。但是有些特殊情况,比如进度条的更新可以在工作线程直接操作,以及吐司
(1).ProgressBar.setProgress()
class WorkerThread extends Thread { @Override public void run() { progressBar.setProgress(10);// 直接操作更新ProgressBar { //progressBar.setProgress()内部实现原理 //... ... if (progress != mProgress) { mProgress = progress; refreshProgress(R.id.progress, mProgress, fromUser); { //refreshProgress();内部执行操作 private synchronized void refreshProgress(int id, int progress, boolean fromUser) { //首先判断当前线程是否为mainThread,如果为MainThread执行操作更新进度 if (mUiThreadId == Thread.currentThread().getId()) { doRefreshProgress(id, progress, fromUser, true); } else { if (mRefreshProgressRunnable == null) { mRefreshProgressRunnable = new RefreshProgressRunnable(); } final RefreshData rd = RefreshData.obtain(id, progress, fromUser); mRefreshData.add(rd); if (mAttached && !mRefreshIsPosted) { /* * 如果不是在UI线程,则通过View的post()方法,传递一个Runnable接口, * 将生成的消息放到handler生成主消息队列中,由主线程处理,完成UI界面进度的更新 */ post(mRefreshProgressRunnable); mRefreshIsPosted = true; } } } } } } } }
总之,对于SeekBar,ProgressDialog,ProgressBar进度的更新可以直接放在工作线程中,其内部实现还为消息队列!
(2).dialog.dismiss()
dialog的字段成员handler的对象实例化是在主线程被创建的!class WorkerThread extends Thread { @Override public void run() { //一个dialog在工作线程中可以直接调用dismiss()方法 { //dismiss()内部实现 public void dismiss() { //如果是当前线程直接调用dismissDialog(); if (Looper.myLooper() == mHandler.getLooper()) { dismissDialog(); } else { /* * 如果不在主线程中会通过handler对象,传递一个runnable接口对象, * 此时handler对象的创建位置至关重要,由于dialog的为UI界面,其实例化都是在主线程中 * 故将会被传递至主消息队列中,从而可以完成UI界面的更新(dismiss对话框) */ mHandler.post(mDismissAction); { //mDismissAction的实例化为一个runnable子类 private final Runnable mDismissAction = new Runnable() { public void run() { dismissDialog(); } }; } } } } } }
(3).Toast
class WorkerThread extends Thread { @Override public void run() { Looper.prepare(); Toast.makeText(MainActivity.this, "工作线程吐死了!", 1).show(); Looper.loop(); } }
6.应用
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private Button button;
private EditText path;
private ImageView imageView;
private Runnable runnable;
private ProgressBar bar;
private TextView info;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//判断图片是否加载完全
if(msg.arg1 == 1){
imageView.setImageBitmap((Bitmap)msg.obj); //更新图片
}else{
bar.setProgress(msg.what); //更新进度条值
info.setText(msg.what+"%"); //设定text值
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//得到控件
button = (Button) this.findViewById(R.id.confirm);
path = (EditText) this.findViewById(R.id.path);
imageView = (ImageView) this.findViewById(R.id.image);
bar = (ProgressBar) this.findViewById(R.id.bar);
info = (TextView) this.findViewById(R.id.info);
//设定click事件
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bar.setProgress(0); //初始化进度条
new Thread(runnable).start(); //开启线程
}
});
runnable = new Runnable() {
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(path.getText().toString()).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(3000);
if(conn.getResponseCode() == 200){
int totalSize = conn.getContentLength();
int currentSize = 0;
InputStream in = conn.getInputStream();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[1024];
int len = -1;
while((len=in.read(bytes)) != -1){
bos.write(bytes, 0, len);
currentSize += len;
int value = (int)((currentSize/(float)totalSize)*100);
//发送消息,以更新进度条
Message msg = Message.obtain(handler, value);
msg.sendToTarget();
}
Bitmap map = BitmapFactory.decodeByteArray(bos.toByteArray(), 0, totalSize);
//将得到的图片以消息传送
Message msg = Message.obtain(handler);
msg.obj = map;
msg.arg1 =1;
msg.sendToTarget();
}
} catch (IOException e) {
e.printStackTrace();
}
}
};
}
}