切入後台操作比如點擊HOME按鍵,點擊返回按鍵...
那麼重新進入程式報異常主要Surfaceiew 有兩點會報異常:
第一:提交畫布異常!如(模擬器錯誤提示,以及Logcat Detail)
解決代碼:
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
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的類中的代碼:
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
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
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,這裡圖和解釋夠詳細了,希望大家以後真正開發一款遊戲的時候,一定要嚴謹代碼,不要留有後患哈~
Himi 原創, 歡迎轉載,轉載請在明顯處註明! 謝謝。
原文地址:http://blog.csdn.net/xiaominghimi/archive/2011/01/18/6149816.aspx
在這裡先向各位童鞋道個歉!我解釋下:當我在給大家講解的時候會附帶上源碼,可是這個源碼是示範代碼,為了讓大家看的清楚,所以我會儘可能把一些與其無關的刪掉,但是發現示範代碼還是被一些童鞋們效仿,導致不少童鞋問我為什麼程式執行後切入後台重新進入會報異常的問題!(這裡我就全面講解下運行機制,希望以後大家有類似問題自己就能解決了哈~)