我正在开发简单的Android游戏,但我在测试时遇到了问题。当我在联想Tab3 7平板电脑(Android 5.0.1)或LG P880手机(Android 4.0.3)上运行时,它运行正常。当我在三星S7手机(Android 7.0)上运行时,游戏通常运行良好。我的意思是,我可以连续10次运行它没有任何问题,但有时游戏停止5-30秒或停止响应。这通常发生在新活动开始期间或之后不久。
游戏有4个Activities
,使用扩展SurfaceView
作为布局。所有SurfaceView都实现Runnable
。活动包括:启动画面(noHistory =" true" in Manifest),菜单,难度选择和游戏。
我只使用mdpi drawables
并按比例缩放所有屏幕尺寸。位图使用BitmapFactory.decodeResource
BitmapFactory.Options
inDensity = 1
,inScaled = false
加载。
出现问题时,logcat仅显示垃圾回收。有时游戏"暂停" (没有注册水龙头)5-30秒并恢复正常,有时由于没有响应而必须重新启动。我觉得游戏因某种原因停止收集输入。通过覆盖onTouchEvent
并检查ACTION_UP
是否在抽头图像范围内来处理输入。正如我所说,这只发生在S7(我在两部手机上试过),而不是在平板电脑或P880上,所以我认为它可能与Nougat
或我强迫降低{{1}有关。在电话上。
所以,既然我已经没想到会导致这个问题,而且我是Android游戏开发的新手,那么有没有人知道/我知道应该在哪里寻找解决方案?有什么Nougat特定我应该设置/检查?强制像素密度会以任何方式影响设备性能吗?
修改1
globalApp
density
菜单项
public class globalApp extends Application {
SoundPool soundPool;
SoundPool.Builder soundPoolBuilder;
AudioAttributes audioAttributes;
AudioAttributes.Builder audioAttributesBuilder;
int soundTap, soundCorrect, soundIncorrect, soundVictory, soundDefeat;
int soundBarrelVerySlow, soundBarrelSlow, soundBarrelNormal, soundBarrelFast, soundBarrelVeryFast;
@Override
public void onCreate() {
super.onCreate();
}
public void buildSoundPool(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
audioAttributesBuilder = new AudioAttributes.Builder();
audioAttributesBuilder.setUsage(AudioAttributes.USAGE_GAME);
audioAttributesBuilder.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION);
audioAttributes = audioAttributesBuilder.build();
soundPoolBuilder = new SoundPool.Builder();
soundPoolBuilder.setMaxStreams(2);
soundPoolBuilder.setAudioAttributes(audioAttributes);
soundPool = soundPoolBuilder.build();
}
else {
soundPool = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
}
}
public void loadSounds(){
soundBarrelVerySlow = soundPool.load(this,R.raw.very_slow_move, 1);
soundBarrelSlow = soundPool.load(this, R.raw.slow_move, 1);
soundBarrelNormal = soundPool.load(this, R.raw.slow_move, 1);
soundBarrelFast = soundPool.load(this,R.raw.fast_move, 1);
soundBarrelVeryFast = soundPool.load(this,R.raw.very_fast_move, 1);
soundTap = soundPool.load(this, R.raw.tap_sound, 1);
soundCorrect = soundPool.load(this, R.raw.correct, 1);
soundIncorrect = soundPool.load(this, R.raw.incorrect, 1);
soundVictory = soundPool.load(this, R.raw.victory, 1);
soundDefeat = soundPool.load(this, R.raw.defeat, 1);
}
public void playTap(){
soundPool.play(soundTap, 1, 1,1, 0, 1);
}
public void playCorrect(){
soundPool.play(soundCorrect, 1, 1,1, 0, 1);
}
public void playIncorrect(){
soundPool.play(soundIncorrect, 1, 1,1, 0, 1);
}
public void playVictory(){
soundPool.play(soundVictory, 1, 1,1, 0, 1);
}
public void playDefeat(){
soundPool.play(soundDefeat, 1, 1,1, 0, 1);
}
public void playBarrelVerySlow(){soundPool.play(soundBarrelVerySlow, 1, 1, 1, 0, 1);}
public void playBarrelSlow(){soundPool.play(soundBarrelSlow, 1, 1, 1, 0, 1);}
public void playBarrelNormal(){
soundPool.play(soundBarrelNormal, 1, 1,1, 0, 1);
}
public void playBarrelFast(){soundPool.play(soundBarrelFast, 1, 1, 1, 0, 1);}
public void playBarrelVeryFast(){soundPool.play(soundBarrelVeryFast, 1, 1, 1, 0, 1);}
}
MainActivity
public class MenuItem {
private Bitmap bmp;
private Context context;
private Rect sourceRect;
private RectF destRect;
private int srcWidth;
private int srcHeight;
private int destW, destH;
private int x, y;
private int screenH;
public MenuItem(Context ctx, String bmpName, int w, int x, int y, int sX, int sY){
context = ctx;
BitmapFactory.Options bmpFOptions = new BitmapFactory.Options();
bmpFOptions.inDensity = 1;
bmpFOptions.inScaled = false;
int res = context.getResources().getIdentifier(bmpName, "drawable", ctx.getPackageName());
bmp = BitmapFactory.decodeResource(ctx.getResources(), res, bmpFOptions);
srcWidth = w;
srcHeight = bmp.getHeight();
this.x = x;
this.y = y;
screenH = sY;
sourceRect = new Rect(0,0, srcWidth, srcHeight);
destRect = new RectF();
setProportionalDestinationRect(sX, sY);
}
private void setProportionalDestinationRect(int scrX, int scrY) {
if (scrX != 1024 || scrY != 552){
float propX = (float)scrX/1024;
float propY = (float)scrY/600;
// All drawables are designed for 1024x600 screen
// if device screen is different, scale image proportionally
destW = (int)(srcWidth * propX);
destH = (int) (srcHeight * propY);
x = (int) (x*propX);
y = (int) (y*propY);
}
else {
destW = srcWidth;
destH = srcHeight;
}
destRect.set(x,y, x+destW,y+destH);
}
public void update(){
}
public Bitmap getBmp() {
return bmp;
}
public void setBmp(Bitmap bmp) {
this.bmp = bmp;
}
public Rect getSourceRect() {
return sourceRect;
}
public void setSourceRect(Rect sourceRect) {
this.sourceRect = sourceRect;
}
public RectF getDestRect() {
return destRect;
}
public void setDestRect(RectF destRect) {
this.destRect = destRect;
}
public boolean contains(int x, int y){
if (destRect.left <= x && destRect.right >= x)
if (destRect.top <= y && destRect.bottom >= y)
return true;
return false;
}
public void setY(int y) {
this.y = y;
if (screenH != 552){
float propY = (float)screenH/600;
y = (int) (y*propY);
}
destRect.set(x,y, x+destW,y+destH);
}
}
MainActivitySurface
public class MainActivity extends Activity {
private boolean backPressedOnce = false;
long backPressedTime = 0;
private MainActivitySurface mainActivitySurface;
globalApp app;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
int x = getIntent().getIntExtra("screenWidth", 500);
int y = getIntent().getIntExtra("screenHeight", 500);
app = (globalApp) getApplication();
app.buildSoundPool();
app.loadSounds();
mainActivitySurface = new MainActivitySurface(this, app, x, y);
mainActivitySurface.setParentActivity(MainActivity.this);
setContentView(mainActivitySurface);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1001) {
if (resultCode == RESULT_OK) {
int result = data.getIntExtra("difficulty", 3);
mainActivitySurface.setResultDifficulty(result);
}
}
}
@Override
protected void onPause() {
super.onPause();
mainActivitySurface.pause();
}
@Override
protected void onResume() {
super.onResume();
backPressedOnce = false;
mainActivitySurface.resume();
}
@Override
public void onBackPressed() {
if (backPressedOnce && backPressedTime + 2000 > System.currentTimeMillis()) {
Process.killProcess(Process.myPid());
System.exit(1);
} else {
Toast.makeText(this, "Press back again to exit.", Toast.LENGTH_SHORT).show();
backPressedOnce = true;
}
backPressedTime = System.currentTimeMillis();
}
}
DifficultyActivity
public class MainActivitySurface extends SurfaceView implements Runnable {
private Context context;
private SurfaceHolder surfaceHolder;
private Canvas canvas;
private Thread thread = null;
volatile private boolean running = false;
private boolean surfaceCreated = false;
private Intent playIntent;
private Intent difficultyIntent;
// Screen size
private int screenWidth, screenHeight;
//Menu items
private MenuItem menuItemPlay, menuItemDifficulty, middleBarrel, bg;
private int difficulty = 3;
private Activity parentActivity;
private globalApp app;
public MainActivitySurface(Context ctx, globalApp a, int scrW, int scrH){
super(ctx);
context = ctx;
screenHeight = scrH;
screenWidth = scrW;
app = a;
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
bg = new MenuItem(context, "main_activity_background_single", 1024, 0, 0, scrW, scrH);
menuItemPlay = new MenuItem(context, "menu_item_play_single", 233,(1024-233)/2,100, scrW, scrH);
menuItemDifficulty = new MenuItem(ctx, "menu_item_difficulty_single", 520,(1024 - 520)/2,400,scrW,scrH);
middleBarrel = new MenuItem(ctx, "middle_barrel_single", 323,(1024-323)/2,200,scrW,scrH);
playIntent = new Intent(context, GameActivity.class);
playIntent.putExtra("screenWidth", screenWidth);
playIntent.putExtra("screenHeight", screenHeight);
}
@Override
public void run() {
while (running){
draw();
}
}
private void draw() {
if(surfaceHolder.getSurface().isValid()){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bg.getBmp(), bg.getSourceRect(), bg.getDestRect(), null);
canvas.drawBitmap(menuItemPlay.getBmp(), menuItemPlay.getSourceRect(), menuItemPlay.getDestRect(), null);
canvas.drawBitmap(menuItemDifficulty.getBmp(), menuItemDifficulty.getSourceRect(), menuItemDifficulty.getDestRect(), null);
canvas.drawBitmap(middleBarrel.getBmp(), middleBarrel.getSourceRect(), middleBarrel.getDestRect(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
public void pause(){
running = false;
boolean retry = false;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
Log.d("info", "MainActivitySurface: Error joining thread");
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & event.ACTION_MASK){
case MotionEvent.ACTION_UP:
if (menuItemPlay.contains((int) event.getX(), (int) event.getY())){
app.playTap();
parentActivity.startActivity(playIntent);
parentActivity.overridePendingTransition(0,0);
break;
}
if (menuItemDifficulty.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficultyIntent = new Intent(parentActivity, DifficultyActivity.class);
difficultyIntent.putExtra("screenWidth", screenWidth);
difficultyIntent.putExtra("screenHeight", screenHeight);
difficultyIntent.putExtra("difficulty", difficulty);
parentActivity.startActivityForResult(difficultyIntent, 1001);
parentActivity.overridePendingTransition(0, 0);
break;
}
}
return true;
}
public void setParentActivity(Activity act){
parentActivity = act;
}
public void setResultDifficulty(int diff){
difficulty = diff;
playIntent.putExtra("difficulty", difficulty);
}
}
DifficultySurface
public class DifficultyActivity extends Activity {
private DifficultySurface surface;
private globalApp app;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
app = (globalApp) getApplication();
surface = new DifficultySurface(this, app, getIntent().getIntExtra("screenWidth", 500), getIntent().getIntExtra("screenHeight", 500));
setContentView(surface);
}
@Override
protected void onPause() {
super.onPause();
app.soundPool.release();
surface.pause();
overridePendingTransition(0, 0);
}
@Override
protected void onResume() {
super.onResume();
app.buildSoundPool();
app.loadSounds();
surface.resume();
}
}
GameActivity
public class DifficultySurface extends SurfaceView implements Runnable {
private SurfaceHolder surfaceHolder;
private Thread thread = null;
private Canvas canvas;
private Context context;
private globalApp app;
private boolean surfaceCreated = false;
private boolean running = false;
private MenuItem bgProp, arrowBarrel, okButton, diffVeryEasy, diffEasy, diffNormal, diffHard, diffVeryHard;
private int difficulty;
public DifficultySurface(Context ctx, globalApp a, int scrW, int scrH){
super(ctx);
context = ctx;
app = a;
surfaceHolder = getHolder();
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
surfaceCreated = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
});
difficulty = ((Activity)context).getIntent().getIntExtra("difficulty", 3);
bgProp = new MenuItem(ctx, "difficulty_background", 1024, 0, 0, scrW, scrH);
diffVeryEasy = new MenuItem(ctx, "very_easy",796, 100, 100, scrW, scrH);
diffEasy = new MenuItem(ctx, "easy",796, 100, 200 , scrW, scrH);
diffNormal = new MenuItem(ctx, "normal",796, 100, 300, scrW, scrH);
diffHard = new MenuItem(ctx, "hard",796, 100, 400 , scrW, scrH);
diffVeryHard = new MenuItem(ctx, "very_hard",796, 100, 500, scrW, scrH);
okButton = new MenuItem(ctx, "ok_button", 100, 924, 500, scrW, scrH);
arrowBarrel = new MenuItem(ctx, "barrel_arrow", 100, 0, 100*difficulty, scrW, scrH);
}
@Override
public void run() {
while (running) {
if (surfaceCreated) {
update();
draw();
}
}
}
private void update() {
arrowBarrel.setY(difficulty*100);
}
private void draw() {
if (surfaceHolder.getSurface().isValid()){
canvas = surfaceHolder.lockCanvas();
canvas.drawBitmap(bgProp.getBmp(), bgProp.getSourceRect(), bgProp.getDestRect(), null);
canvas.drawBitmap(arrowBarrel.getBmp(), arrowBarrel.getSourceRect(), arrowBarrel.getDestRect(), null);
canvas.drawBitmap(diffVeryEasy.getBmp(), diffVeryEasy.getSourceRect(), diffVeryEasy.getDestRect(), null);
canvas.drawBitmap(diffEasy.getBmp(), diffEasy.getSourceRect(), diffEasy.getDestRect(), null);
canvas.drawBitmap(diffNormal.getBmp(), diffNormal.getSourceRect(), diffNormal.getDestRect(), null);
canvas.drawBitmap(diffHard.getBmp(), diffHard.getSourceRect(), diffHard.getDestRect(), null);
canvas.drawBitmap(diffVeryHard.getBmp(), diffVeryHard.getSourceRect(), diffVeryHard.getDestRect(), null);
canvas.drawBitmap(okButton.getBmp(), okButton.getSourceRect(), okButton.getDestRect(), null);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & event.ACTION_MASK){
case MotionEvent.ACTION_UP:{
if (diffVeryEasy.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 1; }
if (diffEasy.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 2;
}
if (diffNormal.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 3;
}
if (diffHard.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 4;
}
if (diffVeryHard.contains((int) event.getX(), (int) event.getY())){
app.playTap();
difficulty = 5;
}
if (okButton.contains((int)event.getX(), (int) event.getY())){
app.playTap();
((Activity)context).getIntent().putExtra("difficulty", difficulty);
((Activity)context).setResult(Activity.RESULT_OK, ((Activity)context).getIntent());
((Activity)context).finish();
((Activity)context).overridePendingTransition(0, 0);
}
break;
}
}
return true;
}
public void pause(){
running = false;
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
((Activity)context).overridePendingTransition(0, 0);
}
public void resume(){
running = true;
thread = new Thread(this);
thread.start();
}
}
当我开始public class GameActivity extends Activity {
private GameSurface surface;
private globalApp app;
private int difficulty;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting full screen
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
decorView.setSystemUiVisibility(uiOptions);
difficulty = getIntent().getIntExtra("difficulty", 3);
app = (globalApp) getApplication();
surface = new GameSurface(this, app, getIntent().getIntExtra("screenWidth", 500), getIntent().getIntExtra("screenHeight", 500), difficulty);
surface.setParentActivity(this);
setContentView(surface);
}
@Override
protected void onPause() {
super.onPause();
app.soundPool.release();
surface.pause();
}
@Override
protected void onPostResume() {
super.onPostResume();
app.buildSoundPool();
app.loadSounds();
surface.resume();
}
@Override
protected void onStop() {
super.onStop();
surface.stop();
}
@Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
}
(我点击一个DificultyActivity
个对象但没有任何反应)或者当我开始MenuItem
时(游戏仍会显示GameActivity
+ {{} 1}})。
Android Monitor显示的内存分配不到40MB,因此我认为位图不应该成为问题。我尝试回收所有位图,但问题出现了(这就是为什么我选择只使用MainActivity
;起初我使用了所有像素密度,但尝试在导致暂停的情况下降低资源)。
答案 0 :(得分:0)
如果不查看代码,很难找到问题。没有任何特定于牛轧糖的处理资源的方式。
但是android N声称拥有更好的内存管理,并且因为你在抱怨很多垃圾收集,所以它可能是其中一个原因。确保回收未使用的位图。并使用RGB_565作为首选位图配置,需要比RGB_8888半内存。
答案 1 :(得分:0)
我已经解决了我的问题。在发布问题后,我遇到了this。看来我们遇到了同样的问题。当我减慢绘制速度时(使用thread.sleep
),没有更多问题。
感谢那些帮助过我的人。