从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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值