您的位置:首页 > 产品设计 > UI/UE

[置顶] Android_Thread多线程_Handler,Message,Looper,MessageQueue多线程和特殊UI更新

2013-09-20 07:39 661 查看
本博文为子墨原创,转载请注明出处!
http://blog.csdn.net/zimo2013/article/details/11848463

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

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

dialog的字段成员handler的对象实例化是在主线程被创建的!

(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();
}
}
};
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐