HardBirch

Android提高第三篇之SurfaceView与多线程的混搭

时间:10-11-04 栏目:安卓入门与提高 作者:张飞不张,文采横飞 评论:56 点击: 15,044 次

       上一篇 简单介绍了SurfaceView的基本使用,这次就介绍SurfaceView与多线程的混搭。SurfaceView与多线程混搭,是为了防止动画闪烁而实现的一种多线程应用。android的多线程用法与JAVA的多线程用法完全一样,本文不做多线程方面的介绍了。直接讲解SurfaceView与多线程的混合使用,即开一条线程专门读取图片,另外一条线程专门绘图。

      本文程序运行截图如下,左边是开单个线程读取并绘图,右边是开两个线程,一个专门读取图片,一个专门绘图:

对比一下,右边动画的帧速明显比左边的快,左右两者都没使用Thread.sleep()。为什么要开两个线程一个读一个画,而不去开两个线程像左边那样都“边读边画”呢?因为SurfaceView每次绘图都会锁定Canvas,也就是说同一片区域这次没画完下次就不能画,因此要提高动画播放的效率,就得开一条线程专门画图,开另外一条线程做预处理的工作。

main.xml的源码:




<LinearLayout android:id="@+id/LinearLayout01"
android:layout_width="wrap_content" android:layout_height="wrap_content">
<Button android:id="@+id/Button01" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="单个独立线程"></Button>
<Button android:id="@+id/Button02" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="两个独立线程"></Button>
</LinearLayout>
<SurfaceView android:id="@+id/SurfaceView01"
android:layout_width="fill_parent" android:layout_height="fill_parent"></SurfaceView>
</LinearLayout>

本文程序的源码:

 

import java.lang.reflect.Field;
import java.util.ArrayList;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.Button;

public class testSurfaceView extends Activity {
/** Called when the activity is first created. */
Button btnSingleThread, btnDoubleThread;
SurfaceView sfv;
SurfaceHolder sfh;
ArrayList<Integer> imgList = new ArrayList<Integer>();
int imgWidth, imgHeight;
Bitmap bitmap;//独立线程读取,独立线程绘图

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

btnSingleThread = (Button) this.findViewById(R.id.Button01);
btnDoubleThread = (Button) this.findViewById(R.id.Button02);
btnSingleThread.setOnClickListener(new ClickEvent());
btnDoubleThread.setOnClickListener(new ClickEvent());
sfv = (SurfaceView) this.findViewById(R.id.SurfaceView01);
sfh = sfv.getHolder();
sfh.addCallback(new MyCallBack());// 自动运行surfaceCreated以及surfaceChanged
}

class ClickEvent implements View.OnClickListener {

@Override
public void onClick(View v) {

if (v == btnSingleThread) {
new Load_DrawImage(0, 0).start();//开一条线程读取并绘图
} else if (v == btnDoubleThread) {
new LoadImage().start();//开一条线程读取
new DrawImage(imgWidth + 10, 0).start();//开一条线程绘图
}

}

}

class MyCallBack implements SurfaceHolder.Callback {

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.i("Surface:", "Change");

}

@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("Surface:", "Create");

// 用反射机制来获取资源中的图片ID和尺寸
Field[] fields = R.drawable.class.getDeclaredFields();
for (Field field : fields) {
if (!"icon".equals(field.getName()))// 除了icon之外的图片
{
int index = 0;
try {
index = field.getInt(R.drawable.class);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 保存图片ID
imgList.add(index);
}
}
// 取得图像大小
Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
imgList.get(0));
imgWidth = bmImg.getWidth();
imgHeight = bmImg.getHeight();
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("Surface:", "Destroy");

}

}

/*
* 读取并显示图片的线程
*/
class Load_DrawImage extends Thread {
int x, y;
int imgIndex = 0;

public Load_DrawImage(int x, int y) {
this.x = x;
this.y = y;
}

public void run() {
while (true) {
Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
+ imgWidth, this.y + imgHeight));
Bitmap bmImg = BitmapFactory.decodeResource(getResources(),
imgList.get(imgIndex));
c.drawBitmap(bmImg, this.x, this.y, new Paint());
imgIndex++;
if (imgIndex == imgList.size())
imgIndex = 0;

sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
}
}
};

/*
* 只负责绘图的线程
*/
class DrawImage extends Thread {
int x, y;

public DrawImage(int x, int y) {
this.x = x;
this.y = y;
}

public void run() {
while (true) {
if (bitmap != null) {//如果图像有效
Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
+ imgWidth, this.y + imgHeight));

c.drawBitmap(bitmap, this.x, this.y, new Paint());

sfh.unlockCanvasAndPost(c);// 更新屏幕显示内容
}
}
}
};

/*
* 只负责读取图片的线程
*/
class LoadImage extends Thread {
int imgIndex = 0;

public void run() {
while (true) {
bitmap = BitmapFactory.decodeResource(getResources(),
imgList.get(imgIndex));
imgIndex++;
if (imgIndex == imgList.size())//如果到尽头则重新读取
imgIndex = 0;
}
}
};
}

声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: Android提高第三篇之SurfaceView与多线程的混搭

Android提高第三篇之SurfaceView与多线程的混搭:目前有56 条留言

  1. 0楼
    kf156:

    [e01]期待期待,越来越精彩了

    2010-11-04 11:34 [回复]
  2. [e01]顶GV.

    2010-11-04 11:36 [回复]
  3. 0楼
    hmc1985:

    [e01]学到了不少东西!更加期待后面的文章了!

    2010-11-04 11:36 [回复]
  4. 0楼
    JNU_kinke:

    顶,黄瓜帅到此一游

    2010-11-04 11:37 [回复]

  5. 流氓来袭

    2010-11-04 16:08 [回复]
  6. 0楼
    lurkerming:

    继续跟进,呵呵。[e03]

    2010-11-04 16:18 [回复]
  7. 0楼
    xunlei842:

    双缓冲,学习了,[e01]

    2010-11-05 09:26 [回复]
  8. 0楼
    huicaier:

    很好,正需要。

    2010-11-05 12:41 [回复]
  9. 0楼
    netpirate:

    [e10]
    线程一直不sleep,两套处理手法对性能有无太大差异?

    2010-11-05 13:11 [回复]
  10. 0楼
    high117:

    读线程和画线程分开了不叫双缓冲。
    双线程里面 bitmap 的同步都没做。

    2010-11-05 13:28 [回复]
  11. 0楼
    hellogv:

    回复 high117:
    写的时候还没验证过synchronized() 的效果,迟点试一下,谢谢提示。[e01]

    2010-11-05 13:54 [回复]
  12. 0楼
    hyx_lsxh:

    [e01]

    2010-11-05 14:50 [回复]
  13. [e01] 期待…

    2010-11-06 14:07 [回复]
  14. 0楼
    burellow:

    [e01]

    2010-11-06 14:50 [回复]
  15. 0楼
    chkvip:

    [e03][e03][e04]

    2010-11-06 17:08 [回复]
  16. 太棒了,学到了不少东西

    2010-11-07 00:28 [回复]
  17. 0楼
    snail82:

    写的不错[e01]

    2010-11-07 15:04 [回复]
  18. 回复 hellogv:好,但是确实没有看到双缓存

    2010-11-07 21:12 [回复]
  19. 好,但是确实没有看到双缓存

    2010-11-07 21:12 [回复]
  20. 0楼
    DaXiaLv:

    双缓冲是指开两块内存区然后交替着写数据和显示吧。

    2010-11-07 21:17 [回复]
  21. 0楼
    hellogv:

    回复 DaXiaLv:
    文中也解释了为什么不开两个线程交替,因为canvas会被锁定.

    2010-11-07 22:10 [回复]
  22. 0楼
    ngwsx:

    要实现双缓冲,新建一个Bitmap和Canvas,用这个新建的Canvas把正弦波画到新建的Bitmap,画完再通过sfh.lockCanvas获取SurfaceView对应的Canvas,用这个Canvas把新建的Bitmap画到SurfaceView上去,这才叫双缓冲吧?

    还有双缓存和多线程没什么关系吧?

    2010-11-11 22:42 [回复]
  23. 0楼
    ngwsx:

    博主这两篇SurfaceView的文章确实不错,[e01]!

    2010-11-11 22:44 [回复]
  24. 0楼
    pan168:

    这跟双缓冲好像没啥关系吧[e08],应该算是双线程绘图。

    2010-11-23 16:30 [回复]
  25. 0楼
    zk0301:

    楼主我要那几只猫的图片[e05]
    不然就给demo我[e04]

    2010-12-25 15:45 [回复]
  26. 0楼
    ffshow2006:

    有人运行了吗,我运行了下,图片并没有动啊

    2011-01-05 12:00 [回复]
  27. 0楼
    ffshow2006:

    回复 ffshow2006:我是直接拷贝的楼主的代码,一点也没有改,开始怀疑是不是gif太大了,弄了个小的也是不动,请楼主看看

    2011-01-05 12:02 [回复]
  28. 0楼
    hellogv:

    回复 ffshow2006:
    本来就是静态图片…本文是把静态图做成动画而已,你先看完代码吧

    2011-01-05 12:30 [回复]
  29. 0楼
    ffshow2006:

    回复 hellogv:恩,刚刚看完了,还以为是显示的GIF呢,不好意思,呵呵。

    2011-01-05 13:12 [回复]
  30. 0楼
    brook19:

    回复 ngwsx:我也赞同你的这个观点[e01]

    2011-01-10 13:32 [回复]
  31. 算我是2011回复的 确实跟双缓冲没有关系

    2011-01-12 14:56 [回复]
  32. 0楼
    lqz1988:

    回复 xuxiake2012:
    我也觉得不是,mfc里面也不是这样子啊

    2011-01-21 15:56 [回复]
  33. 这个有个办法比较快。。把所有动画图片都画到一个bitmap上,然后通过copy选择画图。。呵呵,不用每次解析了!

    2011-01-25 10:56 [回复]
  34. 大家也不用纠结于双缓存,只要够快就可以了,楼主有空加我Q191972323

    2011-01-25 10:57 [回复]
  35. 0楼
    lkfy524:

    ..SurfaceView本身就是双缓冲的,那些纠结于双缓冲的人无需想太多。楼主的方法还是不错的,不过确实应该同步一下bitmap吧

    2011-02-15 16:57 [回复]
  36. 回复 lkfy524:确实,
    Canvas c = sfh.lockCanvas(new Rect(this.x, this.y, this.x
    + imgWidth, this.y + imgHeight));
    c.drawBitmap(bitmap, this.x, this.y, new Paint());
    sfh.unlockCanvasAndPost(c);
    这句话就可以看出surfaceview本身就是双缓冲

    2011-02-17 10:24 [回复]
  37. 要是同步的话,至少要bitmap数组来保存图片吧,不如一个缓冲bitmap,同步和单线程的没有任何区别了,都是读完了才能显示,不能继续读取下一个图片

    2011-02-17 10:27 [回复]
  38. 0楼
    chrisghr:

    我是一只菜鸟,每天都在找寻菜虫。。。

    2011-03-18 19:26 [回复]
  39. 我是一棵菜菜,每天都在努力躲避菜虫。。。

    2011-03-28 14:47 [回复]
  40. 0楼
    lishunjin:

    回复 high117:双缓冲确实不是这样,是有两个画布实现。

    2011-04-13 16:41 [回复]
  41. 0楼
    raincl:

    好文好文顶起顶起。

    2011-04-14 17:22 [回复]
  42. Lz 我按你的方法写了另一个程序,但由于读取的线程速度远大于画图线程的速度,结果就是读取了n次才画一次图,是不是只能用同步了?

    2011-04-28 10:12 [回复]
  43. 0楼
    xrealx:

    请不要误导大众.这个不是双缓冲.
    为什么会比左边的速度快,可能你自己都没明白。
    双线程实现相当于是跳帧,装载速度大于绘制速度,某些图片没有进行绘制。而单线程实现,则每个图片都绘制。
    例:当装载完第3个图片时,才刚刚完成绘制图片1
    这时,绘制线程再次循环,则绘制的图片会是图片3,而图片2,则没进行绘制

    2011-05-05 15:31 [回复]
  44. 0楼
    hellogv:

    回复 xrealx:
    说得有道理,终于有人指出错误了,我迟点再改

    2011-05-05 15:46 [回复]
  45. 0楼
    pang68599:

    我不知道楼主…..进行连个线程读取的实际意义是什么…..在设计绘制界面的时候一般都会有一个loading界面,在loading的过程中会调用对界面进行数据初始化,初始化结束不就可以流畅的播放图片动画和声音了么?

    2011-05-07 10:58 [回复]
  46. 多线程对于一个任务的具体处理好像没有多大速度的提升吧

    2011-05-07 18:27 [回复]
  47. 多线程对多核好像还是有用的

    2011-05-07 19:16 [回复]
  48. 0楼
    ncite:

    回复 hellogv:
    没关系,其实你在这个基础上改一改就是传说中的三缓冲.

    2011-06-30 01:04 [回复]
  49. 0楼
    BIAOBIAOqi:

    看见有人解释跳帧~学到了。
    这个多线程竟然没有加锁啊。
    有点危险吧?

    2011-07-22 20:45 [回复]
  50. 0楼
    fangliquan:

    学到不少东西,根据你的原理 ,在surfaceview播放不同视频文件时可以吗?值得我去尝试 。谢谢楼主

    2011-07-27 20:30 [回复]
  51. 0楼
    allthesame:

    好东西,学习了~~

    2011-08-05 16:29 [回复]
  52. 0楼
    yjy951753:

    这个直接退出会导致错误,因为创建的线程并没有关闭。。。是不是应该给创建的线程一个名字,然后再Activity的生命周期destroy里先关闭之前创建的线程。。

    2011-08-10 15:39 [回复]
  53. new LoadImage().start();//开一条线程读取
    new DrawImage(imgWidth + 10, 0).start();//
    这样两个线程顺序性的执行,可以保证最后的效果是先读图片a,再显示图片a么?会不会因为堵塞的问题造成读取了b,才显示,结果显示的是b呢?是不是线程之间应该有所联系?

    2011-10-01 10:19 [回复]
  54. 0楼
    guomyth:

    感谢楼主的文章。
    if (v == btnSingleThread) {
    new Load_DrawImage(0, 0).start();
    } else if (v == btnDoubleThread) {
    new LoadImage().start();//开一条线程读取
    new DrawImage(imgWidth + 10, 0).start();
    }
    为什么Load_DrawImage和DrawImage的参数是不一样的,有什么差别呢?

    2011-12-09 16:24 [回复]
  55. 楼主的截屏工具是什么??
    求地址!

    2012-02-08 17:24 [回复]
  56. 楼主优化一下吧,并且如果我的图片是从摄像头抓取的话我怎么实现?

    2012-05-17 15:39 [回复]

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐