从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      @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 

GameSurfaceView.java
 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  }

游戏控制线程 

GameThread.java
  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  }

布局文件 

main.xml
 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)。

原文地址:https://www.cnblogs.com/firecode/p/2620348.html