文档章节

android 使用双缓冲辨析及surfaceview使用例程-转载(觉得写的很清楚)

LiangX
 LiangX
发布于 2013/05/28 21:26
字数 1710
阅读 777
收藏 16

本文转自:http://blog.csdn.net/blogercn/article/details/7404485

感谢作者分享!

      双缓冲是图像编程中很重要的概念,在电脑的图像处理中就常常使用双缓冲来加快图像显示速度,消除图像刷新时的闪烁现象,提升用户体验。双缓冲为图像加速,提升显示速度,提高显示质量的原理是:计算机访问显示屏和磁盘的速度远远小于CPU缓存和内存,每一次调用绘图函数往显示屏刷新数据,即使你的显示内容已经加载到了内存,但每一次访问显示屏,仍然会花费比内存大得多的时间,如果你的资源里有一百个图片,那么直接把他们全刷到屏幕上需要调用一百次drawBitmap方法,每次都需要访问显示屏,就会导致显示过程效率低下。这时如果使用双缓冲,创建一块屏幕客户区大小的BUFFER,把这一百个图片全画到BUFFER上,只就需要访问一百次内存,最后访问一次显示屏,把这块BUFFER刷到显示屏上就行了,如果内存与外设的速度比是一比十,那么几乎可以节约十分之九的时间,速度当然快了,这就是双缓冲技术。

那么安卓编程是否需要双缓冲?安卓surfaceview和双缓冲有什么关系?

下面通过三个例子说明,三个例子内容一样,都是一个背景图片,5*8个不停放缩的实心圆:

1、不使用双缓冲,直接使用onDraw往屏幕刷数据。代码如下:

package com.Double;

import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;

public class TestDoubleActivity extends Activity {
    MyView mv;
    float m_circle_r = 10;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        mv = new MyView(this);
        setContentView(mv);
        
        Timer timer = new Timer();  
        timer.scheduleAtFixedRate(new MyTask(), 1, 100);  
    }
    public class MyView extends View {
        MyView(Context context) {
            super(context);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
            super.onDraw(canvas);
            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.BLUE);
            paint.setStrokeWidth(10);
            paint.setStyle(Style.FILL);
            if (m_circle_r >= (getWidth()/10))
            {
                m_circle_r = 0;
            }
            else
            {
                m_circle_r++;
            }
            Bitmap pic = ((BitmapDrawable) getResources().getDrawable(R.drawable.qq)).getBitmap();
            canvas.drawBitmap(pic, 0, 0, paint); 
            for (int i = 0; i < 5; i++)
                for (int j = 0; j < 8; j++)
            canvas.drawCircle((getWidth()/5)*i+(getWidth()/10), (getHeight()/8)*j+(getHeight()/16), m_circle_r, 

paint);
        }

    }
    private class MyTask extends TimerTask{  
        @Override  
        public void run() {  
            mv.postInvalidate();
        }     
    }  
}

2、使用双缓冲,同样的代码,创建一块和屏幕大小一样的Bitmap,把需要画的内存提前画在一个tmp的Bitmap上,最后把tmp刷到屏上,代码如下:

package com.myDouble;

import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import com.myDouble.MySurfaceView;

public class DoubleActivity extends Activity {
	MyView mv;
	float m_circle_r = 10;
	Bitmap tmp;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// setContentView(R.layout.main);
	
		mv = new MyView(this);
		setContentView(mv);

		Timer timer = new Timer();
		timer.scheduleAtFixedRate(new MyTask(), 1, 100);
		
	}

	public class MyView extends View {
		MyView(Context context) {
			super(context);
		}

		@Override
		protected void onDraw(Canvas canvas) {
			// TODO Auto-generated method stub
			super.onDraw(canvas);
			Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
			paint.setColor(Color.BLUE);
			paint.setStrokeWidth(10);
			paint.setStyle(Style.FILL);
			if (m_circle_r >= (getWidth() / 10)) {
				m_circle_r = 0;
			} else {
				m_circle_r++;
			}
			/* 创建Canvas对象 */
			Canvas mCanvas = new Canvas();
			/* 创建屏幕大小的缓冲区 tmp*/
			tmp = Bitmap
					.createBitmap(getWidth(), getHeight(), Config.ARGB_8888);
			/* 设置将内容绘制在tmp上 */
			mCanvas.setBitmap(tmp);
            //pic 在tmp上
			Bitmap pic = ((BitmapDrawable) getResources().getDrawable(
					R.drawable.qq)).getBitmap();
			mCanvas.drawBitmap(pic, 0, 0, paint);
			//把5*8个圆绘制在tmp上
			for (int i = 0; i < 5; i++)
				for (int j = 0; j < 8; j++)
					mCanvas.drawCircle(
							(getWidth() / 5) * i + (getWidth() / 10),
							(getHeight() / 8) * j + (getHeight() / 16),
							m_circle_r, paint);
			//把tmp绘制在物理设备上
			canvas.drawBitmap(tmp, 0, 0, paint);
		}
	}

	private class MyTask extends TimerTask {
		@Override
		public void run() {
			mv.postInvalidate();
		}
	}
}

      两个例子对比后发现,如果这些图形使用VC编写,不使用双缓冲动画会闪得很厉害,使用双缓冲动画就会很流畅。而在安卓上,不使用双缓冲图像也很顺畅,而使用了双缓冲后,动画反而比不使用更卡,根据结果的推测,应该是安卓平台onDraw已经经过了双缓冲优化,而再次使用双缓冲时,反而不仅仅增大了系统内存开销,而且比原来多使用了一次图像拷贝操作,是而效率反而低了不少。再看下面使用surface的例子:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder.Callback;

public class MySurfaceView extends SurfaceView implements Runnable, Callback {
	private SurfaceHolder mHolder; // 用于控制SurfaceView
	private Thread t; // 声明一条线程
	private volatile boolean flag; // 线程运行的标识,用于控制线程
	private Canvas mCanvas; // 声明一张画布
	private Paint p; // 声明一支画笔
	float m_circle_r = 10;

	public MySurfaceView(Context context) {
		super(context);

		mHolder = getHolder(); // 获得SurfaceHolder对象
		mHolder.addCallback(this); // 为SurfaceView添加状态监听
		p = new Paint(); // 创建一个画笔对象
		p.setColor(Color.WHITE); // 设置画笔的颜色为白色
		setFocusable(true); // 设置焦点
	}

	/**
	 * 当SurfaceView创建的时候,调用此函数
	 */
	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		t = new Thread(this); // 创建一个线程对象
		flag = true; // 把线程运行的标识设置成true
		t.start(); // 启动线程
	}

	/**
	 * 当SurfaceView的视图发生改变的时候,调用此函数
	 */
	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}

	/**
	 * 当SurfaceView销毁的时候,调用此函数
	 */
	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		flag = false; // 把线程运行的标识设置成false
		mHolder.removeCallback(this);
	}

	/**
	 * 当屏幕被触摸时调用
	 */
	@Override
	public boolean onTouchEvent(MotionEvent event) {

		return true;
	}

	/**
	 * 当用户按键时调用
	 */
	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
		}
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public boolean onKeyUp(int keyCode, KeyEvent event) {
		surfaceDestroyed(mHolder);
		return super.onKeyDown(keyCode, event);
	}

	@Override
	public void run() {
		while (flag) {
			try {
				synchronized (mHolder) {
					Thread.sleep(100); // 让线程休息100毫秒
					Draw(); // 调用自定义画画方法
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			} finally {
				if (mCanvas != null) {
					// mHolder.unlockCanvasAndPost(mCanvas);//结束锁定画图,并提交改变。

				}
			}
		}
	}

	/**
	 * 自定义一个方法,在画布上画一个圆
	 */
	protected void Draw() {
		mCanvas = mHolder.lockCanvas(); // 获得画布对象,开始对画布画画
		if (mCanvas != null) {
			Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
			paint.setColor(Color.BLUE);
			paint.setStrokeWidth(10);
			paint.setStyle(Style.FILL);
			if (m_circle_r >= (getWidth() / 10)) {
				m_circle_r = 0;
			} else {
				m_circle_r++;
			}
			Bitmap pic = ((BitmapDrawable) getResources().getDrawable(
					R.drawable.qq)).getBitmap();
			mCanvas.drawBitmap(pic, 0, 0, paint);
			for (int i = 0; i < 5; i++)
				for (int j = 0; j < 8; j++)
					mCanvas.drawCircle(
							(getWidth() / 5) * i + (getWidth() / 10),
							(getHeight() / 8) * j + (getHeight() / 16),
							m_circle_r, paint);
			mHolder.unlockCanvasAndPost(mCanvas); // 完成画画,把画布显示在屏幕上
		}
	}
}
      这个例子我们把绘图工作放到了线程t里,在线程t里调用了我们自己的非重载函数Draw。一般我们都会使用这种方法更新用户UI。因为如果把UI放在系统线程,如果遇到阻塞超过5S,软件就会被系统干掉。所以,我们在安卓系统上,不需要再考虑双缓冲,系统已经实现了双缓冲,再次调用,反而会导致效率更进一步被压缩。如果确实需要提升显示效果,就使用surfaceview吧,如果仍然不行,就试着使用C,C++来实现这些复杂处理吧。程序效果图如下:

本文转载自:http://blog.csdn.net/blogercn/article/details/7404485

共有 人打赏支持
LiangX
粉丝 22
博文 33
码字总数 12468
作品 0
朝阳
程序员
私信 提问
【Android游戏开发之二】剖析游戏开发用view还是surfaceView ?!

李华明Himi 原创,转载务必在明显处注明: 转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/android-game/295.html 很多童鞋说我的代码运行后,点击home或者back后会程序异常,...

迷途d书童
2012/03/19
0
0
Android音视频开发之SurfaceView的使用

由于公司业务要用到音视频方面的知识,所以我打算学习一下 Android 音视频开发。在网上搜索资料和教程,发现系统化的比较少,大多讲得比较零散。Jhuster 的博客 Android 音视频开发入门指南 ...

落英坠露
2018/10/28
0
0
【Android游戏开发十四】深入Animation,在SurfaceView中照样使用Android—Tween Animation!

李华明Himi 原创,转载务必在明显处注明: 转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/android-game/331.html 很多童鞋说我的代码运行后,点击home或者back后会程序异常,...

迷途d书童
2012/03/19
0
0
Android-SurfaceView与SurfaceHolder对象

1、Android-SurfaceView与SurfaceHolder对象: http://blog.csdn.net/andyhuabing/article/details/7657069 2、Android学习之 VideoView,SurfaceView: http://blog.csdn.net/abidepan/arti......

当空皓月
2014/12/18
0
0
Android中UI(View)的刷新

看了很多资料,翻啊翻啊,似乎有些了解了。 Android中对View的更新有很多种方式,使用时要区分不同的应用场合。我感觉最要紧的是分清:多线程和双缓冲的使用情况。 现在可以尝试理解下面的模...

DreamWorker
2010/10/30
0
4

没有更多内容

加载失败,请刷新页面

加载更多

开始看《ES6标准入门》

写得非常好,全是干货。还以为是很容易上手的,结果一看还是要有一些JS基础才行的 1.ES6简介。可以了解一下,特别是ES6和ES2015的关系 2.Let和Const命令。这个感觉是帮JS打补丁了。之前的Var...

max佩恩
25分钟前
1
0
SpringBoot入门系列HelloWorld

根据咱们程序员学习的惯例,学习一门新技术都是从HelloWorld开始的。 感觉编程是一件非常富有意义的事情,程序员也是一群可爱的人,渴望被关怀和关注,因为我们总在和世界say Hi. 好了进入正...

凌宇之蓝
今天
4
0
Linux之《荒岛余生》(二)CPU篇

温馨提示,动图已压缩,流量党放心查看。CPU方面内容不多,我们顺便学点命令。本篇是《荒岛余生》系列第二篇,垂直观测CPU。其余参见: Linux之《荒岛余生》(一)准备篇 如何做一个CPU cpu...

mskk
今天
1
0
Java基础教程,第十讲,继承

前面我们学习了类和对象的概念,学习了方法的重载,今天我们将学习面向对象另一个重要特性,继承(inheritance)。利用继承,我们可以基于一个已经存在的类狗仔一个新的类,继承已经存在的类...

程序员补给栈
今天
2
0
nginx的日志

nginx的日志包括错误日志和访问日志,分别使用不同的指令来定义其输出位置和相应的级别。 下面介绍其各自的用途。 错误日志 nginx提供了error_log指令来指定错误日志的输出文件和级别。 指令...

xtof
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部