SurfaceView多线程 画图

来源:互联网 发布:淘宝来客提醒软件违规 编辑:程序博客网 时间:2024/05/01 00:39


最近又重新看了下SurfaceView 相关用法,想使用SurfaceView 来进行多线程绘图。最基本的很简单 要使用SurfaceView 必须要有个SurfaceView,可以通过layout来布局,也可以通过代码来添加。 添加玩surfaceivew后需要,实现SurfaceHolder.Callback接口。代码如下

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mSurfaceView = (SurfaceView) findViewById(R.id.surfaceview);mHolder = mSurfaceView.getHolder();mHolder.addCallback(new MyHolderCallBack());mList.add(R.drawable.trees);mList.add(R.drawable.trees2);mList.add(R.drawable.trees3);mList.add(R.drawable.trees4);mList.add(R.drawable.trees5);}private ArrayList<Integer> mList = new ArrayList<Integer>();class MyHolderCallBack implements SurfaceHolder.Callback {@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {Log.e(TAG,"surface changed!");}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubLog.e(TAG,"surface created!");new LoadingThread().start();new DrawThread().start();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stub}}Object o = new Object();int MAX = 5;LinkedList<Bitmap> mBitmapList = new LinkedList<Bitmap>();    Object b = new Object();class DrawThread extends Thread {public void run() {while (true) {Canvas canvas = mHolder.lockCanvas();if (canvas != null) {Log.e(TAG,"Canvas is not null");if(mBitmapList.size() > 0){                    Bitmap map = mBitmapList.removeFirst();canvas.drawBitmap(map, 0, 0, new Paint());map.recycle();//Log.e(TAG,"当前图片的数量为 =  " + mBitmapList.size());int size = mBitmapList.size();if (size < MAX) {try {this.join(200);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (o) {o.notify();}}}mSurfaceView.getHolder().unlockCanvasAndPost(canvas);} else {Log.e(TAG,"Canvas is null");}}}}


上面的代码也许看着很简单,但是在写的时候还是遇到了一些麻烦.

1)    Canvas canvas = mHolder.lockCanvas();  或许很多人都遇到过这个问题, mHolder.lockCanvas(); 返回值为null,打印log 明明, surfaceCreated(SurfaceHolder holder)已经创建了,但mHolder.lockCanvas()返回值还是为空. 查看了官方文档,里面是这样说明的

Start editing the pixels in the surface. The returned Canvas can be used to draw into the surface's bitmap. A null is returned if the surface has not been created or otherwise cannot be edited. You will usually need to implement Callback.surfaceCreated to find out when the Surface is available for use.

我们的surfaceCreated 已经被调用了,肯定不是这里问题了,看了系统源代码才知道问题原因了


public Canvas lockCanvas() {            return internalLockCanvas(null);        }

        private final Canvas internalLockCanvas(Rect dirty) {            mSurfaceLock.lock();            Canvas c = null;            if (!mDrawingStopped && mWindow != null) {                if (dirty == null) {                    if (mTmpDirty == null) {                        mTmpDirty = new Rect();                    }                    mTmpDirty.set(mSurfaceFrame);                    dirty = mTmpDirty;                }                try {                    c = mSurface.lockCanvas(dirty);                } catch (Exception e) {                    Log.e(LOG_TAG, "Exception locking surface", e);                }            }            if (DEBUG) Log.i(TAG, "Returned canvas: " + c);            if (c != null) {                mLastLockTime = SystemClock.uptimeMillis();                return c;            }                        // If the Surface is not ready to be drawn, then return null,            // but throttle calls to this function so it isn't called more            // than every 100ms.            long now = SystemClock.uptimeMillis();            long nextTime = mLastLockTime + 100;            if (nextTime > now) {                try {                    Thread.sleep(nextTime-now);                } catch (InterruptedException e) {                }                now = SystemClock.uptimeMillis();            }            mLastLockTime = now;            mSurfaceLock.unlock();                        return null;        }

原来每次在取Canvas时候如果当前Surface没有准备好,就会等待100毫秒然后返回一个null.所以当你在使用lockCanvas时候并不是每次返回的canvas都是可以是可用的,有可能是返回一个null值.

2) 多线程处理我是使用两个线程一个用来读取资源LoadingThread,一个用来绘制DrawThread.这两个线程工作方式跟工厂模式有点像.边读边写.

最开始这两个线程的启动是放在onCreate函数里面的,但是想想,lockCanvas是在surfaceCreated调用后才能调用的.就把这两个线程的启动放在了 surfaceCreated里面.


这个线程是专门用来读取图片的,当然也可以从网上读取图片我这里做了一个简单处理,把每次读取的最大数量做了限定,当读取到最大数量图片时候,阻塞线程.等待唤醒重新读取.

class LoadingThread extends Thread {int current = 0;public void run() {while (true) {Bitmap map = BitmapFactory.decodeResource(getResources(),mList.get(current));//Log.e(TAG,"xxxx =" + current);mBitmapList.addLast(map);current++;if (current == mList.size()) {current = 0;}//Log.e(TAG,"图片数已经最大,现在等待" + mBitmapList.size());if (mBitmapList.size() == MAX) {synchronized (o) {try {o.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}}

DrawThread这个线程使用来绘制图片的,在这里我使用了LinkedList按照先进先出的原则读取图片,每读取一张图片就等待200毫秒,等待图片loading

class DrawThread extends Thread {public void run() {while (true) {Canvas canvas = mHolder.lockCanvas();if (canvas != null) {if(mBitmapList.size() > 0){                    Bitmap map = mBitmapList.removeFirst();canvas.drawBitmap(map, 0, 0, new Paint());
                                                //释放图片资源.map.recycle();//Log.e(TAG,"当前图片的数量为 =  " + mBitmapList.size());int size = mBitmapList.size();if (size < MAX) {try {this.join(200);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}synchronized (o) {o.notify();}}}mSurfaceView.getHolder().unlockCanvasAndPost(canvas);} else {Log.e(TAG,"Canvas is null");}}}}


以上个人见解,若有不足还请指正。







原创粉丝点击