从android游戏框架看其生命周期
书本上, 网路上android游戏框架比比皆是, 但是都未深入其生命周期,
以下是我选取的基于servaceView的android游戏框架,
Activity
1 package air.frame;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.util.Log;
6 import android.view.Menu;
7 import android.view.MenuItem;
8 import android.widget.TextView;
9
10 public
class GameActivity
extends Activity {
11
private GameSurfaceView gameSurfaceView;
12
private GameThread gameThread;
13
public static
final String TAG
= "GameFrame";
14
15 @Override
16
public void onCreate(Bundle savedInstanceState) {
17
super.onCreate(savedInstanceState);
18 setContentView(R.layout.main);
19 gameSurfaceView
= (GameSurfaceView)
this.findViewById(R.id.gameview);
20 gameThread
= gameSurfaceView.getThread();
21 gameSurfaceView.setTextView((TextView) findViewById(R.id.textview));
22
23 Log.d(TAG,
"onCreate");
24
25
if (savedInstanceState
== null) {
26
// 游戏第一次启动时,初始化游戏状态
27 gameThread.doStart();
28 Log.d(TAG,
"SIS is null");
29 }
else {
30
// 从其他应用界面切回游戏时,如果Activity重新创建,则恢复上次切出游戏时的各项数据
31 gameThread.restoreState(savedInstanceState);
32 Log.d(TAG,
"SIS is not null");
33 }
34 }
35
36
/**
37 * 当Activity被切换到后台时调用,存储Activity重新创建时需要恢复的游戏数据
38
*/
39 @Override
40
protected void onSaveInstanceState(Bundle outState) {
41
super.onSaveInstanceState(outState);
42 gameThread.saveState(outState);
43 Log.d(TAG,
"SIS called");
44 }
45
46
/**
47 * 当Activity被切换到后台时调用,在这里对游戏做"暂停"的处理
48
*/
49 @Override
50
protected void onPause() {
51
super.onPause();
52
// 暂停游戏
53 Log.d(TAG,
"onPause");
54 gameSurfaceView.getThread().pause();
55 }
56
57
/**
58 * 当Activity切换到前台时调用
59
*/
60 @Override
61
protected void onResume() {
62
super.onResume();
63
// 游戏结束暂停状态,游戏正常进行
64 Log.d(TAG,
"onResume");
65 gameSurfaceView.getThread().unpause();
66 }
67
68
/**
69 * 创建游戏菜单
70
*/
71 @Override
72
public boolean onCreateOptionsMenu(Menu menu) {
73
// TODO Auto-generated method stub
74 Log.d(TAG,
"onCreateOptionsMenu");
75
return super.onCreateOptionsMenu(menu);
76 }
77
78
/**
79 * 定义游戏菜单的点击事件处理
80
*/
81 @Override
82
public boolean onOptionsItemSelected(MenuItem item) {
83
// TODO Auto-generated method stub
84 Log.d(TAG,
"onOptionsItemSelected");
85
return super.onOptionsItemSelected(item);
86 }
87
88 @Override
89
protected void onRestart() {
90 Log.d(TAG,
"onRestart");
91
super.onRestart();
92 }
93
94 @Override
95
protected void onRestoreInstanceState(Bundle savedInstanceState) {
96 Log.d(TAG,
"onRestoreInstanceState");
97
super.onRestoreInstanceState(savedInstanceState);
98 }
99
100 @Override
101
protected void onStart() {
102 Log.d(TAG,
"onStart");
103
super.onStart();
104 }
105
106 @Override
107
protected void onStop() {
108 Log.d(TAG,
"onStop");
109
super.onStop();
110 }
111
112 @Override
113
protected void onDestroy() {
114
super.onDestroy();
115 Log.d(TAG,
"onDestroy");
116
117
// 停止游戏
118 gameThread.setRunning(false);
119
boolean retry
= true;
120
while (retry) {
121
try {
122
// 阻塞Activity的主线程直到游戏线程执行完
123 gameThread.join();
124 retry
= false;
125 }
catch (InterruptedException e) {
126 }
127 }
128 }
129 }
surfaceView
package air.frame;
2
3 import android.content.Context;
4 import android.os.Handler;
5 import android.os.Message;
6 import android.util.AttributeSet;
7 import android.util.Log;
8 import android.view.KeyEvent;
9 import android.view.SurfaceHolder;
10 import android.view.SurfaceView;
11 import android.view.SurfaceHolder.Callback;
12 import android.widget.TextView;
13
14 public
class GameSurfaceView
extends SurfaceView
implements Callback {
15
private GameThread gameThread;
16
private TextView textview;
17
18
final String TAG
= GameActivity.TAG;
19
20
public GameSurfaceView(Context context, AttributeSet attrs) {
21
super(context, attrs);
22 SurfaceHolder holder
= getHolder();
23 holder.addCallback(this);
24 gameThread
= new GameThread(holder, context,
new Handler() {
25 @Override
26
public
void handleMessage(Message m) {
27 textview.setText(m.getData().getString("text"));
28 }
29 });
30
// 设置可获得焦点,确保能捕获到KeyEvent
31 setFocusable(true);
32 }
33
34
/**
35 * 获取一个Activity传来的View协助SurfaceView显示游戏视图,View的具体类型可以根据游戏需要来定
36
*/
37
public void setTextView(TextView view) {
38
this.textview
= view;
39 }
40
41
public GameThread getThread() {
42
return gameThread;
43 }
44
45 @Override
46
public boolean onKeyDown(int keyCode, KeyEvent event) {
47
return gameThread.doKeyDown(keyCode, event);
48 }
49
50 @Override
51
public boolean onKeyUp(int keyCode, KeyEvent event) {
52
return gameThread.doKeyUp(keyCode, event);
53 }
54
55
/**
56 * 当SurfaceView得到或失去焦点时调用,使游戏暂停/恢复运行,
57
*/
58 @Override
59
public void onWindowFocusChanged(boolean hasWindowFocus) {
60
if (!hasWindowFocus) {
61 gameThread.pause();
62 }
else {
63 gameThread.unpause();
64 }
65 }
66
67 @Override
68
public void surfaceChanged(SurfaceHolder holder,
int format,
int width,
69
int height) {
70 Log.d(TAG,
"surfaceChanged()");
71 gameThread.setSurfaceSize(width, height);
72 gameThread.setRunning(true);
73
if (gameThread.isAlive()) {
74 Log.v(this.getClass().getName(),
"unpause gameThread");
75 gameThread.unpause();
76 }
else {
77 Log.v(this.getClass().getName(),
"start gameThread");
78 gameThread.start();
79 }
80 }
81
82 @Override
83
public void surfaceCreated(SurfaceHolder holder) {
84 Log.d(TAG,
"surfaceCreated()");
85 }
86
87
/**
88 * 为防止surface还会被创建(比如来电)导致gameThread再次启动出现错误,且Activity的onPause方法中已做暂停处理,
89 * 这边不对gameThread做处理
90 *
91 *
@param holder
92
*/
93 @Override
94
public void surfaceDestroyed(SurfaceHolder holder) {
95 Log.d(TAG,
"surfaceDestroyed");
96 }
97 }
游戏控制线程
1 package air.frame;
2
3 import android.content.Context;
4 import android.graphics.Canvas;
5 import android.os.Bundle;
6 import android.os.Handler;
7 import android.util.Log;
8 import android.view.KeyEvent;
9 import android.view.SurfaceHolder;
10
11 publicclass GameThreadextends
Thread {
12
final String TAG
= GameActivity.TAG;
13
14
// 游戏状态值:ready
15 public
final
staticint GS_READY
= 0;
16
// 游戏线程每执行一次需要睡眠的时间
17 private
final
staticint DELAY_TIME=
100;
18
// 上下文,方便获取到应用的各项资源,如图片、音乐、字符串等
19 private Context context;
20
// 与Activity其他View交互用的handler
21 private Handler handler;
22
// 由SurfaceView提供的SurfaceHolder
23 private SurfaceHolder surfaceHolder;
24
// 游戏线程运行开关
25 private
boolean running
= false;
26
// 游戏状态
27 private
int gameState;
28
// 当前surface/canvas的高度,在surfaceChanged方法中被设置
29 private
int mCanvasHeight
= 1;
30
// 当前surface/canvas的宽度,在surfaceChanged方法中被设置
31 private
int mCanvasWidth
= 1;
32
33
/**
34 * 游戏是否暂停
35
*/
36
private boolean isPaused=
false;
37
38
public GameThread(SurfaceHolder holder, Context context, Handler handler) {
39
this.surfaceHolder
= holder;
40
this.context
= context;
41
this.handler
= handler;
42 }
43
44
/**
45 * 设置游戏状态
46 *
47 *
@param mode 游戏状态
48
*/
49
public void setState(int mode) {
50
synchronized (surfaceHolder) {
51 setState(mode,null);
52 }
53 }
54
55
/**
56 * 设置游戏状态
57 *
58 *
@param mode 游戏状态
59 *
@param message 设置游戏状态时的附加文字信息
60
*/
61
public void setState(int mode, CharSequence message) {
62
synchronized (surfaceHolder) {
63 // TODO
64 }
65 }
66
67
/**
68 * 暂停游戏
69
*/
70
public void pause() {
71
synchronized (surfaceHolder) {
72 isPaused=
true;
73 }
74 }
75
76
/**
77 * 恢复运行游戏
78
*/
79
public void unpause() {
80
// 如果游戏中有时间,别忘记应将其在这里调整到正常
81 synchronized (surfaceHolder) {
82 isPaused=
false;
83 }
84 }
85
86
/**
87 * 当Activity因销毁而被重新创建时,在这里恢复游戏上次运行的数据
88 *
89 *
@param saveState Activity传来的保存游戏数据的容器
90
*/
91
public void restoreState(Bundle saveState) {
92
// TODO
93 }
94
95
/**
96 * 在Activity切到后台时保存游戏的数据
97 *
98 *
@param outState 保存游戏数据的容器
99
*/
100
public void saveState(Bundle outState) {
101
// TODO
102 }
103
104
/**
105 * 设置游戏线程运行开关
106 *
107 *
@param b 开/关
108
*/
109
public void setRunning(boolean b) {
110 running= b;
111 }
112
113
/**
114 * 处理按下按键的事件
115 *
116 *
@param keyCode 按键事件动作值
117 *
@param msg 按键事件对象
118 *
@return 是否处理完
119
*/
120
public boolean doKeyDown(int keyCode, KeyEvent msg) {
121
synchronized (surfaceHolder) {
122 // TODO
123 return
false;
124 }
125 }
126
127
/**
128 * 处理弹起按键的事件
129 *
130 *
@param keyCode 按键事件动作值
131 *
@param msg 按键事件对象
132 *
@return 是否处理完
133
*/
134
public boolean doKeyUp(int keyCode, KeyEvent msg) {
135
synchronized (surfaceHolder) {
136 // TODO
137 }
138
return false;
139 }
140
141
/**
142 * 设置surface/canvas的宽度和高度
143 *
144 *
@param width 由SurfaceHolder传来的宽度
145 *
@param height 由SurfaceHolder传来的高度
146
*/
147
public void setSurfaceSize(int width,int
height) {
148
// synchronized to make sure these all change atomically
149 synchronized (surfaceHolder) {
150 mCanvasWidth= width;
151 mCanvasHeight= height;
152 // 不要忘记每次画布的宽度和高度改变时, 在这里对图片等资源做缩放等相关适配屏幕的处理
153 // TODO
154 }
155 }
156
157
public void run() {
158
while (running) {
159 if (!isPaused) {
160 Canvas c=
null;
161 try {
162 c= surfaceHolder.lockCanvas(null);
163 synchronized (surfaceHolder) {
164 doDraw(c);
165 }
166 logic();
167 }finally {
168 if (c
!= null) {
169 surfaceHolder.unlockCanvasAndPost(c);
170 }
171 }
172 try {
173 Thread.sleep(DELAY_TIME);
174 }catch (InterruptedException e) {
175 e.printStackTrace();
176 }
177 }
178 }
179 }
180
181
/**
182 * 游戏逻辑处理
183
*/
184
public void logic() {
185 Log.v(this.getClass().getName(),"logic");
186
// TODO
187 }
188
189
/**
190 * 游戏绘画
191
*/
192
private void doDraw(Canvas canvas) {
193 Log.v(this.getClass().getName(),"doDraw");
194
// TODO
195 }
196
197
198
/**
199 * 初始化游戏开始时的参数
200
*/
201
public void doStart() {
202
// TODO
203 }
204 }
布局文件
main.xml
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <air.frame.GameSurfaceView android:id="@+id/gameview" android:layout_width="fill_parent" android:layout_height="fill_parent" /> <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:id="@+id/textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:gravity="center_horizontal" android:textColor="#88ffffff" android:textSize="24sp" /> </RelativeLayout> </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)。
http://www.cnblogs.com/HelloHolyWorld/archive/2011/09/19/2181890.html