Android——HandlerThread浅析

Posted Yawn,

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android——HandlerThread浅析相关的知识,希望对你有一定的参考价值。

上一篇详细介绍了Handler的使用

1. 简介

android系统中,我们执行完耗时操作都要另外开启子线程来执行,执行完线程以后线程会自动销毁。想象一下如果我们在项目中经常要执行耗时操作,如果经常要开启线程,接着又销毁线程,这无疑是很消耗性能的。

HandlerThread是Google帮我们封装好的,可以用来执行多个耗时操作,而不需要多次开启线程,里面是采用Handler和Looper实现的。

怎么使用?

//创建实例对象,该参数表示线程的名字
HandlerThread handlerThread = new HandlerThread("myHandlerThread");

//启动我们创建的HandlerThread线程
handlerThread.start();

//怎样将Handler与线程对象绑定在一起
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
	@Override
	public void handleMessage(Message msg) {
		checkForUpdate();
		if(isUpdate){
			mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
		}
	}
};

2. 看个实例

public class MainActivity extends AppCompatActivity {
	private static final int MSG_UPDATE_INFO = 0x100;
	Handler mMainHandler = new Handler();
	private TextView mTv;
	private Handler mThreadHandler;
	private HandlerThread mHandlerThread;
	private boolean isUpdate = true;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mTv = (TextView) findViewById(R.id.tv);
		initHandlerThread();
	}
	private void initHandlerThread() {
		mHandlerThread = new HandlerThread("xujun");
		mHandlerThread.start();
		mThreadHandler = new Handler(mHandlerThread.getLooper()){
			@Override
			public void handleMessage(Message msg) {
				checkForUpdate();
				if (isUpdate) {
					mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
				}
			}
		};
	}
	
/**
* 模拟从服务器解析数据
*/
	private void checkForUpdate() {
		try {
			//模拟耗时
			Thread.sleep(1200);
			mMainHandler.post(new Runnable() {
				@Override
				public void run() {
					String result = "实时更新中,当前股票行情:<fontcolor='red'>%d</font>";
					result = String.format(result, (int) (Math.random() * 5000 + 1000));
					mTv.setText(html.fromHtml(result));
				}	
			});
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	protected void onResume() {
		isUpdate = true;
		super.onResume();
		mThreadHandler.sendEmptyMessage(MSG_UPDATE_INFO);
	}
	@Override
	protected void onPause() {
		super.onPause();
		isUpdate = false;
		mThreadHandler.removeMessages(MSG_UPDATE_INFO);
	}
@Override
	protected void onDestroy() {
		super.onDestroy();
		mHandlerThread.quit();
		mMainHandler.removeCallbacksAndMessages(null);
	}
}

运行以上测试代码,将可以看到如下效果图(例子不太恰当,主要使用场景是在handleMessage中执行耗时操作)
在这里插入图片描述

3. 看看源码

基于sdk23的,可以看到,只有一百多行代码:

public class HandlerThread extends Thread {
	int mPriority;
	int mTid = -1;
	Looper mLooper;
	public HandlerThread(String name) {
		super(name);
		mPriority = Process.THREAD_PRIORITY_DEFAULT;
	}
	public HandlerThread(String name, int priority) {
		super(name);
		mPriority = priority;
	}
/**
* Call back method that can be explicitly overridden if nee
ded to execute some
* setup before Looper loops.
*/
	protected void onLooperPrepared() {
	}
	
	@Override
	public void run() {
		mTid = Process.myTid();
		Looper.prepare();
		//持有锁机制来获得当前线程的Looper对象
		synchronized (this) {
			mLooper = Looper.myLooper();
			//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
			notifyAll();
		}
		//设置线程的优先级别
		Process.setThreadPriority(mPriority);
		//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
		onLooperPrepared();
		Looper.loop();
		mTid = -1;
	}
	
	public Looper getLooper() {
		if (!isAlive()) {
		return null;
		}
		// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
		synchronized (this) {
			while (isAlive() && mLooper == null) {
				try {
					wait();
				} catch (InterruptedException e) {
				}
			}
		}
		return mLooper;
	}
	
	public boolean quit() {
		Looper looper = getLooper();
		if (looper != null) {
			looper.quit();
			return true;
		}
		return false;
	}
	
	public boolean quitSafely() {
		Looper looper = getLooper();
		if (looper != null) {
			looper.quitSafely();
			return true;
		}
		return false;
	}
/**
* Returns the identifier of this thread. See Process.myTid(
).
*/
	public int getThreadId() {
		return mTid;
	}
}

(1)首先看看构造函数

	public HandlerThread(String name) {
		super(name);
		mPriority = Process.THREAD_PRIORITY_DEFAULT;
	}
	public HandlerThread(String name, int priority) {
		super(name);
		mPriority = priority;
	}

一个参数的和两个参数的,name代表当前线程的名称,priority为线程的优先级别

(2)看一下run()方法
在run方法里面我们可以看到我们会初始化一个Looper,并设置线程的优先级别

	public void run() {
		mTid = Process.myTid();
		Looper.prepare();
		//持有锁机制来获得当前线程的Looper对象
		synchronized (this) {
			mLooper = Looper.myLooper();
			//发出通知,当前线程已经创建mLooper对象成功,这里主要是通知getLooper方法中的wait
			notifyAll();
		}
		//设置线程的优先级别
		Process.setThreadPriority(mPriority);
		//这里默认是空方法的实现,我们可以重写这个方法来做一些线程开始之前的准备,方便扩展
		onLooperPrepared();
		Looper.loop();
		mTid = -1;
	}

前面我们说到使用HandlerThread的时候必须调用 start() 方法,接着才可以将我们的HandlerThread和我们的handler绑定在一起

  • 原因就是我们是在 run() 方法才开始初始化我们的looper,而我们调用HandlerThread的 start() 方法的时候,线程会交给虚拟机调度,由虚拟机自动调用run方法

为什么要使用锁机制和 notifyAll() :

  • 原因我们可以从 getLooper() 方法中知道
	public Looper getLooper() {
		if (!isAlive()) {
			return null;
		}
	// 直到线程创建完Looper之后才能获得Looper对象,Looper未创建成功,阻塞
	synchronized (this) {
		while (isAlive() && mLooper == null) {
			try {
				wait();
			} catch (InterruptedException e) {
			}
		}
	}
	return mLooper;
}

总结:在获得mLooper对象的时候存在一个同步的问题,只有当线程创建成功并且Looper对象也创建成功之后才能获得mLooper的值。这里等待方法wait和run方法中的notifyAll方法共同完成同步问题。

(3)接着我们来看一下quit方法和quitSafe方法

	public boolean quit() {
		Looper looper = getLooper();
		if (looper != null) {
			looper.quit();
			return true;
		}
		return false;
	}
	
	public boolean quitSafely() {
		Looper looper = getLooper();
		if (looper != null) {
			looper.quitSafely();
			return true;
		}
		return false;
	}

跟踪这两个方法容易知道只两个方法最终都会调用MessageQueue的 quit(boolean safe) 方法

void quit(boolean safe) {
	if (!mQuitAllowed) {
		throw new IllegalStateException("Main thread not allowedto quit.");
	}
	synchronized (this) {
		if (mQuitting) {
			return;
		}
		mQuitting = true;
		//安全退出调用这个方法
		if (safe) {
			removeAllFutureMessagesLocked();
		} else {//不安全退出调用这个方法
			removeAllMessagesLocked();
		}
		// We can assume mPtr != 0 because mQuitting was previously false.
		nativeWake(mPtr);
	}
}

不安全的会调用 removeAllMessagesLocked(); ,我们来看这个方法是怎样处理的,其实就是遍历Message链表,移除所有信息的回调,并重置为null。

private void removeAllMessagesLocked() {
	Message p = mMessages;
	while (p != null) {
		Message n = p.next;
		p.recycleUnchecked();
		p = n;
	}
	mMessages = null;
}

安全地会调用 removeAllFutureMessagesLocked(); 这个方法,它会根据Message.when这个属性,判断我们当前消息队列是否正在处理消息,没有正在处理消息的话,直接移除所有回调,正在处理的话,等待该消息处理处理完毕再退出该循环。因此说 quitSafe() 是安全的,而 quit() 方法是不安全的,因为quit方法不管是否正在处理消息,直接移除所有回调

private void removeAllFutureMessagesLocked() {
	final long now = SystemClock.uptimeMillis();
	Message p = mMessages;
	if (p != null) {
		//判断当前队列中的消息是否正在处理这个消息,没有的话,直接移除所有回调
		if (p.when > now) {
			removeAllMessagesLocked();
		} else {
			//正在处理的话,等待该消息处理处理完毕再退出该循环
			Message n;
			for (;;) {
				n = p.next;
				if (n == null) {
					return;
				}
				if (n.when > now) {
					break;
				}
				p = n;
			}
			p.next = null;
			do {
				p = n;
				n = p.next;
				p.recycleUnchecked();
			} while (n != null);
		}
	}
}

接下来看看Android——IntentService

以上是关于Android——HandlerThread浅析的主要内容,如果未能解决你的问题,请参考以下文章

Android——多进程

Android培训HandlerThread的使用及源码解析

Android HandlerThread 消息循环机制之源代码解析

android源码解析之-->HandlerThread

Android面试收集录8 HandlerThread详解

Android HandlerThread