性能优化 -- 优化SurfaceView的线程调用

性能优化 – 优化SurfaceView的线程调用

目录

SurfaceView的基本用法

SurfaceView是一个适用于频繁的刷新布局的View,可以在非主线程中刷新View,其基本用法如下

创建实例对象

SurfaceView mSurfaceView = new SurfaceView(this);
setContentView(mSurfaceView);

添加SurfaceHolder.Callback

mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }
});

在Surface创建改变和销毁时会调用此回调

一般会在surfaceCreated方法中启用一个线程,然后借用这个线程来刷新View

mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {

    private boolean destroy = false;

    @Override
    public void surfaceCreated(final SurfaceHolder holder) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!destroy) {
                    Canvas canvas = holder.lockCanvas();
                    onDraw(canvas);
                    holder.unlockCanvasAndPost(canvas);
                }
            }
        });
        thread.start();
    }

    private void onDraw(Canvas canvas) {
        canvas.drawColor(Color.GREEN);
        //.......................在此做对View的绘制操作
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        destroy = true;
    }
});

首先借用new Thread创建一个线程,然后用一个变量destroy来标记SurfaceView的状态,借助destroy来控制线程的启停.

然后在线程中通过holder.lockCanvas()获得画布对象canvas,通过holder.unlockCanvasAndPost(canvas)释放和发送canvas对象来刷新布局.

值得注意的是用以上方法获得的Canvas对象是之前留下来的,所以如果没有需要保留上次绘制的情况下,需要调用canvas.drawColor()来执行一次清屏操作.

问题

基本用法介绍完了,那么思考个问题,上面创建了一个线程,几乎是死循环,就算没有新视图的刷新,线程还是一直在运行,这样是对系统资源的一种浪费,那么该如何优化线程的使用呢?

加延时吗?

加延时的化,Android屏幕的刷新时间是16ms,如果超过这个时间,便会有卡顿的感觉,也就是说加延时的话最多在每次循环里加16ms.

每次延时16ms,在一般情况下有点杯水车薪的感觉,然后有时候也可能有频繁刷新的请求,在请求频繁时或者绘制强度比较高的情况下16ms依然是造成视图卡顿的.

那么到底应该如何优化线程的调用呢?

解决方案

其实我们可以使用HandlerThread来代替普通的Thread,HandlerThread相比于普通的Thread,在没有任务请求时时阻塞挂起的,几乎不会消耗多少系统资源,有任务请求时又可以快速唤醒运行,相比于使用直接使用Thread明显更合理不少.

我们可以创建一个MySurfaceView类如下


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import com.yxf.surfaceviewtest.WeakHandler;


public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Handler.Callback {

    public static final String TAG = "MySurfaceView";

    public static final int MESSAGE_DRAW = 0;

    private HandlerThread handlerThread;
    private WeakHandler handler;

    private volatile Path path = new Path();


    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        getHolder().addCallback(this);
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MySurfaceView(Context context) {
        this(context, null);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.moveTo(event.getX(), event.getY());
                refresh();
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(event.getX(), event.getY());
                refresh();
                break;
            case MotionEvent.ACTION_UP:

            case MotionEvent.ACTION_CANCEL:

                break;
        }
        return true;
    }

    private void refresh() {
        Message message = Message.obtain();
        message.what = MESSAGE_DRAW;
        handler.removeMessages(MESSAGE_DRAW);
        handler.sendMessage(message);
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        handlerThread = new HandlerThread(TAG);
        handlerThread.start();
        handler = new WeakHandler(handlerThread.getLooper(), this);
        refresh();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        handlerThread.quit();
        handlerThread = null;
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MESSAGE_DRAW:
                Canvas canvas = getHolder().lockCanvas();
                Paint paint = new Paint();
                paint.setStyle(Paint.Style.STROKE);
                paint.setColor(Color.GREEN);
                paint.setStrokeWidth(10);
                canvas.drawColor(Color.WHITE);
                canvas.drawPath(path, paint);
                getHolder().unlockCanvasAndPost(canvas);
                return true;
        }
        return false;
    }
}

如此在surfaceCreated方法中初始化HandlerThreadWeakHandler对象,用WeakHandler发消息的来刷新视图的方式完全优于直接用Thread死循环的.

不过其实还可以对特殊情况再做下优化,比如说我们的视图中有多个SurfaceView,而且其刷新强度都不是特别高,则我们可以将多个视图共用同一个HandlerThread,改进后如下

package com.yxf.surfaceviewtest;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;


public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Handler.Callback {

    public static final String TAG = "MySurfaceView";

    public static final int MESSAGE_DRAW = 0;

    private boolean isQuitHandlerThreadWhenDestroy = true;

    private HandlerThread handlerThread;
    private WeakHandler handler;

    private volatile Path path = new Path();


    public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        getHolder().addCallback(this);
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MySurfaceView(Context context) {
        this(context, null);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                path.moveTo(event.getX(), event.getY());
                refresh();
                break;
            case MotionEvent.ACTION_MOVE:
                path.lineTo(event.getX(), event.getY());
                refresh();
                break;
            case MotionEvent.ACTION_UP:

            case MotionEvent.ACTION_CANCEL:

                break;
        }
        return true;
    }

    private void refresh() {
        Message message = Message.obtain();
        message.what = MESSAGE_DRAW;
        handler.sendMessage(message);
    }

    public WeakHandler setHandlerThread(HandlerThread thread) {
        return setHandlerThread(thread, null);
    }

    public WeakHandler setHandlerThread(HandlerThread thread, Handler.Callback callback) {
        if (thread == null) {
            Log.w(TAG, "the HandlerThread set is null");
            return null;
        }
        return initHandler(thread, callback,null);
    }

    public WeakHandler setHandlerThread(HandlerThread thread, Handler.Callback callback, WeakHandler handler) {
        if (thread == null) {
            Log.w(TAG, "the HandlerThread set is null");
            return null;
        }
        return initHandler(thread, callback, handler);
    }

    private WeakHandler initHandler(HandlerThread thread, Handler.Callback callback, WeakHandler h) {
        this.handlerThread = thread;
        if (handlerThread.getLooper() == null) {
            handlerThread.start();
        }
        if (callback == null) {
            callback = this;
        }
        if (h == null) {
            handler = new WeakHandler(thread.getLooper(), callback);
        } else {
            handler = h;
        }
        return handler;
    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (handlerThread == null) {
            handlerThread = new HandlerThread(TAG);
            initHandler(handlerThread, null,null);
            isQuitHandlerThreadWhenDestroy = true;
        }
        refresh();
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (isQuitHandlerThreadWhenDestroy) {
            handlerThread.quit();
            handlerThread = null;
        }
    }

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case MESSAGE_DRAW:
                Canvas canvas = getHolder().lockCanvas();
                Paint paint = new Paint();
                paint.setStyle(Paint.Style.STROKE);
                paint.setColor(Color.GREEN);
                paint.setStrokeWidth(10);
                canvas.drawColor(Color.WHITE);
                canvas.drawPath(path, paint);
                getHolder().unlockCanvasAndPost(canvas);
                return true;
        }
        return false;
    }
}

这样就可以实现多个SurfaceView共用同一个HandlerThread了.

为了防止Handler引发的内存泄漏,在此示例中使用了WeakHandler,关于WeakHandler,参见

性能优化 – 如何优雅的防止Handler引发的内存泄漏

猜你喜欢

转载自blog.csdn.net/dqh147258/article/details/79510153