2012-08-17 39 views
1

我正在开发针对Android 2.2+的简单视频录制应用程序,并且无法按预期方式使Timer线程正常工作。代码如下,所以步骤如下:在Java中使用通知/等待来停止/重新启动线程

  1. 当用户pres开始录制按钮时,开始录制并调用fightTimer.start()方法。
  2. 它调用timer.start()方法开始运行线程。定时器是fightTimer内部的线程对象
  3. 当用户单击停止按钮时,调用stopTimer方法stop()。在那里,我设置了一个标志stopTimer = true,并停止线程方法运行并调用timer.wait()方法,以便线程在用户单击开始录制按钮时等待
  4. 然后再次调用fightTimer.start()方法。它调用线程timer.start(),因为计时器已经启动,但是它会抛出异常。
  5. 我发现异常并调用fightTimer.restart()方法。此方法设置标志stopTimer我在步骤3中设置为true,所以现在我们有stopTimer = true。计时器线程仍在运行的内等待()方法
  6. 然后调用timer.notify(),让定时器知道,它不需要等待了,可以继续运行
  7. 现在我期待的计时器线程启动再次运行,但由于某种原因,此时执行跳转到调用notify()(restart())的同一方法的开头,并设置标志stopTimer = false,然后尝试再次通知定时器线程。它引发运行时异常,并且它是结束的地方。

我认为我对线程的整体同步的理解是不正确的,所以如果任何人都能指出我搞砸的地方那会很好。下面是FightTimeris的代码,我甚至没有在logCat中获得任何输出信息。就像我说的任何帮助将不胜感激,因为我不明白为什么会发生这种情况,以及如何解决它。

public class FightTimer extends SurfaceView implements Runnable, SurfaceHolder.Callback { 

     public Thread timer; 
     public SurfaceHolder surfaceHolder; 
     public Canvas canvas; 
     public int counter=0; 
     public volatile boolean stopTimer=false; 
     public volatile boolean pauseTimer=false; 

     public FightTimer(Context context) 
     { 
      super(context); 

      surfaceHolder=getHolder(); 

      // this is necessary to make surfaceview transparent 
      surfaceHolder.setFormat(PixelFormat.TRANSLUCENT); 
      setZOrderOnTop(true); 

      surfaceHolder.addCallback(this); 

      timer=new Thread(this); 
     } 


     public void start() 
     { 
      try 
      { 
       timer.start(); 
      } 
      catch(IllegalThreadStateException ex) 
      { 
       reStart(); 
      } 
     } 

     public synchronized void reStart() 
     { 
// here the method is executed twice as I described in step 7 
// after notify() it actually jumps back to stopTimer=false again and then exits the function. Then outside of this object I catch RuntimeException   
      stopTimer=false; 
        timer.notify(); 
     } 

     public synchronized void pause() 
     { 
      pauseTimer=true; 
     } 

     public synchronized void resume() 
     { 
      pauseTimer=false; 
      timer.notify(); 
     } 

     public void stop() 
     { 
      stopTimer=true; 
     } 

     public void run() { 

      TimeWatch timeWatch=TimeWatch.start(); 

      Paint paint=new Paint(); 

      paint.setColor(Color.RED); 

      paint.setTypeface(Typeface.create("Arial",Typeface.NORMAL)); 
      paint.setTextSize(20); 

      while(true) 
      { 
       // this is to pause timer 

       try 
       { 
        if(pauseTimer) 
        { 
         synchronized(timer) { 
          while(pauseTimer) 
           timer.wait(); 
         } 

         // TODO heres the code should be executed when timer is resumed eg. 
         // maybe calculate how timer should count now as it wasn't counting for a while etc 

        } 
       } catch(InterruptedException ex) 
       { 

       } 

       // this is to pause timer 

       try 
       { 
        if(stopTimer) 
        { 
         synchronized(timer) { 
          while(stopTimer) 
           timer.wait(); 
         } 

         // TODO heres the code should be executed when timer is restarted 
         // maybe reset timer completely etc 

         timeWatch.reset(); 
        } 
       } catch(InterruptedException ex) 
       { 

       } 

       canvas=surfaceHolder.lockCanvas(); 

       // canvas might not exists at this point as we might be in activitis onStop() callback and stopping preview 
       // etc. so we need to check if so then we exit run function 

       if(canvas==null) return; 

       //canvas.drawARGB(0,255,255,255); 
       canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR); 

       long minutes=timeWatch.time(TimeUnit.SECONDS)/60; 

       canvas.drawText(counter+"  "+minutes+":"+timeWatch.time(TimeUnit.SECONDS)%60,0,counter%60, paint); 

       counter++; 

       surfaceHolder.unlockCanvasAndPost(canvas); 
      } 

     } 

     public void surfaceChanged(SurfaceHolder holder, int format, int width, 
       int height) { 
      // TODO Auto-generated method stub 

      //Toast.makeText(getContext(), "Surface Changed", Toast.LENGTH_LONG).show(); 

     } 

     public void surfaceCreated(SurfaceHolder holder) { 
      // TODO Auto-generated method stub 

      //timer.start(); 
     } 

     public void surfaceDestroyed(SurfaceHolder holder) { 
      // TODO Auto-generated method stub 
      // when surface is destroyed it means it cannot be displayed anymore and there is no canvas to draw 
      // meaning the run() method cannot draw anything and calls to surfaceHolder will throw exception 
      // so we need to stop thread here 
      // this will happen when activity is in onStop() callback and when is already invisible and we are going to 
      // remove the object anyway so we don't care what will happenn later and make it wait. All we need is stop 
      // run() from calling any other methods on canvas from surfaceHolder 


      Toast.makeText(getContext(), "Surface Destroyed", Toast.LENGTH_LONG).show(); 
     } 

     public void setSurfaceHolder(SurfaceHolder surfaceHolder2) { 
      // TODO Auto-generated method stub 
      surfaceHolder=surfaceHolder2; 
     } 

    } 

另请参阅在重新启动()方法中编辑的注释。当重新启动()方法退出时,以下是调用堆栈。请让我知道是否需要更多信息。

DalvikVM[localhost:8754]  
    Thread [<1> main] (Suspended) 
     <VM does not provide monitor information> 
     MyFirstAppActivity.startRecording(View) line: 271 
     Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
     Method.invoke(Object, Object...) line: 521 
     View$1.onClick(View) line: 2077 
     Button(View).performClick() line: 2461 
     View$PerformClick.run() line: 8888 
     ViewRoot(Handler).handleCallback(Message) line: 587 
     ViewRoot(Handler).dispatchMessage(Message) line: 92 
     Looper.loop() line: 123 
     ActivityThread.main(String[]) line: 4627  
     Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method] 
     Method.invoke(Object, Object...) line: 521 
     ZygoteInit$MethodAndArgsCaller.run() line: 858 
     ZygoteInit.main(String[]) line: 616 
     NativeStart.main(String[]) line: not available [native method] 
    Thread [<7> Binder Thread #2] (Running) 
    Thread [<6> Binder Thread #1] (Running) 
    Thread [<8> Binder Thread #3] (Running) 
    Thread [<9> Thread-9] (Running) 
+0

发布从Eclipse的调试窗口发布的异常调用堆栈 – nandeesh 2012-08-17 01:24:04

+0

。它有帮助还是我应该提供更多信息? – spirytus 2012-08-17 01:40:35

回答

1

方法reStart()和resume()必须在调用notify()之前获取对象定时器的监视器。 (类似于如何timer.wait()在代码正在做以上()。)

public synchronized void reStart() 
{ 
// here the method is executed twice as I described in step 7 
// after notify() it actually jumps back to stopTimer=false again and then exits the function. Then outside of this object I catch RuntimeException 
    stopTimer=false; 
    synchronized(timer) { 
     timer.notify(); 
    } 
} 

public synchronized void resume() 
{ 
    pauseTimer=false; 
    synchronized(timer) { 
     timer.notify(); 
    } 
} 

否则在JRE所以导致抛出:IllegalMonitorStateException。 stacktrace发布了类似的问题。

+0

是的,我实际上已经修复它,但这是正确的答案。我跟踪它通知()抛出异常“对象没有锁定..”,所以这是正确的答案 – spirytus 2012-08-17 22:25:05