本文为楼主原创,转载请表明出处:http://blog.csdn.net/suma_sun/article/details/51598266
Android程序大部分的代码运行在主线程(必须运行在UI线程),比如事件(用户输入、滑动、屏幕旋转等)、回调、UI绘制等,都是执行在主线程的。而在这些地方插入的我们的代码也都是运行在主线程的,比如activity的生命周期方法——onCreate、onResume等。
写了个小demo,A的onResume跳转B然后finiash,onActivityResult时finish A。可以看出Activity的几个重要生命周期方法都是运行在主线程的。至于怎么判断是否为主线程上一篇文章Android多线程性能优化(一)里已经有该代码了不知道各位仔细阅读了没,下面给出关键代码:
Thread.currentThread() == Looper.getMainLooper().getThread();
在onPause持久化状态时要尽量快,不然会影响用户体验。当onPause结束时才会跳转到下个界面,我在onPause中线程休眠了几秒,明显给用户非常卡的感觉!所以一旦在生命周期方法中添加复杂的代码,就有可能影响到主线程的事件响应、绘制UI等。
为了让屏幕刷新帧率达到60fps,我们需要在16ms内完成单次刷新操作。一旦在主线程操作过于复杂将导致接收到刷新信号时无法及时刷新(资源占用),这样就造成了掉帧现象,刷新率自然就下降了,一旦帧率将至20fps用户将明显感受到卡顿不流畅。
为了避免上面的掉帧现象,我们就要使用多线程技术,使复杂的操作于另一个线程内处理,然后再由主线程显示结果,尽量避免占用资源影响UI绘制使帧率下降。
为了让主线程减少压力,使用多线程操作将成为重点。Android为开发者提供了几个方便的工具类。
IntentService
AsyncTask
在上一篇中已经介绍了,如果不清楚可以看我上一篇文章Android多线程性能优化(一)。
PS:其实是写太多剩下的还是分篇写吧
HandlerThread
HandlerThread是一个继承了Thread的子类。和AsyncTask一样并发量为1,但线程重用非常容易!停止也非常简单!
方法也不多也很好理解(我只介绍几个重要的方法)
quit()、quitSafely()两个方法差不多 关键就在于Safely,如何安全的退出。
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;
}
调用了Looer不同的退出方法,接着看
public void quit() {
mQueue.quit(false);
}
public void quitSafely() {
mQueue.quit(true);
}
调用了MessageQueue的queit方法
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to 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);
}
}
差别就在于调用了不同的移除方法
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
Message.next是什么呢?
// sometimes we store linked lists of these things
/package/ Message next;
这个next将会缓存队列里的另一个Message,缓存的是MessageQueue的mMessages对象,该对象由enqueueMessage(Message,long)也就是Handler发送消息的最终执行方法里进行赋值,还一个地方就是MessageQueue的next方法中赋值。
recycleUnchecked()这个方法用于清空Message中的消息和重置属性,以便重新利用该Message。怎么重用呢?——obtain该开头的签名方法都是利用之前已存在的Message来保存新的Message消息,避免重新创建对象造成资源浪费。
现在来看removeAllMessagesLocked()这个方法就很简单了。循环的把queue里的Message给清空掉。
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);
}
}
}
首先是now的获取SystemClock.uptimeMillis();返回值是这样说明的——milliseconds of non-sleep uptime since boot.
返回的是开机时间,且不计算休眠的时间也就是开机到现在的真正运行的时间。为什么要用这个时间而不用系统时间呢?因为该时间是不可修改的。
然后就是when了。when来源于enqueueMessage(Message,long)的long参数,也就是Handler发送消息时候的执行时间。如何保证两个值能正确的比较呢?
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
操作时间都是经过SystemClock.uptimeMillis()进行处理的。
mMessage作为将要执行的下一个Message其执行的时间大于当前,即未来要执行的任务就调用removeAllMessagesLocked();将队列里的消息全部重置。
下面是MessageQueue里的next方法里的相关代码
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
flags 在obtain()方法中就会置0,&——两数都为1时才为1;
所以拿到最后msg = null;next就会死循环但不会取出Message。
然后就是死循环取出next的Message,当next的Message里执行时间大于当前时间的退出循环,把其及后面的全部Message 清除掉(对象没有释放只是重置属性)。而执行时间小于等于当前时间的任务会继续执行完。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
run中先通过prepare()生成一个Looper,然后进入同步块获取该Looper,该同步块对应的是getLooper()这是为了解决异步时能获取到mLooper而阻塞了。
Process.setThreadPriority(mPriority);设置当前线程的优先级。
onLooperPrepared();
/**
* Call back method that can be explicitly overridden if needed to execute some
* setup before Looper loops.
*/
protected void onLooperPrepared() {
}
一个执行在loop()之前的方法,将实现推迟到子类。
到现在HandlerThread解析就结束了,弄清楚它的实现后使用起来就简单了!
下面给出使用方式
private HandlerThread mHandlerThread;
private Handler mBackgroundHandler;//后台线程Hander
private Handler mhandler = new Handler();//负责UI更新的Handler
private void initThread(){
mHandlerThread = new HandlerThread("backgroudThread", android.os.Process.THREAD_PRIORITY_BACKGROUND);
mBackgroundHandler = getBackgroudHandler(mHandlerThread);
}
private Handler getBackgroudHandler(HandlerThread handlerThread){
return new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0:
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final boolean isMainThread = Thread.currentThread() == Looper.getMainLooper().getThread();
mhandler.post(new Runnable() {
@Override
public void run() {
toast(String.format("当前%sUI线程", isMainThread ? "是" : "不是"));
}
});
break;
default:
break;
}
}
};
}
private void stopThread(){
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN_MR2)
mHandlerThread.quit();
else
mHandlerThread.quitSafely();
}