停止线程无法使用函数setRunning(false)

时间:2015-01-02 09:58:26

标签: java android multithreading canvas

我有一个使用画布绘制线条的项目。我的项目(下载here)将创建一个随机数,并使用画布将它们连接起来。它运作良好。但问题是,当我的应用程序运行时,我单击HOME按钮,然后我的应用程序将崩溃,如

FATAL EXCEPTION: Thread-10001
java.lang.NullPointerException
at com.example.waveplot.WaveformView.PlotPoints(WaveformView.java:78)
at com.example.waveplot.WaveformPlotThread.run(WaveformPlotThread.java:35)

如何解决?非常感谢。 让我解释一下我的应用我的申请包括三个类:

WaveformView.java: class to draw the line in thread with random number.
MainActivity.java: main calss and create the random number and call WaveformView class
WaveformPlotThread: create a thread and manager canvas

这是我的代码 MainActivity.java

        private Runnable Timer_Tick = new Runnable() {
        public void run() {
            numRandom=genNum(0,200)-100;// from -100 to 100
            if(dataIndex1<MAX_SAMPLES) 
                dataY[dataIndex1++] = numRandom;
            else
            {
                dataY[MAX_SAMPLES-1] = numRandom;
               for (int i = 0; i <  WIDTH/10 - 1; i++)
                   //Shift data
                   dataY[i]=dataY[i+1];

            }
            mWaveform.set_data(dataY);
        }
     };

WaveformView.java

public class WaveformView extends SurfaceView implements SurfaceHolder.Callback{

    // plot area size
    private final static int WIDTH = 660;
    private static int[] dataX = new int[WIDTH];
    private static int[] dataY = new int[WIDTH];
    private WaveformPlotThread plot_thread; 
    private Paint dataY_color = new Paint();
    private int  index = 0;

    public WaveformView(Context context, AttributeSet attrs){
        super(context, attrs);

        getHolder().addCallback(this);
        plot_thread = new WaveformPlotThread(getHolder(), this);        
        dataY_color.setColor(Color.GREEN);
        dataY_color.setStrokeWidth(3);

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

    }   
    @Override
    public void surfaceCreated(SurfaceHolder holder){
        plot_thread.setRunning(true);
        plot_thread.start();
        //plot_thread.startThread();
    }   
    @Override
    public void surfaceDestroyed(SurfaceHolder holder){
        Log.d("D","STOOOOOOOOOOOOOOOOOOP");

        boolean retry = true;
        plot_thread.setRunning(false);
        while (retry){
            try{
                plot_thread.join();
                retry = false;
            }catch(InterruptedException e){

            }
        }
    }

    @Override
    public void onDraw(Canvas canvas){
        PlotPoints(canvas);

    }

    public void set_data(int[] data1){      
        plot_thread.setRunning(false);
        if(index<WIDTH/10-1)
            index++;
        for(int x=0; x<WIDTH/10-1; x++){
            if(x<(data1.length)){
            dataX[x+1]=(x+1)*WIDTH/66;
            dataY[x] = data1[x];
            }
            else{
                dataY[x] = 0;
            }
        }
        plot_thread.setRunning(true);
    }

    public void PlotPoints(Canvas canvas){
        if(canvas == null) { return ; }
        // clear screen
        canvas.drawColor(Color.rgb(20, 20, 20));
        // plot data
        for(int x=0; x<index-1; x++){           
            canvas.drawLine(dataX[x], dataY[x], dataX[x+1], dataY[x+1], dataY_color);
        }

    }

}

WaveformPlotThread

public class WaveformPlotThread extends Thread {
    private SurfaceHolder holder;
    private WaveformView plot_area;
   // private volatile Thread runner;
    private volatile boolean _run=true;

    public WaveformPlotThread(SurfaceHolder surfaceHolder, WaveformView view){
        holder = surfaceHolder;
        plot_area = view;
    }
    public void setRunning(boolean run){
        _run = run;
    }
    @Override
    public void run(){
        Canvas c;
        while(_run){
        //while(Thread.currentThread() == runner){
            c = null;
            try{
                c = holder.lockCanvas(null);
                synchronized (holder) {
                    plot_area.PlotPoints(c);
                }
            }finally{
                if(c!=null){
                    holder.unlockCanvasAndPost(c);
                }
            }
        }
    }
}

这是日志文件

 D/D(9747): STOOOOOOOOOOOOOOOOOOP
 W/SurfaceView(9747): CHECK surface infomation creating=false formatChanged=false sizeChanged=false visible=false visibleChanged=true surfaceChanged=true realSizeChanged=false redrawNeeded=false left=false top=false
 W/System.err(9747): java.lang.IllegalThreadStateException: Thread already started.
 W/System.err(9747):    at java.lang.Thread.start(Thread.java:1045)
 W/System.err(9747):    at com.example.waveplot.WaveformView.surfaceCreated(WaveformView.java:41)
 W/System.err(9747):    at android.view.SurfaceView.updateWindow(SurfaceView.java:609)
 W/System.err(9747):    at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:235)
 W/System.err(9747):    at android.view.View.dispatchWindowVisibilityChanged(View.java:7625)
 W/System.err(9747):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1047)
 W/System.err(9747):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1047)
 W/System.err(9747):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1047)
 W/System.err(9747):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1047)
 W/System.err(9747):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1047)
 W/System.err(9747):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:1047)
 W/System.err(9747):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1346)
 W/System.err(9747):    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1121)
 W/System.err(9747):    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4598)
 W/System.err(9747):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
 W/System.err(9747):    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
 W/System.err(9747):    at android.view.Choreographer.doFrame(Choreographer.java:525)
 W/System.err(9747):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
 W/System.err(9747):    at android.os.Handler.handleCallback(Handler.java:615)
 W/System.err(9747):    at android.os.Handler.dispatchMessage(Handler.java:92)
 W/System.err(9747):    at android.os.Looper.loop(Looper.java:137)
 W/System.err(9747):    at android.app.ActivityThread.main(ActivityThread.java:4950)
 W/System.err(9747):    at java.lang.reflect.Method.invokeNative(Native Method)
 W/System.err(9747):    at java.lang.reflect.Method.invoke(Method.java:511)
 W/System.err(9747):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:997)
 W/System.err(9747):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:764)
 W/System.err(9747):    at dalvik.system.NativeStart.main(Native Method)

解决方案:根据Veritas的建议,我将该函数设置为在SurfaceCreated中创建新线程

@Override
public void surfaceCreated(SurfaceHolder holder){
    plot_thread = new WaveformPlotThread(getHolder(), this);
    plot_thread.setRunning(true);
    plot_thread.start();

}   
public WaveformView(Context context, AttributeSet attrs){
    super(context, attrs);

    getHolder().addCallback(this);
    //plot_thread = new WaveformPlotThread(getHolder(), this);      
    dataY_color.setColor(Color.GREEN);
    dataY_color.setStrokeWidth(3);

}

3 个答案:

答案 0 :(得分:1)

四件事

  1. 检查PlotPoints方法中canvas是否为空,

    if(canvas == null){        回归; //默默地回归     }

    可能在表面上被破坏的surfaceholder会返回一个空画布

  2. 正如isalgueiro建议make _run为volatile /

  3. 将_run的默认值设为true,因为如果某个地方你启动这个线程而没有将_run设置为true,那么线程很可能在你可以用它做任何有建设性的事情之前结束。默认值false在您的情况下没有意义。

  4. 不要扩展线程而是实现runnable。

  5. 最后两点是更多最佳做法..

    - 编辑 - 暂时在surfaceCreated中创建一个新线程然后启动它,因为当_run设置为false时退出while循环的逻辑将使线程完成其run方法并且线程将死亡。错误是自我指示,一旦线程已经启动,就无法启动它。要么通过更正_run = false / true的逻辑来重用您的线程,要么使用Executor ThreadPool采用更好的解决方案,请参阅http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html。 暂时你可以在surfacecreated方法而不是构造函数

    中创建一个新线程

答案 1 :(得分:1)

我遇到了同样的问题。我无法弄清楚为什么Thread在调用setRunning(false)时不会停止,但我设法绕过NullPointerException,将plot_area.PlotPoints()包裹在另一个if(canvas != null)语句中。因此,run()中的WaveformPlotThread方法看起来像这样:

@Override
public void run(){
    Canvas c;
    while(_run){
    //while(Thread.currentThread() == runner){
        c = null;
        try{
            c = holder.lockCanvas(null);
            synchronized (holder) {
                if(c!=null) {
                    plot_area.PlotPoints(c);
                }
            }
        }finally{
            if(c!=null){
                holder.unlockCanvasAndPost(c);
            }
        }
    }
}

此外,作为附注,@Override public void onDraw(Canvas canvas)不是必需的,不建议覆盖onDraw()draw()。整个方法都可以删除。

答案 2 :(得分:0)

使_run易变,因此读写是原子的。见http://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

public class WaveformPlotThread extends Thread {
    private SurfaceHolder holder;
    private WaveformView plot_area;
    private volatile boolean _run = false;