Android SurfaceView onTouchEvent not getting calle

2019-08-11 22:20发布

问题:

I'm developing a game using SurfaceView which listens to touch events. The onTouchEvent method in SurfaceView works fine for many of the devices, but in some devices, sometimes it doesn't get called (Moto X Style is the one) and my app also stops responding.

I guess that this might be due to the overloading of main thread due to which onTouchEvent is starving.

Could some Android experts over here give me some tips to reduce the load on main thread if it's getting overloaded, or there might be some other reason which may cause this

The code is quite complex but still I'm posting some if you want to go through it

GameLoopThread

public class GameLoopThread extends Thread{
    private GameView view;
    // desired fps
    private final static int    MAX_FPS = 120;
    // maximum number of frames to be skipped
    private final static int    MAX_FRAME_SKIPS = 5;
    // the frame period
    private final static int    FRAME_PERIOD = 1000 / MAX_FPS;

    private boolean running = false;

    public GameLoopThread(GameView view){
        this.view = view;
    }

    public void setRunning(boolean running){
        this.running = running;
    }

    public boolean isRunning() {
        return running;
    }
    @Override
    public void run() {
        Canvas canvas;
        long beginTime;     // the time when the cycle begun
        long timeDiff;      // the time it took for the cycle to execute
        int sleepTime;      // ms to sleep (<0 if we're behind)
        int framesSkipped;  // number of frames being skipped

        while (running) {
            canvas = null;
            // try locking the canvas for exclusive pixel editing
            // in the surface
            try {
                canvas = view.getHolder().lockCanvas();
                synchronized (view.getHolder()) {
                    beginTime = System.nanoTime();
                    framesSkipped = 0;  // resetting the frames skipped
                    // update game state
                    // render state to the screen
                    // draws the canvas on the panel
                    this.view.draw(canvas);
                    // calculate how long did the cycle take
                    timeDiff = System.nanoTime() - beginTime;
                    // calculate sleep time
                    sleepTime = (int)(FRAME_PERIOD - timeDiff/1000000);

                    if (sleepTime > 0) {
                        // if sleepTime > 0 we're OK
                        try {
                            // send the thread to sleep for a short period
                            // very useful for battery saving
                            Thread.sleep(sleepTime);
                        } catch (InterruptedException e) {}
                    }

                    while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
                        // update without rendering
                        // add frame period to check if in next frame
                        sleepTime += FRAME_PERIOD;
                        framesSkipped++;
                    }
                }
            }
            finally {
                // in case of an exception the surface is not left in
                // an inconsistent state
                view.getHolder().unlockCanvasAndPost(canvas);
            }   // end finally
        }
    }
}

GameView

public class GameView extends SurfaceView {
    ArrayList<Bitmap> circles = new ArrayList<>();
    int color;
    public static boolean isGameOver;

    public GameLoopThread gameLoopThread;
    Circle circle;      // Code for Circle class is provided below

    public static int score = 0;
    public static int stars = 0;

    final Handler handler = new Handler();

    int remainingTime;

    boolean oneTimeFlag;

    Bitmap replay;
    Bitmap home;
    Bitmap star;

    int highScore;
    boolean isLeaving;

    public GameView(Context context, ArrayList<Bitmap> circles, int color) {
        super(context);
        this.circles = circles;
        this.color = color;

        oneTimeFlag = true;
        gameLoopThread = new GameLoopThread(GameView.this);

        getHolder().addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                if (!gameLoopThread.isRunning()) {
                    gameLoopThread.setRunning(true);
                    gameLoopThread.start();
                }
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

            }

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                gameLoopThread.setRunning(false);
                gameLoopThread = new GameLoopThread(GameView.this);
            }
        });

        initializeCircles();
        if(!gameLoopThread.isRunning()) {
            gameLoopThread.setRunning(true);
            gameLoopThread.start();
        }
    }

    public void initializeCircles() {
        ArrayList<String> numbers = new ArrayList<>();
        for(int i=0;i<10;i++)
            numbers.add(i+"");

        Random random = new Random();
        int position = random.nextInt(4);
        numbers.remove(color + "");

        int p1 = position;

        int r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
        numbers.remove(r1+"");

        int r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
        numbers.remove(r2 + "");

        int r3 = Integer.valueOf(numbers.get(random.nextInt(7)));

        ArrayList<Bitmap> bitmaps = new ArrayList<>();

        if(position == 0) {
            bitmaps.add(circles.get(color));
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(r3));
        }
        else if(position == 1) {
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(color));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(r3));
        }
        else if(position == 2) {
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(color));
            bitmaps.add(circles.get(r3));
        }
        else {
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(r3));
            bitmaps.add(circles.get(color));
        }

        numbers = new ArrayList<>();
        for(int i=0;i<10;i++)
            numbers.add(i+"");

        position = random.nextInt(4);
        numbers.remove(color + "");

        r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
        numbers.remove(r1 + "");

        r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
        numbers.remove(r2 + "");

        r3 = Integer.valueOf(numbers.get(random.nextInt(7)));

        if(position == 0) {
            bitmaps.add(circles.get(color));
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(r3));
        }
        else if(position == 1) {
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(color));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(r3));
        }
        else if(position == 2) {
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(color));
            bitmaps.add(circles.get(r3));
        }
        else {
            bitmaps.add(circles.get(r1));
            bitmaps.add(circles.get(r2));
            bitmaps.add(circles.get(r3));
            bitmaps.add(circles.get(color));
        }

        circle = new Circle(this, bitmaps, circles, p1, position, color, getContext());
    }

    @Override
    public void draw(Canvas canvas) {
        if(canvas != null) {
            super.draw(canvas);
            canvas.drawColor(Color.WHITE);

            if(!isGameOver && timer != null)
                stopTimerTask();
            try {
                circle.draw(canvas);
            } catch (GameOverException e) {
                isGameOver = true;
                if(isLeaving)
                    gameOver(canvas);
                else if(GameActivity.counter > 0) {
                    gameOver(canvas);
                    GameActivity.counter++;
                } else {
                    if (oneTimeFlag) {
                        int size1 = 200 * GameActivity.SCREEN_HEIGHT / 1280;
                        int size2 = 125 * GameActivity.SCREEN_HEIGHT / 1280;
                        float ratio = (float) GameActivity.SCREEN_HEIGHT / 1280;

                        replay = GameActivity.decodeSampledBitmapFromResource(getResources(), R.drawable.replay, size1, size1);
                        home = GameActivity.decodeSampledBitmapFromResource(getResources(), R.drawable.home, size2, size2);
                        continueButton = GameActivity.decodeSampledBitmapFromResource(getContext().getResources(), R.drawable.button, (int) (540 * ratio), (int) (100 * ratio));
                        star = GameActivity.decodeSampledBitmapFromResource(getContext().getResources(), R.drawable.star1, (int) (220 * ratio), (int) (220 * ratio));

                        int w = (int) ((float) GameActivity.SCREEN_WIDTH * 0.9);
                        oneTimeFlag = false;
                    }

                    if (askPurchaseScreen == 2) {
                        gameOver(canvas);
                    } else {
                        canvas.drawColor(Circle.endColor);
                    }
                }
            }
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        circle.onTouch(x, y);
        return true;
    }
}

Circle

public class Circle {
    int x;
    int y1;
    int y2;
    public static float speedY1 = 12.5f*(float)GameActivity.SCREEN_HEIGHT/1280;
    public static float speedY2 = 12.5f*(float)GameActivity.SCREEN_HEIGHT/1280;
    ArrayList<Bitmap> bitmaps;
    GameView gameView;
    int p1;     // Position of required circle in slot 1
    int p2;     // Position of required circle in slot 2
    int color;
    int tempColor;
    int width;
    Context context;

    // Centers of required circle
    float centerX1;
    float centerX2;
    float centerY1;
    float centerY2;

    ArrayList<Bitmap> circles = new ArrayList<>();
    boolean touchedFirst;
    boolean touchedSecond;
    int count1 = 1;     // Slot 1 circle radius animation
    int count2 = 1;     // Slot 2 circle radius animation

    float tempSpeedY1;
    float tempSpeedY2;
    boolean stopY1;
    boolean stopY2;
    int barCounter = 1;
    int loopCount = 0;

    int endGameCount = 0;   // Count to move circle upwards
    double limit;
    float endRadiusSpeed;
    int endSlot;    // Where you died
    int endRadiusCount = 0; // Count to increase circle radius
    int barEndCounter = 1;

    final Handler handler = new Handler();
    boolean exception;
    public static int endColor;

    public Circle(GameView gameView, ArrayList<Bitmap> bitmaps, ArrayList<Bitmap> circles, int p1, int p2, int color, Context context) {
        this.gameView = gameView;
        this.bitmaps = bitmaps;
        this.circles = circles;
        this.p1 = p1;
        this.p2 = p2;
        this.color = color;
        this.context = context;

        width = GameActivity.SCREEN_WIDTH / 4 - 10;
        x = 10;
        y1 = 0;
        y2 = -(GameActivity.SCREEN_HEIGHT + width) / 2;

        centerX1 = x + p1 * (10 + width) + width / 2;
        centerY1 = y1 + width / 2;

        centerX2 = x + p2 * (10 + width) + width / 2;
        centerY2 = y2 + width / 2;
    }

    public void update() throws GameOverException {
        y1+= speedY1;
        y2+= speedY2;

        centerY1+= speedY1;
        centerY2+= speedY2;

        float ratio = (float)GameActivity.SCREEN_HEIGHT/1280;
        limit = width/(20*ratio);

        if(y1 >= gameView.getHeight()) {
            loopCount++;

            if(touchedFirst)
                touchedFirst = false;
            else {
                speedY1 = speedY2 = -(12.5f * ratio);
                endColor = bitmaps.get(p1).getPixel(width/2, width/2);

                endGameCount += 1;
                endSlot = 1;
            }

            if(endGameCount == 0) {
                if (stopY1) {
                    tempSpeedY1 = speedY1;
                    speedY1 = 0;
                    ArrayList<Integer> numbers = new ArrayList<>();
                    for (int i = 0; i < 10; i++) {
                        if (i != color)
                            numbers.add(i);
                    }
                    tempColor = numbers.get(new Random().nextInt(9));
                }

                y1 = -(gameView.getWidth() / 4 - 10);
                count1 = 1;
                setBitmaps(1);
            }
        }
        else if(y2 >= gameView.getHeight()) {
            loopCount++;

            if(touchedSecond)
                touchedSecond = false;
            else {
                speedY1 = speedY2 = -(12.5f * ratio);
                endColor = bitmaps.get(p2 + 4
                ).getPixel(width/2, width/2);
                endGameCount += 1;
                endSlot = 2;
            }

            if(endGameCount == 0) {
                if (stopY2) {
                    tempSpeedY2 = speedY2;
                    speedY2 = 0;
                }

                y2 = -(gameView.getWidth() / 4 - 10);
                count2 = 1;
                setBitmaps(2);
            }
        }
    }

    public void setBitmaps(int slot) {
        ArrayList<String> numbers = new ArrayList<>();
        for(int i=0;i<10;i++)
            numbers.add(i+"");

        Random random = new Random();
        int position = random.nextInt(4);
        numbers.remove(color + "");

        int r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
        numbers.remove(r1+"");

        int r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
        numbers.remove(r2 + "");

        int r3 = Integer.valueOf(numbers.get(random.nextInt(7)));

        if(position == 0) {
            bitmaps.set((slot - 1)*4, circles.get(color));
            bitmaps.set((slot - 1)*4 + 1, circles.get(r1));
            bitmaps.set((slot - 1)*4 + 2, circles.get(r2));
            bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
        }
        else if(position == 1) {
            bitmaps.set((slot - 1)*4, circles.get(r1));
            bitmaps.set((slot - 1)*4 + 1, circles.get(color));
            bitmaps.set((slot - 1)*4 + 2, circles.get(r2));
            bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
        }
        else if(position == 2) {
            bitmaps.set((slot - 1)*4, circles.get(r1));
            bitmaps.set((slot - 1)*4 + 1, circles.get(r2));
            bitmaps.set((slot - 1)*4 + 2, circles.get(color));
            bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
        } else {
            bitmaps.set((slot - 1)*4,circles.get(r1));
            bitmaps.set((slot - 1)*4 + 1,circles.get(r2));
            bitmaps.set((slot - 1)*4 + 2,circles.get(r3));
            bitmaps.set((slot - 1)*4 + 3,circles.get(color));
        }
        if(slot == 1) {
            p1 = position;
            centerX1 = x+position*(10 + width) + width/2;
            centerY1 = y1 + width/2;
        }
        else if(slot == 2) {
            p2 = position;
            centerX2 = x+position*(10 + width) + width/2;
            centerY2 = y2 + width/2;
        }
    }

    public void onTouch(float X, float Y) {
        int radius = (gameView.getWidth() / 4 - 10) / 2;

        if(endGameCount == 0) {
            if ((X >= centerX1 - radius) && (X <= centerX1 + radius) && (Y >= centerY1 - radius) && (Y <= centerY1 + radius)) {
                GameView.score++;
                touchedFirst = true;
                centerX1 = centerY1 = -1;

                if(p1 == (timerCount - 1) && timer != null && starSlot == 1) {
                    GameView.stars++;
                    starCollected = true;
                    timerCount = 0;
                    stopTimerTask(0);
                }
            } else if ((X >= centerX2 - radius) && (X <= centerX2 + radius) && (Y >= centerY2 - radius) && (Y <= centerY2 + radius)) {
                GameView.score++;
                touchedSecond = true;
                centerX2 = centerY2 = -1;

                if(p2 == (timerCount - 1) && timer != null && starSlot == 2) {
                    GameView.stars++;
                    starCollected = true;
                    timerCount = 0;
                    stopTimerTask(0);
                }
            } else {
                endSlot = 0;
                if ((Y >= centerY1 - radius) && (Y <= centerY1 + radius)) {
                    endSlot = 1;
                    if (X >= 10 && X <= 10 + 2 * radius) {
                        p1 = 0;
                        centerX1 = 10 + radius;
                    } else if (X >= 20 + 2 * radius && X <= 20 + 4 * radius) {
                        p1 = 1;
                        centerX1 = 20 + 3 * radius;
                    } else if (X >= 30 + 4 * radius && X <= 30 + 6 * radius) {
                        p1 = 2;
                        centerX1 = 30 + 5 * radius;
                    } else if (X >= 40 + 6 * radius && X <= 40 + 8 * radius) {
                        p1 = 3;
                        centerX1 = 40 + 2 * radius;
                    } else
                        endSlot = 0;
                } else if ((Y >= centerY2 - radius) && (Y <= centerY2 + radius)) {
                    endSlot = 2;
                    if (X >= 10 && X <= 10 + 2 * radius) {
                        p2 = 0;
                        centerX2 = 10 + radius;
                    } else if (X >= 20 + 2 * radius && X <= 20 + 4 * radius) {
                        p2 = 1;
                        centerX2 = 20 + 3 * radius;
                    } else if (X >= 30 + 4 * radius && X <= 30 + 6 * radius) {
                        p2 = 2;
                        centerX2 = 30 + 5 * radius;
                    } else if (X >= 40 + 6 * radius && X <= 40 + 8 * radius) {
                        p2 = 3;
                        centerX2 = 40 + 2 * radius;
                    } else
                        endSlot = 0;
                }
                if (endSlot != 0) {
                    speedY1 = speedY2 = 0;
                    limit = endGameCount = 6;

                    if (endSlot == 1) {
                        endColor= bitmaps.get(p1).getPixel(width/2, width/2);
                    } else {
                        endColor = bitmaps.get(p2 + 4).getPixel(width/2, width/2);
                    }
                }
            }

            if (GameView.score % 5 == 0 && GameView.score <= 110 && barCounter == 1) {
                float ratio = (float)GameActivity.SCREEN_HEIGHT/1280;
                speedY1 += ratio*0.5;
                speedY2 += ratio*0.5;
            }

            if (GameView.score > 0 && GameView.score % 15 == 14) {
                if(isOddScore)
                    stopY1 = true;
                else
                    stopY2 = true;
            }

            if (GameView.score > 0 && GameView.score % 15 == 0 && barCounter == 1) {
                if(isOddScore)
                    stopY2 = true;
                else
                    stopY1 = true;
            }

            if (GameView.score % 15 == 1)
                barCounter = 1;
        }
    }
    public void draw(Canvas canvas) throws GameOverException {
        GameView.isGameOver = false;
        if(exception)
            throw new GameOverException(color);
        update();

        for(int i=0;i<bitmaps.size();i++) {
            if(i<4) {
                Rect rect = new Rect(x+i*(10 + width),y1,(x+width)*(i+1),y1+width);
                if(endGameCount == Math.ceil(limit) && endSlot == 1) {
                    if(i == p1) {
                        endRadiusCount += 1;
                        if (endRadiusCount > 23) {
                            star.recycle();
                            loopCount = loopCount%starInterval;
                            Cryptography.saveFile((loopCount + "").getBytes(), context, "interval");
                            endGameCount = 0;
                            exception = true;
                            throw new GameOverException(color);
                        }

                        rect = new Rect(x + i * (10 + width) - endRadiusCount*(int)Math.ceil(endRadiusSpeed), y1 - endRadiusCount*(int)Math.ceil(endRadiusSpeed), (x + width) * (i + 1) + endRadiusCount*(int)Math.ceil(endRadiusSpeed), y1 + width + endRadiusCount*(int)Math.ceil(endRadiusSpeed));
                        canvas.drawBitmap(bitmaps.get(i), null, rect, null);
                    }
                }

                // TOUCH ANIMATION : DIMINISH CIRCLE
                else if(i==p1 && touchedFirst) {
                    rect = new Rect(x + i * (10 + width) + 3*count1 + ((int)speedY1-15), y1 + 3*count1 + ((int)speedY1-15), (x + width) * (i + 1) - 3*count1 - ((int)speedY1-15), y1 + width - 3*count1 - ((int)speedY1-15));
                    canvas.drawBitmap(bitmaps.get(i), null, rect, null);
                    count1++;
                }

                else if(endSlot != 2) {
                    canvas.drawBitmap(bitmaps.get(i), null, rect, null);
                    if(timerCount > 0 && starSlot == 1) {
                        int size = width * 30 / 50;
                        int difference = (width - size) / 2;
                        Rect starRect = new Rect(x + (timerCount - 1) * (10 + width) + difference, y1 + difference, (x + width) * (timerCount) - difference, y1 + width - difference);
                        canvas.drawBitmap(star, null, starRect, null);
                    }
                }
            }
            if(i >= 4) {
                Rect rect = new Rect(x + (i % 4) * (10 + width), y2, (x + width) * ((i % 4) + 1), y2 + width);
                if(endGameCount == Math.ceil(limit) && endSlot == 2) {
                    if((i%4)==p2) {
                        endRadiusCount += 1;
                        if (endRadiusCount > 23) {
                            star.recycle();
                            loopCount = loopCount%starInterval;
                            Cryptography.saveFile((loopCount + "").getBytes(), context, "interval");
                            endGameCount = 0;
                            exception = true;
                            throw new GameOverException(color);
                        }

                        rect = new Rect(x + (i % 4) * (10 + width) - endRadiusCount*(int)Math.ceil(endRadiusSpeed), y2 - endRadiusCount*(int)Math.ceil(endRadiusSpeed), (x + width) * ((i % 4) + 1) + endRadiusCount*(int)Math.ceil(endRadiusSpeed), y2 + width + endRadiusCount*(int)Math.ceil(endRadiusSpeed));
                        canvas.drawBitmap(bitmaps.get(i), null, rect, null);
                    }
                }
                else if((i%4)==p2 && touchedSecond) {
                    rect = new Rect(x + (i % 4) * (10 + width) + 3*count2 + ((int)speedY1-15), y2 + 3*count2 + ((int)speedY1-15), (x + width) * ((i % 4) + 1) - 3*count2 - ((int)speedY1-15), y2 + width - 3*count2 - ((int)speedY1-15));
                    canvas.drawBitmap(bitmaps.get(i), null, rect, null);
                    count2++;
                }
                else if(endSlot != 1) {
                    canvas.drawBitmap(bitmaps.get(i), null, rect, null);
                    if(timerCount > 0 && starSlot == 2) {
                        int size = width * 30 / 50;
                        int difference = (width - size) / 2;
                        Rect starRect = new Rect(x + (timerCount - 1) * (10 + width) + difference, y2 + difference, (x + width) * (timerCount) - difference, y2 + width - difference);
                        canvas.drawBitmap(star, null, starRect, null);
                    }
                }
            }
        }
        Rect src = new Rect(circles.get(color).getWidth()/2 - 10,circles.get(color).getHeight()/2 - 10,circles.get(color).getWidth()/2 + 10,circles.get(color).getHeight()/2 + 10);
        Rect dst;

        Paint paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setTextAlign(Paint.Align.RIGHT);
        paint.setTypeface(Typeface.SANS_SERIF);
        paint.setTextSize(72 * ratio);
        canvas.drawText(GameView.score + " ", GameActivity.SCREEN_WIDTH, width / 2, paint);

        dst = new Rect(5,5, (int) (120 * ratio - 5), (int) (120 * ratio - 5));
        canvas.drawBitmap(star,null,dst,null);
        paint.setTextAlign(Paint.Align.LEFT);
        canvas.drawText("" + GameView.stars, 120 * ratio, width/2, paint);
    }
}

回答1:

Don't override draw(). That's used to render the View, not the Surface, and you generally shouldn't override that method even if you're creating a custom View:

When implementing a view, implement onDraw(android.graphics.Canvas) instead of overriding this method.

SurfaceViews have two parts, the Surface and the View. The View part is handled like any other View, but is generally just a transparent "hole" in the layout. The Surface is a separate layer that, by default, sits behind the View layer. Whatever you draw on the Surface "shows through" the transparent hole.

By overriding draw() you're drawing on the View whenever the View UI is invalidated. You're also calling draw() from the render thread, so you're drawing on the Surface, but with default Z-ordering you can't see that because the View contents are fully opaque. You will reduce your impact on the UI thread by not drawing everything in two different layers.

Unless you're deliberately drawing on the View, it's best to avoid subclassing SurfaceView entirely, and just use it as a member.

Because your draw code is synchronized, the two draw passes will not execute concurrently. That means your View layer draw call will block waiting for the Surface layer rendering to complete. Canvas rendering on a Surface is not hardware-accelerated, so if you're touching a lot of pixels it can get slow, and the UI thread will have to wait for it to run. That wouldn't be so bad, but you're holding on to the mutex while you're sleeping, which means the only opportunity for the main UI thread to run comes during the brief instant when the loop wraps around. The thread scheduler does not guarantee fairness, so it's entirely possible to starve the main UI thread this way.

If you change @override draw() to myDraw() things should get better. You should probably move your sleep call out of the synchronized block just on general principles, or work to eliminate it entirely. You might also want to consider using a custom View instead of SurfaceView.

On an unrelated note, you should probably avoid doing this every update:

Random random = new Random();

for the reasons noted here.



回答2:

Successfully solved the issue. Can't imagine that the solution would be this much simple as compared to the problem that I was considering that complex. Just reduced the frame rate from 120 to 90 and guess what, it worked like charm!

Due to a high frame rate, the SurfaceView was busy doing all the drawing and onTouchEvent() method had to starve