同类型游戏 生命周期 讲解的博客还有 : 从零开始Android游戏编程(第二版) 第九章 游戏程序的生命周期
书本上, 网络上android游戏框架比比皆是, 但是都未深入其生命周期, 下面只适用于 单机版,网络版好像不适用
以下是我选取的基于servaceView的android游戏框架,
Activity
1 GameActivity.java 2 package air.frame; 3 4 import android.app.Activity; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.Menu; 8 import android.view.MenuItem; 9 import android.widget.TextView; 10 11 publicclass GameActivity extends Activity { 12 private GameSurfaceView gameSurfaceView; 13 private GameThread gameThread; 14 publicstaticfinal String TAG ="GameFrame"; 15 16 @Override 17 publicvoid onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.main); 20 gameSurfaceView = (GameSurfaceView) this.findViewById(R.id.gameview); 21 gameThread = gameSurfaceView.getThread(); 22 gameSurfaceView.setTextView((TextView) findViewById(R.id.textview)); 23 24 Log.d(TAG, "onCreate"); 25 26 if (savedInstanceState ==null) { 27 // 游戏第一次启动时,初始化游戏状态 28 gameThread.doStart(); 29 Log.d(TAG, "SIS is null"); 30 } else { 31 // 从其他应用界面切回游戏时,如果Activity重新创建,则恢复上次切出游戏时的各项数据 32 gameThread.restoreState(savedInstanceState); 33 Log.d(TAG, "SIS is not null"); 34 } 35 } 36 37 /** 38 * 当Activity被切换到后台时调用,存储Activity重新创建时需要恢复的游戏数据 39 */ 40 @Override 41 protectedvoid onSaveInstanceState(Bundle outState) { 42 super.onSaveInstanceState(outState); 43 gameThread.saveState(outState); 44 Log.d(TAG, "SIS called"); 45 } 46 47 /** 48 * 当Activity被切换到后台时调用,在这里对游戏做"暂停"的处理 49 */ 50 @Override 51 protectedvoid onPause() { 52 super.onPause(); 53 // 暂停游戏 54 Log.d(TAG, "onPause"); 55 gameSurfaceView.getThread().pause(); 56 } 57 58 /** 59 * 当Activity切换到前台时调用 60 */ 61 @Override 62 protectedvoid onResume() { 63 super.onResume(); 64 // 游戏结束暂停状态,游戏正常进行 65 Log.d(TAG, "onResume"); 66 gameSurfaceView.getThread().unpause(); 67 } 68 69 /** 70 * 创建游戏菜单 71 */ 72 @Override 73 publicboolean onCreateOptionsMenu(Menu menu) { 74 // TODO Auto-generated method stub 75 Log.d(TAG, "onCreateOptionsMenu"); 76 returnsuper.onCreateOptionsMenu(menu); 77 } 78 79 /** 80 * 定义游戏菜单的点击事件处理 81 */ 82 @Override 83 publicboolean onOptionsItemSelected(MenuItem item) { 84 // TODO Auto-generated method stub 85 Log.d(TAG, "onOptionsItemSelected"); 86 returnsuper.onOptionsItemSelected(item); 87 } 88 89 @Override 90 protectedvoid onRestart() { 91 Log.d(TAG, "onRestart"); 92 super.onRestart(); 93 } 94 95 @Override 96 protectedvoid onRestoreInstanceState(Bundle savedInstanceState) { 97 Log.d(TAG, "onRestoreInstanceState"); 98 super.onRestoreInstanceState(savedInstanceState); 99 } 100 101 @Override 102 protectedvoid onStart() { 103 Log.d(TAG, "onStart"); 104 super.onStart(); 105 } 106 107 @Override 108 protectedvoid onStop() { 109 Log.d(TAG, "onStop"); 110 super.onStop(); 111 } 112 113 @Override 114 protectedvoid onDestroy() { 115 super.onDestroy(); 116 Log.d(TAG, "onDestroy"); 117 118 // 停止游戏 119 gameThread.setRunning(false); 120 boolean retry =true; 121 while (retry) { 122 try { 123 // 阻塞Activity的主线程直到游戏线程执行完 124 gameThread.join(); 125 retry =false; 126 } catch (InterruptedException e) { 127 } 128 } 129 } 130 }
surfaceView
1 GameSurfaceView.java 2 package air.frame; 3 4 import android.content.Context; 5 import android.os.Handler; 6 import android.os.Message; 7 import android.util.AttributeSet; 8 import android.util.Log; 9 import android.view.KeyEvent; 10 import android.view.SurfaceHolder; 11 import android.view.SurfaceView; 12 import android.view.SurfaceHolder.Callback; 13 import android.widget.TextView; 14 15 publicclass GameSurfaceView extends SurfaceView implements Callback { 16 private GameThread gameThread; 17 private TextView textview; 18 19 final String TAG = GameActivity.TAG; 20 21 public GameSurfaceView(Context context, AttributeSet attrs) { 22 super(context, attrs); 23 SurfaceHolder holder = getHolder(); 24 holder.addCallback(this); 25 gameThread =new GameThread(holder, context, new Handler() { 26 @Override 27 publicvoid handleMessage(Message m) { 28 textview.setText(m.getData().getString("text")); 29 } 30 }); 31 // 设置可获得焦点,确保能捕获到KeyEvent 32 setFocusable(true); 33 } 34 35 /** 36 * 获取一个Activity传来的View协助SurfaceView显示游戏视图,View的具体类型可以根据游戏需要来定 37 */ 38 publicvoid setTextView(TextView view) { 39 this.textview = view; 40 } 41 42 public GameThread getThread() { 43 return gameThread; 44 } 45 46 @Override 47 publicboolean onKeyDown(int keyCode, KeyEvent event) { 48 return gameThread.doKeyDown(keyCode, event); 49 } 50 51 @Override 52 publicboolean onKeyUp(int keyCode, KeyEvent event) { 53 return gameThread.doKeyUp(keyCode, event); 54 } 55 56 /** 57 * 当SurfaceView得到或失去焦点时调用,使游戏暂停/恢复运行, 58 */ 59 @Override 60 publicvoid onWindowFocusChanged(boolean hasWindowFocus) { 61 if (!hasWindowFocus) { 62 gameThread.pause(); 63 } else { 64 gameThread.unpause(); 65 } 66 } 67 68 @Override 69 publicvoid surfaceChanged(SurfaceHolder holder, int format, int width, 70 int height) { 71 Log.d(TAG, "surfaceChanged()"); 72 gameThread.setSurfaceSize(width, height); 73 gameThread.setRunning(true); 74 if (gameThread.isAlive()) { 75 Log.v(this.getClass().getName(), "unpause gameThread"); 76 gameThread.unpause(); 77 } else { 78 Log.v(this.getClass().getName(), "start gameThread"); 79 gameThread.start(); 80 } 81 } 82 83 @Override 84 publicvoid surfaceCreated(SurfaceHolder holder) { 85 Log.d(TAG, "surfaceCreated()"); 86 } 87 88 /** 89 * 为防止surface还会被创建(比如来电)导致gameThread再次启动出现错误,且Activity的onPause方法中已做暂停处理, 90 * 这边不对gameThread做处理 91 * 92 * @param holder 93 */ 94 @Override 95 publicvoid surfaceDestroyed(SurfaceHolder holder) { 96 Log.d(TAG, "surfaceDestroyed"); 97 } 98 }
游戏控制线程
1 GameThread.java 2 package air.frame; 3 4 import android.content.Context; 5 import android.graphics.Canvas; 6 import android.os.Bundle; 7 import android.os.Handler; 8 import android.util.Log; 9 import android.view.KeyEvent; 10 import android.view.SurfaceHolder; 11 12 publicclass GameThread extends Thread { 13 final String TAG = GameActivity.TAG; 14 15 // 游戏状态值:ready 16 publicfinalstaticint GS_READY =0; 17 // 游戏线程每执行一次需要睡眠的时间 18 privatefinalstaticint DELAY_TIME =100; 19 // 上下文,方便获取到应用的各项资源,如图片、音乐、字符串等 20 private Context context; 21 // 与Activity其他View交互用的handler 22 private Handler handler; 23 // 由SurfaceView提供的SurfaceHolder 24 private SurfaceHolder surfaceHolder; 25 // 游戏线程运行开关 26 privateboolean running =false; 27 // 游戏状态 28 privateint gameState; 29 // 当前surface/canvas的高度,在surfaceChanged方法中被设置 30 privateint mCanvasHeight =1; 31 // 当前surface/canvas的宽度,在surfaceChanged方法中被设置 32 privateint mCanvasWidth =1; 33 34 /** 35 * 游戏是否暂停 36 */ 37 privateboolean isPaused =false; 38 39 public GameThread(SurfaceHolder holder, Context context, Handler handler) { 40 this.surfaceHolder = holder; 41 this.context = context; 42 this.handler = handler; 43 } 44 45 /** 46 * 设置游戏状态 47 * 48 * @param mode 游戏状态 49 */ 50 publicvoid setState(int mode) { 51 synchronized (surfaceHolder) { 52 setState(mode, null); 53 } 54 } 55 56 /** 57 * 设置游戏状态 58 * 59 * @param mode 游戏状态 60 * @param message 设置游戏状态时的附加文字信息 61 */ 62 publicvoid setState(int mode, CharSequence message) { 63 synchronized (surfaceHolder) { 64 // TODO 65 } 66 } 67 68 /** 69 * 暂停游戏 70 */ 71 publicvoid pause() { 72 synchronized (surfaceHolder) { 73 isPaused =true; 74 } 75 } 76 77 /** 78 * 恢复运行游戏 79 */ 80 publicvoid unpause() { 81 // 如果游戏中有时间,别忘记应将其在这里调整到正常 82 synchronized (surfaceHolder) { 83 isPaused =false; 84 } 85 } 86 87 /** 88 * 当Activity因销毁而被重新创建时,在这里恢复游戏上次运行的数据 89 * 90 * @param saveState Activity传来的保存游戏数据的容器 91 */ 92 publicvoid restoreState(Bundle saveState) { 93 // TODO 94 } 95 96 /** 97 * 在Activity切到后台时保存游戏的数据 98 * 99 * @param outState 保存游戏数据的容器 100 */ 101 publicvoid saveState(Bundle outState) { 102 // TODO 103 } 104 105 /** 106 * 设置游戏线程运行开关 107 * 108 * @param b 开/关 109 */ 110 publicvoid setRunning(boolean b) { 111 running = b; 112 } 113 114 /** 115 * 处理按下按键的事件 116 * 117 * @param keyCode 按键事件动作值 118 * @param msg 按键事件对象 119 * @return 是否处理完 120 */ 121 publicboolean doKeyDown(int keyCode, KeyEvent msg) { 122 synchronized (surfaceHolder) { 123 // TODO 124 returnfalse; 125 } 126 } 127 128 /** 129 * 处理弹起按键的事件 130 * 131 * @param keyCode 按键事件动作值 132 * @param msg 按键事件对象 133 * @return 是否处理完 134 */ 135 publicboolean doKeyUp(int keyCode, KeyEvent msg) { 136 synchronized (surfaceHolder) { 137 // TODO 138 } 139 returnfalse; 140 } 141 142 /** 143 * 设置surface/canvas的宽度和高度 144 * 145 * @param width 由SurfaceHolder传来的宽度 146 * @param height 由SurfaceHolder传来的高度 147 */ 148 publicvoid setSurfaceSize(int width, int height) { 149 // synchronized to make sure these all change atomically 150 synchronized (surfaceHolder) { 151 mCanvasWidth = width; 152 mCanvasHeight = height; 153 // 不要忘记每次画布的宽度和高度改变时, 在这里对图片等资源做缩放等相关适配屏幕的处理 154 // TODO 155 } 156 } 157 158 publicvoid run() { 159 while (running) { 160 if (!isPaused) { 161 Canvas c =null; 162 try { 163 c = surfaceHolder.lockCanvas(null); 164 synchronized (surfaceHolder) { 165 doDraw(c); 166 } 167 logic(); 168 } finally { 169 if (c !=null) { 170 surfaceHolder.unlockCanvasAndPost(c); 171 } 172 } 173 try { 174 Thread.sleep(DELAY_TIME); 175 } catch (InterruptedException e) { 176 e.printStackTrace(); 177 } 178 } 179 } 180 } 181 182 /** 183 * 游戏逻辑处理 184 */ 185 publicvoid logic() { 186 Log.v(this.getClass().getName(), "logic"); 187 // TODO 188 } 189 190 /** 191 * 游戏绘画 192 */ 193 privatevoid doDraw(Canvas canvas) { 194 Log.v(this.getClass().getName(), "doDraw"); 195 // TODO 196 } 197 198 199 /** 200 * 初始化游戏开始时的参数 201 */ 202 publicvoid doStart() { 203 // TODO 204 } 205 }
布局文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:orientation="vertical" 5 android:layout_width="fill_parent" 6 android:layout_height="fill_parent"> 7 <air.frame.GameSurfaceView 8 android:id="@+id/gameview" 9 android:layout_width="fill_parent" 10 android:layout_height="fill_parent"/> 11 <RelativeLayout 12 android:layout_width="fill_parent" 13 android:layout_height="fill_parent"> 14 <TextView 15 android:id="@+id/textview" 16 android:layout_width="wrap_content" 17 android:layout_height="wrap_content" 18 android:layout_centerInParent="true" 19 android:gravity="center_horizontal" 20 android:textColor="#88ffffff" 21 android:textSize="24sp"/> 22 </RelativeLayout> 23 </FrameLayout>
生命周期初步信息
如上所示: surfaceView的生命周期寄托在activity的生命周期之上
附:
activity现场保护:(可以用于实现游戏暂停, 也可以持久化存储现场, 在surfaceCreate里面判断恢复)
http://www.cnblogs.com/wumao/archive/2011/04/25/2026483.html
以下链接是恢复现场的几种方法!
http://www.cnblogs.com/xirihanlin/archive/2009/08/05/1539420.html
除了官方的生命周期图之外, 如下链接对生命周期的见解, 我觉得不错!
http://www.cnblogs.com/kofi1122/archive/2011/04/10/2011772.html
另外, 两个不常用的状态:
onSaveInstanceState: Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key) /当某个activity变得“容易”被系统销毁时,该activity的onSaveInstanceState就会被执行,除非该activity是被用户主动销毁的,例如当用户按返回键的时候。
onSaveInstanceState触发的时机
1、当用户按下HOME键时。
这是显而易见的,系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
2、长按HOME键,选择运行其他的程序时。
3、按下电源按键(关闭屏幕显示)时。
4、从activity A中启动一个新的activity时。
5、屏幕方向切换时,例如从竖屏切换到横屏时。
在屏幕切换之前,系统会销毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以onSaveInstanceState一定会被执行
总而言之,onSaveInstanceState的调用遵循一个重要原则,即当系统“未经你许可”时销毁了你的activity,则onSaveInstanceState会被系统调用,这是系统的责任,因为它必须要提供一个机会让你保存你的数据(当然你不保存那就随便你了)。
onRestoreInstanceState: 需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成对的被调用的,onRestoreInstanceState被调用的前提是,activity A“确实”被系统销毁了,而如果仅仅是停留在有这种可能性的情况下,则该方法不会被调用,例如,当正在显示activity A的时候,用户按下HOME键回到主界面,然后用户紧接着又返回到activity A,这种情况下activity A一般不会因为内存的原因被系统销毁,故activity A的onRestoreInstanceState方法不会被执
另外,onRestoreInstanceState的bundle参数也会传递到onCreate方法中,你也可以选择在onCreate方法中做数据还原(如例子所示Line25~Line33)。