Android SurfaceView运行机制剖析--处理切换到后台再重新进入程序时的异常

有不少朋友都遇到过这种问题,程序执行时切换到后台,然后再重新进入会报异常,本文就这种问题全面讲解下SurfaceView的运行机制,了解了这些原理你就能自己解决这些问题了。

我们通常会通过单击HOME按键或返回按键等操作切换到后台,之后可能会再次进入程序,这个时候就有可能报异常。这里SurfaceView可能报的异常主要有两点,如下:

一、提交画布异常。如下图(模拟器错误提示,以及Logcat Detail)

Java代码

public void draw() { try { canvas = sfh.lockCanvas(); if (canvas != null) { canvas.drawColor(Color.WHITE); canvas.drawBitmap(bmp, bmp_x, bmp_y, paint); } } catch (Exception e) { Log.v("Himi", "draw is Error!"); } finally {//备注1 if (canvas != null)//备注2 sfh.unlockCanvasAndPost(canvas); } }

先看备注1这里,之前的文章中我给大家解释过为什么要把 sfh.unlockCanvasAndPost(canvas); 写在finally中,主要是为了保证能正常的提交画布。

今天主要说说备注2,这里一定要判定下canvas是否为空,因为当程序切入后台的时候,canvas是获取不到的!那么canvas一旦为空,提交画布这里就会出现参数异常的错误!

二、线程启动异常。如下图(模拟器错误提示,以及Logcat Detail)

这种异常只是在当你程序运行期间点击Home按钮后再次进入程序的时候报的异常,异常说咱们的线程已经启动!为什么返回按钮就没事?

OK,下面我们就要来先详细讲解一下Android中Back和Home按键的机制!然后分析问题,并且解决问题!

先看下面MySurfaceViewAnimation.java的类中的代码:

Java代码

public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable { private Thread th; private SurfaceHolder sfh; private Canvas canvas; private Paint paint; private Bitmap bmp; private int bmp_x, bmp_y; public MySurfaceViewAnimation(Context context) { super(context); this.setKeepScreenOn(true); bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream); sfh = this.getHolder(); sfh.addCallback(this); paint = new Paint(); paint.setAntiAlias(true); this.setLongClickable(true); th = new Thread(this, "himi_Thread_one"); Log.e("Himi", "MySurfaceViewAnimation"); } public void surfaceCreated(SurfaceHolder holder) { th.start(); Log.e("Himi", "surfaceCreated"); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e("Himi", "surfaceChanged"); } public void surfaceDestroyed(SurfaceHolder holder) { Log.e("Himi", "surfaceDestroyed"); } public void draw() { try { canvas = sfh.lockCanvas(); if (canvas != null) { canvas.drawColor(Color.WHITE); canvas.drawBitmap(bmp, bmp_x, bmp_y, paint); } } catch (Exception e) { Log.v("Himi", "draw is Error!"); } finally {//备注1 if (canvas != null)//备注2 sfh.unlockCanvasAndPost(canvas); } } public void run() { while (true) { draw(); try { Thread.sleep(100); } catch (Exception ex) { } } } }

以上是我们常用的自定义SurfaceView,并且使用Runnable接口老框架了不多说了,其中我在本类的构造、创建、状态改变、消亡函数都加上打印!

OK,下面看第一张图:(刚运行程序)

上图的左边部分是Dubug。这里显示我们有一条线程在运行,名字叫”himi_Thread_one”。

上图的右边部分是LogCat日志。大家很清晰的看到,当第一次进入程序的时候,会先进入view构造函数、然后是创建view,然后是view状态改变,OK,这个大家都知道!

下面是我来点击Home(手机上的小房子)按键,这时程序处于后台,然后重新进入程序的过程!

上图可以看出我们的线程还是一条,这里主要观察从点击home到再次进入程序的过程,如下所述:

点击home 调用了view销毁,然后进入程序会先进入view创建,最后是view状态改变。

上面的过程很容易理解,重要的角色上场了~Back 按钮!点我点击Back按钮看看发生了什么!

先看左边的Debug一栏,多了一条线程! 看LogCat发现比点击Home按键多调用了一次构造函数!

好了,从我们测试的程序来看,无疑,点击Home 和 点击 Back按钮再次进入程序的时候,步骤是不一样的,线程数量也变了!

那么这里就能解释为什么我们点击Back按钮不异常,点击Home会异常了!

原因:因为点击Back按钮再次进入程序的时候先进入的是view构造函数里,那么就是说这里又new了一个线程出来,并启动!那么而我们点击Home却不一样了,因为点击home之后再次进入程序不会进入构造函数,而是直接进入了view创建这个函数,而在view创建这个函数中我们有个启动线程的操作,其实第一次启动程序的线程还在运行,so~这里就一定异常了,说线程已经启动!

有些童鞋会问,我们为何不把th = new Thread(this, “himi_Thread_one”);放在view创建函数中不就好了?!

没错,可以!但是当你反复几次之后你发现你的程序中会多出很多条进程!(如下图)

虽然可以避免出现线程已经启动的异常,很明显这不是我们想要的结果!

那么下面给大家介绍最合适的解决方案:

修改MySurfaceViewAnimation.java:

Java代码

public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable { private Thread th; private SurfaceHolder sfh; private Canvas canvas; private Paint paint; private Bitmap bmp; private int bmp_x, bmp_y; private boolean himi; //备注1 public MySurfaceViewAnimation(Context context) { super(context); this.setKeepScreenOn(true); bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream); sfh = this.getHolder(); sfh.addCallback(this); paint = new Paint(); paint.setAntiAlias(true); this.setLongClickable(true); Log.e("Himi", "MySurfaceViewAnimation"); } public void surfaceCreated(SurfaceHolder holder) { himi = true; th = new Thread(this, "himi_Thread_one");//备注2 th.start(); Log.e("Himi", "surfaceCreated"); } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.e("Himi", "surfaceChanged"); } public void surfaceDestroyed(SurfaceHolder holder) { himi = false;//备注3 Log.e("Himi", "surfaceDestroyed"); } public void draw() { try { canvas = sfh.lockCanvas(); if (canvas != null) { canvas.drawColor(Color.WHITE); canvas.drawBitmap(bmp, bmp_x, bmp_y, paint); } } catch (Exception e) { Log.v("Himi", "draw is Error!"); } finally { if (canvas != null) sfh.unlockCanvasAndPost(canvas); } } public void run() { while (himi) {//备注4 draw(); try { Thread.sleep(100); } catch (Exception ex) { } } } }

这里修改的地方有以下几点:

1、我们都知道一个线程启动后,只要run方法执行结束,线程就销毁了,所以我增加了一个布尔值的成员变量 himi(备注1),这里可以控制我们的线程消亡的一个开关!(备注4)

2、在启动线程之前,设置这个布尔值为ture,让线程一直运行。

3、在view销毁时,设置这个布尔值为false,销毁当前线程!(备注3)

OK,这里图和解释够详细了,希望大家以后真正开发一款游戏的时候,一定要严谨代码,不要留有后患哈~

以上就对Android SurfaceView运行机制详细介绍,后续继续补充相关知识,谢谢大家对本站的支持!

时间: 2024-08-20 22:53:27

Android SurfaceView运行机制剖析--处理切换到后台再重新进入程序时的异常的相关文章

Android SurfaceView运行机制详解—剖析Back与Home按键及切入后台等异常处理!

http://blog.csdn.net/xiaominghimi/article/details/6149816            切入后台操作比如点击HOME按键,点击返回按键...      那么重新进入程序报异常主要Surfaceiew 有两点会报异常:   第一:提交画布异常!如下图(模拟器错误提示,以及Logcat Detail)     解决代码:   [java] view plaincopy public void draw() {           try {     

【ANDROID游戏开发十九】(必看篇)SURFACEVIEW运行机制详解—剖析BACK与HOME按键及切入后台等异常处理!

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/346.html 在这里先向各位童鞋道个歉!我解释下:当我在给大家讲解的时候会附带上源码,可是这个源码是演示代码,为了让大家看的清楚,所以我会尽可能把一些与其无关的删掉,但是发现演示代码还是被一些童鞋们效仿,导致不少童鞋问我为什么程序执行后切入后台重新进入会报异常的问题!(这里我就全面讲解下运行机制,希望以后大家有类

Android使用缓存机制实现文件下载及异步请求图片加三级缓存_Android

首先给大家介绍Android使用缓存机制实现文件下载 在下载文件或者在线浏览文件时,或者为了保证文件下载的正确性,需要使用缓存机制,常使用SoftReference来实现. SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收.也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用.另外

PHP的运行机制与原理(底层)_php实例

说到php的运行机制还要先给大家介绍php的模块,PHP总共有三个模块:内核.Zend引擎.以及扩展层:PHP内核用来处理请求.文件流.错误处理等相关操作:Zend引擎(ZE)用以将源文件转换成机器语言,然后在虚拟机上运行它:扩展层是一组函数.类库和流,PHP使用它们来执行一些特定的操作.比如,我们需要mysql扩展来连接MySQL数据库:当ZE执行程序时可能会需要连接若干扩展,这时ZE将控制权交给扩展,等处理完特定任务后再返还: 最后,ZE将程序运行结果返回给PHP内核,它再将结果传送给SAP

Android 安全架构及权限控制机制剖析

Android 层次化安全架构 Android 作为一个移动设备的平台,其软件层次结构包括了一个操作系统(OS),中间件(MiddleWare)和应用程序(Application).根据 Android 的软件框图,其软件层次结构自下而上分为以下几个层次: 操作系统层(OS) 各种库(Libraries)和 Android 运行环境(RunTime) 应用程序框架(Application Framework) 应用程序(Application) 以下分别介绍 Andoid 各个层次的软件的重点及

深入理解Android Instant Run运行机制

Instant Run Instant Run,是android studio2.0新增的一个运行机制,在你编码开发.测试或debug的时候,它都能显著减少你对当前应用的构建和部署的时间.通俗的解释就是,当你在Android Studio中改了你的代码,Instant Run可以很快的让你看到你修改的效果.而在没有Instant Run之前,你的一个小小的修改,都肯能需要几十秒甚至更长的等待才能看到修改后的效果. 传统的代码修改及编译部署流程 传统的代码修改及编译流程如下:构建整个apk → 部

解析Android应用程序运行机制_Android

在标准的Windows和Linux桌面操作系统中,同时可以在不同的窗口中运行多个应用程序,每次只有一个应用程序是当前焦点状态,但其他的应用程序都是一个平等的位置.用户可以随时切换每个应用程序,在不需要应用程序时,也需要用户来关闭应用程序.     但是Android操作系统的应用程序却不是采用这种方式.     Android中每次只有一个应用程序运行在最前面,除了状态栏的位置,当前应用程序将铺满整个屏幕.用户最常见的应用程序就是应用的主页(Home application),这个程序主要显示背

解析Android应用程序运行机制

在标准的Windows和Linux桌面操作系统中,同时可以在不同的窗口中运行多个应用程序,每次只有一个应用程序是当前焦点状态,但其他的应用程序都是一个平等的位置.用户可以随时切换每个应用程序,在不需要应用程序时,也需要用户来关闭应用程序. 但是Android操作系统的应用程序却不是采用这种方式. Android中每次只有一个应用程序运行在最前面,除了状态栏的位置,当前应用程序将铺满整个屏幕.用户最常见的应用程序就是应用的主页(Home application),这个程序主要显示背景图和应用程序快

android SurfaceView绘制实现原理解析

在Android系统中,有一种特殊的视图,称为SurfaceView,它拥有独立的绘图表面,即它不与其宿主窗口共享同一个绘图表面.由于拥有独立的绘图表面,因此SurfaceView的UI就可以在一个独立的线程中进行绘制.又由于不会占用主线程资源,SurfaceView一方面可以实现复杂而高效的UI,另一方面又不会导致用户输入得不到及时响应.在本文中,我们就详细分析SurfaceView的实现原理.         在前面Android控件TextView的实现原理分析一文中提到,普通的Andro