Android_Thread多线程_Handler,Message,Looper,MessageQueue多线程和特殊UI更新

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();
				}
			}
		};
	}
}
  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值