最近在对一款开发中的游戏进行压力测试时会出现崩溃的问题,具体场景是 在进入一个Activity时立即按返回键退出该Activity再迅速进入该Activity,进入退出的时间间隔越短越容易发生崩溃,后来打印日志发现,由于Activity进入需要加载资源,退出需要释放资源,这两个操作都需要一定的时间,照以上场景进行压力测试就会出现 Activity在资源还没加载完之前然后程序就会去释放资源,这样就会出现空指针而崩溃;另一种情况是程序加载资源比较多,这样耗费的时间会比较久,同样释放资源也会比较久,在立即退出Activity再迅速进入该Activity的时候就会出现Activity的资源还没来得及释放,然后就再次加载了一遍资源,这样就会出现内存溢出异常。
解决问题一定要从根源出发,这样才能确保问题被彻底解决。上述问题的根源就是 由于用户操作太快导致资源在未被加载完之前又开始释放,以及资源会被重复加载两次。从问题根源出发,我们只要保证资源在加载完成之前确保资源不会被释放,以及资源不会被重复加载,这样问题就可以迎刃而解了。
解决方案有两种:一是加Loading页面,强制用户等待,这种方式在很多比较大型的游戏里很常见。二,是设置标志位在Activity资源加载完成之前禁止用户退出Activity, 这个适用于一些资源不太多,没有Loading页面的的情况,本质上也是强制用户等待(一些小的游戏常常没有loading页面 ,资源加载也加载也比较快,所以常常忽略了压力测试)
第一种方案,相信做游戏的朋友们都很熟悉,这里就不细讲了。
第二中方案,这里给出个一个框架,如下:
public class Test1 extends Activity
{
public static final int MSG_INIT_COMPLETE = 1;
public static final int MSG_DELAYED = 200;
private boolean isResume;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
initRes();
isResume = false;
mHandler.sendEmptyMessageDelayed(MSG_INIT_COMPLETE, MSG_DELAYED);
}
public Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what)
{
case MSG_INIT_COMPLETE:
isResume = true;
break;
}
}
};
protected void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
recycleRes();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
// TODO Auto-generated method stub
if (keyCode == KeyEvent.KEYCODE_BACK)
{
if (isResume)
{
startActivity(new Intent(this,Test2.class));
finish();
}
else
return false;
}
return super.onKeyDown(keyCode, event);
}
}
以上代码框架是防止Test1的资源被加载完成之前开始释放资源,即在资源加载完成之前屏蔽返回键事件。为了保险起见,在不影响用户体验的前提下大家可以把MSG_DELAYED的值再设置的大些。
public class Test extends Activity implements OnClickListener
{
public static final int MSG_INIT_COMPLETE = 1;
public static final int MSG_DELAYED = 200;
private boolean isResume;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
isResume = false;
mHandler.sendEmptyMessageDelayed(MSG_INIT_COMPLETE, MSG_DELAYED);
}
public Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what)
{
case MSG_INIT_COMPLETE:
isResume = true;
break;
}
}
};
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
case TAG_GOTO_TEST1:
if(isResume)
{
startActivity(this,test1.class);
finish();
}
break;
}
}
解决问题一定要从根源出发,这样才能确保问题被彻底解决。上述问题的根源就是 由于用户操作太快导致资源在未被加载完之前又开始释放,以及资源会被重复加载两次。从问题根源出发,我们只要保证资源在加载完成之前确保资源不会被释放,以及资源不会被重复加载,这样问题就可以迎刃而解了。
解决方案有两种:一是加Loading页面,强制用户等待,这种方式在很多比较大型的游戏里很常见。二,是设置标志位在Activity资源加载完成之前禁止用户退出Activity, 这个适用于一些资源不太多,没有Loading页面的的情况,本质上也是强制用户等待(一些小的游戏常常没有loading页面 ,资源加载也加载也比较快,所以常常忽略了压力测试)
第一种方案,相信做游戏的朋友们都很熟悉,这里就不细讲了。
第二中方案,这里给出个一个框架,如下:
public class Test1 extends Activity
{
public static final int MSG_INIT_COMPLETE = 1;
public static final int MSG_DELAYED = 200;
private boolean isResume;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
initRes();
isResume = false;
mHandler.sendEmptyMessageDelayed(MSG_INIT_COMPLETE, MSG_DELAYED);
}
public Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what)
{
case MSG_INIT_COMPLETE:
isResume = true;
break;
}
}
};
protected void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
recycleRes();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
// TODO Auto-generated method stub
if (keyCode == KeyEvent.KEYCODE_BACK)
{
if (isResume)
{
startActivity(new Intent(this,Test2.class));
finish();
}
else
return false;
}
return super.onKeyDown(keyCode, event);
}
}
以上代码框架是防止Test1的资源被加载完成之前开始释放资源,即在资源加载完成之前屏蔽返回键事件。为了保险起见,在不影响用户体验的前提下大家可以把MSG_DELAYED的值再设置的大些。
public class Test extends Activity implements OnClickListener
{
public static final int MSG_INIT_COMPLETE = 1;
public static final int MSG_DELAYED = 200;
private boolean isResume;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
isResume = false;
mHandler.sendEmptyMessageDelayed(MSG_INIT_COMPLETE, MSG_DELAYED);
}
public Handler mHandler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
super.handleMessage(msg);
switch (msg.what)
{
case MSG_INIT_COMPLETE:
isResume = true;
break;
}
}
};
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
case TAG_GOTO_TEST1:
if(isResume)
{
startActivity(this,test1.class);
finish();
}
break;
}
}
这段代码是防止Test1的资源在释放完之前再次进入Test1造成内存溢出,同样,在不影响用户体验的前提下进入Test1的延迟时间可以再长久一些。
-------------------------------------------------------------------------------------
本文出自http://labs.easymobi.cn/?p=4386