李華明Himi 原創,轉載務必在明顯處註明:
轉載自【黑米GameDev街區】 原文連結: http://www.himigame.com/android-game/336.html
很多童鞋說My Code運行後,點擊home或者back後會程式異常,如果你也這樣遇到過,那麼你肯定沒有仔細讀完Himi的博文,第十九篇Himi專門寫了關於這些錯誤的原因和解決方案,這裡我在部落格都補充說明下,省的童鞋們總疑惑這一塊;請點擊下面聯絡進入閱讀:
【Android遊戲開發十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入後台等異常處理!
先上一段代碼大家來看一下:
package com.himi;<br />import android.app.Activity;<br />import android.os.Bundle;<br />import android.util.Log;<br />import android.view.MotionEvent;<br />import android.view.Window;<br />import android.view.WindowManager;<br />/**<br /> * @author Himi<br /> */<br />public class MainActivity extends Activity {<br />private Object object;<br />private final int TIME = 50;//備忘1<br />@Override<br />public void onCreate(Bundle savedInstanceState) {<br />super.onCreate(savedInstanceState);<br />getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);<br />this.requestWindowFeature(Window.FEATURE_NO_TITLE);<br />setContentView(R.layout.main);<br />object = new Object();<br />}<br />@Override<br />public boolean onTouchEvent(MotionEvent event) {<br />if (event.getAction() == MotionEvent.ACTION_DOWN) {<br />Log.v("Himi", "ACTION_DOWN");<br />} else if (event.getAction() == MotionEvent.ACTION_UP) {<br />Log.v("Himi", "ACTION_UP");<br />} else if (event.getAction() == MotionEvent.ACTION_MOVE) {<br />Log.v("Himi", "ACTION_MOVE");<br />}<br />synchronized (object) {//備忘2<br />try {<br />object.wait(TIME);<br />} catch (InterruptedException e) {<br />// TODO Auto-generated catch block<br />e.printStackTrace();<br />}<br />}<br />return true;//這裡一定要返回true!原因請看【Android2D遊戲開發之九】<br />}<br />}<br />
代碼很清晰呵呵,主要就是備忘2的地方:
一: 前言:
各位童鞋肯定都知道在模擬器中,我們的滑鼠當點擊一次模擬器螢幕然後釋放,先觸發 ACTION_DOWN 然後 ACTION_UP ;如果是在螢幕上移動那麼才會觸發 ACTION_MOVE 的動作;OK,很對。但是你要知道,這隻是模擬器!!!!!
二:真機與模擬器的區別:
當我們的小使用者(說到使用者我就想起“我叫MT”中的暗夜男那句經典台詞:親愛的客戶,我是嫩爹!)咳咳,回到話題;當我們的使用者在玩我們的遊戲的時候,尤其是RPG這種類型的,使用者肯定需要會長時間的去觸屏按我們的虛擬按鍵,比如我們會在螢幕上畫上一個虛擬方向盤類似這樣子~那麼其實 ACTION_MOVE 這個事件會被Android一直在響應!!
三: 為什麼會一直響應 ACTION_MOVE 這個動作呢? 如果使用者沒有移動手指而是靜止不動也會一直響應?
原因有兩點:第一點是因為,Android 對於觸屏事件很敏感!第二點:雖然我們的手指感覺是靜止沒有移動,其實事實不是如此!當我們的手指觸摸到手機螢幕上之後,感覺靜止沒動,其實手指在不停的微顫抖震動。不信你試試靜止下手指,是不是微微動?嘿嘿~
so~ 我們就要分析了,如果ACTION_MOVE此時間一直被Android os 一直不停的響應並處理,無疑對我們遊戲的效能增加了不少的負擔!
比如我們遊戲線程繪圖時間每次用了100ms,那麼當手指觸控螢幕幕,這短暫的0.1秒內大概會產生10個左右的MotionEvent ,並且系統會儘可能快的把這些event發給監聽線程, 這樣的話在這一段時間內cpu就會忙於處理onTouchEvent從而杯具點的話會造成畫面一卡一卡的。
那麼我們其實根本用不著按鍵響應這麼多次,而是需要在我們每次繪圖後,或者繪圖前接受一次使用者按鍵就OK了,這樣能讓幀率不至於下降的太厲害不是嗎?!so~我們要控制這個時間,讓他慢下來,隨著我們的繪圖時間一起來合作~這樣就能減不少系統線程的負擔。
備忘2:
可能有的童鞋會問為什麼不用sleep()的方法,其實如果我們只是想讓線程休眠指定時間的話可以用sleep()函數,但是這個沒有資源鎖的限制。而Object的wait,notify方法通常用在時間不定的條件限制等待,並且必須寫在同步代碼塊中。so~
還有童鞋會問,為什麼不用當前類的object來使用:this.wait(),而是new 一個object來:
synchronized 中的Object 表示Object 調用wait必須擁有該對象的監視鎖,當前我們有了object的鎖,就要用object調用wait~
備忘1:
這裡的變數大家都知道其實是我們設定的休眠的時間,那麼這裡我想拿出來跟大家說下關於這個時間的定值,在上文我也有說過我們的遊戲中只要按鍵跟我們的繪圖線程的時間一樣即可,當然這裡是個我們的理想時間!如果我們遊戲中有人物的幀,那麼我們可以來根據人物幀數來當成設定這個睡眠時間也是相當合適的,畢竟人物一幀說明邏輯執行了一遍了呵呵~這個還是根據大家遊戲的情況而定吧~
注意:Object.wait(long timeout)這個方法也需要慎用!
原因是因為測試發現:這個睡眠的時間其實比你規定的時間要略微的長一些,不過我們合理控制好時間還是沒問題的。
補充:
1.看到有童鞋問//備忘2這裡能不能用this,也就是當前的object,答案是可以的,但是要注意最好不要這樣用,原因是如果當其他地方也需要與當前的Object進行同步的話有可能出現死結情況!用一個新的object的原因也就是可以讓代碼中該幹什麼就幹什麼,互不影響,
2.//備忘2這裡其實我們可以對其最佳化,畢竟一個Object比較浪費,我們其實只需要一個位元組就足夠了,so~我們可以這樣定義一個:byte[] lock = new byte[0]; 這樣可以算是最優了~