简单的游戏适用于平板电脑,但有时会滞后于手机

时间:2017-10-01 11:09:12

标签: android performance-testing surfaceview samsung-mobile android-7.0-nougat

我正在开发简单的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 = 1inScaled = 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;起初我使用了所有像素密度,但尝试在导致暂停的情况下降低资源)。

2 个答案:

答案 0 :(得分:0)

如果不查看代码,很难找到问题。没有任何特定于牛轧糖的处理资源的方式。

但是android N声称拥有更好的内存管理,并且因为你在抱怨很多垃圾收集,所以它可能是其中一个原因。确保回收未使用的位图。并使用RGB_565作为首选位图配置,需要比RGB_8888半内存。

答案 1 :(得分:0)

我已经解决了我的问题。在发布问题后,我遇到了this。看来我们遇到了同样的问题。当我减慢绘制速度时(使用thread.sleep),没有更多问题。

感谢那些帮助过我的人。