致命异常(线程)

时间:2014-01-14 14:32:06

标签: android multithreading

我想在我的Android应用程序中制作一些水涟漪,所以我使用这个javascript代码http://jsfiddle.net/esteewhy/5Ht3b/6/转换为C代码,就像它显示在Live Wallpaper Water Ripple Effect(由esteewhy)。

我有一个主要活动,点击按钮后调用水波纹活动。我的可变位图水波纹很好用,问题是当我点击Android设备的“后退”按钮时,系统崩溃抛出这个错误:

FATAL EXCEPTION: Thread-2556
java.lang.NullPointerException
at com.wheelly.whater.WaterView.onDraw (line 149)
at com.wheelly.whater.WaterView$GameThread.run (line 255)

我想处理Thread或活动本身存在一些问题。

以下是水涟漪的代码:

public class WaterView extends SurfaceView implements SurfaceHolder.Callback {
    GameThread thread;

    public static Bitmap icon;

    //Measure frames per second.
    long now;
    int framesCount=0;
    int framesCountAvg=0;
    long framesTimer=0;
    Paint fpsPaint=new Paint();

    //Frame speed
    long timeNow;
    long timePrev = 0;
    long timePrevFrame = 0;
    long timeDelta;

    private int width = 480;
    private int height = 800;

    private short riprad = 6;
    boolean flip;
    private short[] ripplemap, last_map;
    Bitmap ripple;

    private static final String TAG = null;

    private Rippler rippler;

    public WaterView(Context context) {
        super(context);
        initialize();
    }

    void initialize() {
        //CB 
        icon = BitmapFactory.decodeResource(getResources(), R.drawable.sand100);
        icon = convertToMutable(icon);
        //--
        rippler = new NativeRippler();
        reinitgGlobals();
        fpsPaint.setTextSize(30);

        //Set thread
        getHolder().addCallback(this);

        setFocusable(true);
    }

    void reinitgGlobals() {
        int size = width * (height + 2) * 2;
        ripplemap = new short[size];
        last_map = new short[size];
        Bitmap texture = createBackground(width, height); // this creates a MUTABLE bitmap
        ripple = texture;
        _td = new int[width * height];
        texture.getPixels(_td, 0, width, 0, 0, width, height);
        _rd = new int[width * height];
    }

    void randomizer() {
        final Random rnd = new Random();
        final Handler disHAndler = new Handler();
        final Runnable disturbWater = new Runnable() {
            @Override
            public void run() {
                disturb(rnd.nextInt(width), rnd.nextInt(height));
                disHAndler.postDelayed(this, 7000);
            }
        };
        disHAndler.post(disturbWater);
    }

    private static Bitmap createBackground(int width, int height) {
        Canvas cb = new Canvas (icon);
        cb.save();
        cb.restore();
        return icon;
    }


    private Bitmap convertToMutable(Bitmap bitmap) {
        try {
            File file = new File("/mnt/sdcard/sample/temp.txt");
            file.getParentFile().mkdirs();
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");

            int width_mutable = bitmap.getWidth();
            int height_mutable = bitmap.getHeight();

            FileChannel channel = randomAccessFile.getChannel();
            MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width_mutable*height_mutable*4);
            bitmap.copyPixelsToBuffer(map);
            bitmap.recycle();

            bitmap = Bitmap.createBitmap(width_mutable, height_mutable, Config.ARGB_8888);
            map.position(0);
            bitmap.copyPixelsFromBuffer(map);

            channel.close();
            randomAccessFile.close();

        } catch (FileNotFoundException e) {
            Log.i(TAG, "::onActivityResult:" + ""+Log.getStackTraceString(e));
        } catch (IOException e) {
            Log.i(TAG, "::onActivityResult:" + ""+Log.getStackTraceString(e));
        }
        return bitmap;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        width = w;
        height = h;
        reinitgGlobals();
    }

    @Override
    protected void onDraw(android.graphics.Canvas canvas) {
        super.onDraw(canvas);
        newframe();
        canvas.drawBitmap(ripple, 0, 0, null);

        //Measure frame rate (unit: frames per second).
        now=System.currentTimeMillis();
        canvas.drawText(framesCountAvg+" fps", 40, 70, fpsPaint);
        framesCount++;
        if(now-framesTimer>1000) {
                framesTimer=now;
                framesCountAvg=framesCount;
                framesCount=0;
        }
    }

    /**
     * Disturb water at specified point
     */
    private void disturb(int dx, int dy) {
        rippler.disturb(dx, dy, width, height, riprad, ripplemap, flip);
    }

    int[] _td;
    int[] _rd;

    /**
     * Generates new ripples
     */
    private void newframe() {
        System.arraycopy(_td, 0, _rd, 0, width * height);
        flip = !flip;
        rippler.transformRipples(height, width, ripplemap, last_map, _td, _rd, flip);
        ripple.setPixels(_rd, 0, width, 0, 0, width, height);
    }


    @Override
    public synchronized boolean onTouchEvent(MotionEvent event) {
            disturb((int)event.getX(), (int)event.getY());
            return true;

    }

    @Override
    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    }

    @Override
    public void surfaceCreated(SurfaceHolder arg0) {
        thread = new GameThread(getHolder(), this);
        thread.setRunning(true);
        thread.start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder arg0) {
        boolean retry = true;
        thread.setRunning(false);
        while (retry) {
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {

            }
        }
    }

    class GameThread extends Thread {
        private SurfaceHolder surfaceHolder;
        private WaterView gameView;
        private boolean run = false;

        public GameThread(SurfaceHolder surfaceHolder, WaterView gameView) {
            this.surfaceHolder = surfaceHolder;
            this.gameView = gameView;
        }

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

        public SurfaceHolder getSurfaceHolder() {
            return surfaceHolder;
        }

        @Override
        public void run() {
            Canvas c;
            while (run) {
                c = null;

                //limit frame rate to max 60fps
                timeNow = System.currentTimeMillis();
                timeDelta = timeNow - timePrevFrame;
                if ( timeDelta < 16) {
                    try {
                        Thread.sleep(16 - timeDelta);
                    }
                    catch(InterruptedException e) {
                    }
                }
                timePrevFrame = System.currentTimeMillis();

                try {
                    c = surfaceHolder.lockCanvas(null);
                    synchronized (surfaceHolder) {
                       //call methods to draw and process next fame
                        gameView.onDraw(c);
                    }
                } finally {
                    if (c != null) {
                        surfaceHolder.unlockCanvasAndPost(c);
                    }
                }
            }
        }
    }
}

3 个答案:

答案 0 :(得分:2)

我找到了解决方案:

我不得不在活动的onPause方法中中断线程,而不是在onStop方法中执行它。现在它运作正常。

非常感谢大家帮助我解决这个问题。

答案 1 :(得分:1)

你应该在方法Activity.onStop

中关闭你的工作线程
    @Override
    protected void onStop() {
        thread.interrupt();
        super.onStop();
    }

在GameThread的run方法中,在调用gameView.onDraw(c)之前检查线程是否被中断;像这样:

    if(isInterrupted()) {
        break;
    }
    gameView.onDraw(c);
顺便说一句,如果用gameView.invalidate()替换gameView.onDraw(c)可能会更好;

答案 2 :(得分:1)

不要直接在视图上调用onDraw,而是调用gameView.invalidate()

synchronized (surfaceHolder) {
    //call methods to draw and process next fame
    gameView.invalidate();
}

如果它不起作用,请尝试使用带有断点的调试器来查看哪个变量完全为空