1

Android 2.2以降用のシンプルなビデオ録画アプリを開発していますが、タイマースレッドを期待どおりに機能させるのに問題があります。コードは以下のとおりです。手順は次のとおりです。

  1. ユーザーが録画開始ボタンを押すと、録画が開始され、fightTimer.start()メソッドが呼び出されます。
  2. スレッドの実行を開始するtimer.start()メソッドを呼び出します。timerはfightTimer内のスレッドオブジェクトです
  3. ユーザーが停止ボタンをクリックすると、fightTimerメソッドstop()が呼び出されます。そこで、フラグstopTimer = trueを設定して、スレッドメソッドの実行を停止し、timer.wait()メソッドを呼び出して、スレッドが待機するようにします。
  4. ユーザーが[記録の開始]ボタンをクリックすると、fightTimer.start()メソッドが再度呼び出されます。タイマーはすでに開始されているため、スレッドtimer.start()を呼び出しますが、例外をスローします。
  5. 例外をキャッチし、fightTimer.restart()メソッドを呼び出します。このメソッドは、ステップ3でフラグstopTimerをtrueに設定したので、stopTimer=trueになります。タイマースレッドはまだrun()メソッド内で待機しています
  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;
        }

    }

restart()メソッドの編集されたコメントも参照してください。以下は、restart()メソッドが終了したときのコールスタックです。さらに情報が必要な場合はお知らせください。

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) 
4

1 に答える 1

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が発生します。また、スタックトレースは同様の問題に向けてポイントを投稿しました。

于 2012-08-17T17:00:08.907 に答える