Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發實例 >> 【Android游戲開發十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入後台等異常處理!

【Android游戲開發十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入後台等異常處理!

編輯:Android開發實例

      在這裡先向各位童鞋道個歉!我解釋下:當我在給大家講解的時候會附帶上源碼,可是這個源碼是演示代碼,為了讓大家看的清楚,所以我會盡可能把一些與其無關的刪掉,但是發現演示代碼還是被一些童鞋們效仿,導致不少童鞋問我為什麼程序執行後切入後台重新進入會報異常的問題!(這裡我就全面講解下運行機制,希望以後大家有類似問題自己就能解決了哈~)

                                                    切入後台操作比如點擊HOME按鍵,點擊返回按鍵...

 

   那麼重新進入程序報異常主要Surfaceiew 有兩點會報異常:

 

第一:提交畫布異常!如下圖(模擬器錯誤提示,以及Logcat Detail)

 

 

解決代碼:

 

 

  1. public void draw() {  
  2.         try {  
  3.             canvas = sfh.lockCanvas();  
  4.             if (canvas != null) {  
  5.                 canvas.drawColor(Color.WHITE);  
  6.                 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint);  
  7.             }  
  8.         } catch (Exception e) {  
  9.             Log.v("Himi", "draw is Error!");  
  10.         } finally {//備注1  
  11.             if (canvas != null)//備注2  
  12.                 sfh.unlockCanvasAndPost(canvas);  
  13.         }  
  14.     } 

 

先看備注1這裡,之前的文章中我給大家解釋過為什麼要把 sfh.unlockCanvasAndPost(canvas); 寫在finally中,主要是為了保證能正常的提交畫

布.今天主要說說備注2這裡一定要判定下canvas是否為空,因為當程序切入後台的時候,canvas是獲取不到的!那麼canvas一旦為空,提交畫

布這裡就會出現參數異常的錯誤!

 

 

下面來說另外一種情況:線程啟動異常!如下圖(模擬器錯誤提示,以及Logcat Detail)

 

這種異常只是在當你程序運行期間點擊Home按鈕後再次進入程序的時候報的異常,異常說咱們的線程已經啟動!為什麼返回按鈕就沒事?

 

OK,下面我們就要來先詳細講解一下Android中Back和Home按鍵的機制!然後分析問題,並且解決問題!

 

先看下面MySurfaceViewAnimation.java的類中的代碼:

 

  1. public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable {  
  2.     private Thread th;  
  3.     private SurfaceHolder sfh;  
  4.     private Canvas canvas;  
  5.     private Paint paint;  
  6.     private Bitmap bmp;  
  7.     private int bmp_x, bmp_y;  
  8.     public MySurfaceViewAnimation(Context context) {  
  9.         super(context);  
  10.         this.setKeepScreenOn(true);  
  11.         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream);  
  12.         sfh = this.getHolder();  
  13.         sfh.addCallback(this);  
  14.         paint = new Paint();  
  15.         paint.setAntiAlias(true);  
  16.         this.setLongClickable(true);  
  17.         th = new Thread(this, "himi_Thread_one");  
  18.         Log.e("Himi", "MySurfaceViewAnimation");  
  19.     }  
  20.     public void surfaceCreated(SurfaceHolder holder) {  
  21.         th.start();  
  22.         Log.e("Himi", "surfaceCreated");  
  23.     }  
  24.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {  
  25.         Log.e("Himi", "surfaceChanged");  
  26.     }  
  27.     public void surfaceDestroyed(SurfaceHolder holder) {  
  28.         Log.e("Himi", "surfaceDestroyed");  
  29.     }  
  30.     public void draw() {  
  31.         try {  
  32.             canvas = sfh.lockCanvas();  
  33.             if (canvas != null) {  
  34.                 canvas.drawColor(Color.WHITE);  
  35.                 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint);  
  36.             }  
  37.         } catch (Exception e) {  
  38.             Log.v("Himi", "draw is Error!");  
  39.         } finally {//備注1  
  40.             if (canvas != null)//備注2  
  41.                 sfh.unlockCanvasAndPost(canvas);  
  42.         }  
  43.     }  
  44.     public void run() {  
  45.         while (true) {  
  46.             draw();  
  47.             try {  
  48.                 Thread.sleep(100);  
  49.             } catch (Exception ex) {  
  50.             }  
  51.         }  
  52.     }  

 

 

以上是我們常用的自定義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

 

 

  1. public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable {    
  2.     private Thread th;    
  3.     private SurfaceHolder sfh;    
  4.     private Canvas canvas;    
  5.     private Paint paint;    
  6.     private Bitmap bmp;    
  7.     private int bmp_x, bmp_y;    
  8.     private boolean himi; //備注1    
  9.     public MySurfaceViewAnimation(Context context) {    
  10.         super(context);    
  11.         this.setKeepScreenOn(true);    
  12.         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream);    
  13.         sfh = this.getHolder();    
  14.         sfh.addCallback(this);    
  15.         paint = new Paint();    
  16.         paint.setAntiAlias(true);    
  17.         this.setLongClickable(true);    
  18.         Log.e("Himi", "MySurfaceViewAnimation");    
  19.     }    
  20.     public void surfaceCreated(SurfaceHolder holder) {    
  21.         himi = true;    
  22.         th = new Thread(this, "himi_Thread_one");//備注2    
  23.         th.start();    
  24.         Log.e("Himi", "surfaceCreated");    
  25.     }    
  26.     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {    
  27.         Log.e("Himi", "surfaceChanged");    
  28.     }    
  29.     public void surfaceDestroyed(SurfaceHolder holder) {    
  30.         himi = false;//備注3    
  31.         Log.e("Himi", "surfaceDestroyed");    
  32.     }    
  33.     public void draw() {    
  34.         try {    
  35.             canvas = sfh.lockCanvas();    
  36.             if (canvas != null) {    
  37.                 canvas.drawColor(Color.WHITE);    
  38.                 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint);    
  39.             }    
  40.         } catch (Exception e) {    
  41.             Log.v("Himi", "draw is Error!");    
  42.         } finally {    
  43.             if (canvas != null)    
  44.                 sfh.unlockCanvasAndPost(canvas);    
  45.         }    
  46.     }    
  47.     public void run() {    
  48.         while (himi) {//備注4    
  49.             draw();    
  50.             try {    
  51.                 Thread.sleep(100);    
  52.             } catch (Exception ex) {    
  53.             }    
  54.         }    
  55.     }    
  56. }   

 

 

 

這裡修改的地方有以下幾點:

 

       1. 我們都知道一個線程啟動後,只要run方法執行結束,線程就銷毀了,所以我增加了一個布爾值的成員變量 himi(備注1),這裡可以控制我們的線程消亡的一個開關!(備注4

       2.在啟動線程之前,設置這個布爾值為ture,讓線程一直運行.

       3.在view銷毀時,設置這個布爾值為false,銷毀當前線程!(備注3

 

     OK,這裡圖和解釋夠詳細了,希望大家以後真正開發一款游戲的時候,一定要嚴謹代碼,不要留有後患哈~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved