从android游戏框架看其生命周期
同类型游戏 生命周期 讲解的博客还有 :从零开始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 @Override102 protectedvoid onStart() {103 Log.d(TAG, "onStart");104 super.onStart();105 }106 107 @Override108 protectedvoid onStop() {109 Log.d(TAG, "onStop");110 super.onStop();111 }112 113 @Override114 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
GameSurfaceView.java1 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 @Override27 publicvoid handleMessage(Message m) {28 textview.setText(m.getData().getString("text"));29 }30 });31 // 设置可获得焦点,确保能捕获到KeyEvent32 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 @Override47 publicboolean onKeyDown(int keyCode, KeyEvent event) {48 return gameThread.doKeyDown(keyCode, event);49 }50 51 @Override52 publicboolean onKeyUp(int keyCode, KeyEvent event) {53 return gameThread.doKeyUp(keyCode, event);54 }55 56 /**57 * 当SurfaceView得到或失去焦点时调用,使游戏暂停/恢复运行,58 */59 @Override60 publicvoid onWindowFocusChanged(boolean hasWindowFocus) {61 if (!hasWindowFocus) {62 gameThread.pause();63 } else {64 gameThread.unpause();65 }66 }67 68 @Override69 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 @Override84 publicvoid surfaceCreated(SurfaceHolder holder) {85 Log.d(TAG, "surfaceCreated()");86 }87 88 /**89 * 为防止surface还会被创建(比如来电)导致gameThread再次启动出现错误,且Activity的onPause方法中已做暂停处理,90 * 这边不对gameThread做处理91 * 92 * @param holder93 */94 @Override95 publicvoid surfaceDestroyed(SurfaceHolder holder) {96 Log.d(TAG, "surfaceDestroyed");97 }98 }
游戏控制线程
GameThread.java1 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 // TODO103 }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 // TODO124 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 // TODO138 }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 atomically150 synchronized (surfaceHolder) {151 mCanvasWidth = width;152 mCanvasHeight = height;153 // 不要忘记每次画布的宽度和高度改变时, 在这里对图片等资源做缩放等相关适配屏幕的处理154 // TODO155 }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 // TODO188 }189 190 /**191 * 游戏绘画192 */193 privatevoid doDraw(Canvas canvas) {194 Log.v(this.getClass().getName(), "doDraw");195 // TODO196 }197 198 199 /**200 * 初始化游戏开始时的参数201 */202 publicvoid doStart() {203 // TODO204 }205 }
布局文件
main.xml1 <?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 <RelativeLayout12 android:layout_width="fill_parent"13 android:layout_height="fill_parent">14 <TextView15 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)。
更多相关文章
- Android学习——Activity生命周期
- Android中的状态选择器
- Android中状态栏的隐藏
- Android 4 游戏高级编程(第2版)
- Cocos2d Box2D 开发Android下的 Breakout 撞球游戏
- Android翻译:应用程序的生命周期 kill进程
- android:configChanges 横竖屏切换的生命周期
- 关于Android生命周期方法调用return的问题
- Android Service判断设备联网状态详解