Android多线程性能优化(二)

本文为楼主原创,转载请表明出处: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为开发者提供了几个方便的工具类。

这一篇主要介绍HandlerThread

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();
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值