postDelayed() für Strobo-Effekt

  • 11 Antworten
  • Letztes Antwortdatum
C

_Coco_

Neues Mitglied
1
Hallo Leute!
Ich steh bisschen auf dem Schlauch. Ich möchte eine Strobo-Funktion in die App bauen. Also Button-Klick bewirkt dass das LED-Licht am Smarthphone ganz schnell blinkt.

Mit
Code:
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
gucke ich, ob das Smartphone überhaupt ein Flashligt besitzt. Wenn ja, dann wird eine runnable mit postDelayed(50) ausgeführt. So nun möchte ich aber dass das LIcht richtig schön schnell flackert. Aufm iPhone klappt das ganz gut, nur aufm Android System flackert es seeeeeehr langsam. Wo liegt mein Denkfehler? Der Wert in postDelayed() gibt doch die Millisekunden oder? 50 ms zwischen den Aktionen sollte doch ein schön schnelles flackern erzeugen?!
Nun ist es auch so, dass der Intervall auch nicht auf allen Smartphones gleich ist. Bei dem einen ist er schneller, beim anderen sogar nur im Sekundentakt (1000).
 
Zeig erstmal, wie genau du postDelayed aufrufst, wenn Du dabei ein Problem hast.

Eins gleich vorne weg: postDelayed() blockiert nicht :D
 
Code:
      Runnable mStatusChecker = new Runnable() 
      {
            @Override 
            public void run()
            {
              updateStatus(); 
              mHandler.postDelayed(mStatusChecker, 50);
            }

            private void updateStatus() 
            {
                if(flashOn == false) //Schalte Kameralicht an
                {
                    cam = Camera.open(); 
                    Parameters p = cam.getParameters();
                    p.setFlashMode(Parameters.FLASH_MODE_TORCH);
                    cam.setParameters(p);
                    cam.startPreview();
                    flashOn = true;
                }
                else if(flashOn == true) //Schalte Kameralicht aus
                {
                    cam.stopPreview();
                    cam.release();
                    flashOn = false;
                }
            }
          };


Der ursprüngliche Beitrag von 11:39 Uhr wurde um 11:40 Uhr ergänzt:

Wie meinst du das mit dem "blockiert nicht"?
 
Nicht blockieren heisst, dass postDelayed(x,t) zwar t msec wartet, bevor das Runnable x ausgeführt wird, die Funktion selbst aber *sofort* endet.

Mit anderen Worten, in:
Code:
postDelayed(x,50);
postDelayed(y,50);
werden x und y beide in etwa gleichzeitig (nach 50msec) ausgeführt.

Was bei deinem Code falsch ist, sehe ich auch nicht auf den ersten Blick. Allerdings ist deine Schleife zwar nicht falsch, wohl aber etwas komisch implementiert. Threads, die sich selbst neu schedulen???

Vielleicht solltest du die Kamera erstmal einfach an lassen. (Camera.open()/release() ausserhalb deiner Schleife behandeln)
 
Zuletzt bearbeitet:
Super! Ich werde das morgen gleich mal anwenden. Hab jetzt schon Feierabend. Werde dir berichten.
 
Sonst gibts hier noch ein Beispiel. Habs nicht getestet, aber vielleicht hilfts ja als Vorlage.
 
Ok, ich habe jetzt folgendes getan.

Code:
    /** Strobo button was tapped */
    public void stroboLight(View view) 
    {
        //Check if is Flashlight
        Boolean hasLight = getCameraLight(this);
         
        if(hasLight == true)
        {
            if(buttonState == false)
            {
                startRepeatingTask();
                Strobobutton.setText("Strobo AUS");
                buttonState = true;
            }
            else if(buttonState == true)
            {
                stopRepeatingTask();
                Strobobutton.setText("Strobo AN");
                buttonState = false;
            }
        }
    }
    /** Start STROBO */
    void startRepeatingTask()
    {
        if(flashOn == false)
        {
            cam = Camera.open(); 
            Parameters p = cam.getParameters();
            p.setFlashMode(Parameters.FLASH_MODE_TORCH);
            cam.setParameters(p);
            mStatusChecker.run();
        }
    }
    
    /** Stop STROBO */
    void stopRepeatingTask() 
    {
        if(flashOn == true)
        {
            cam.stopPreview();
            flashOn = false;
        }
        cam.release();
        mHandler.removeCallbacks(mStatusChecker);
    }

Und hier die Runnable:
Code:
      Runnable mStatusChecker = new Runnable() 
      {
          @Override 
          public void run()
          {
              updateStatus(); 
              mHandler.postDelayed(mStatusChecker, 100);
          }
          
          private void updateStatus() 
          {
              if(flashOn == false) //Schalte Kameralicht an
              {
                  cam.startPreview();
                  flashOn = true;
              }
              else if(flashOn == true) //Schalte Kameralicht aus
              {
                  cam.stopPreview();
                  flashOn = false;
              }
          }
      };

Klappt alles wunderbar. Die Intervalle stimmen auch, ALLERDINGS schaltet sich das Kameralicht an, nach 100 ms wieder aus und dann SOFORT wieder an (ohne 100ms zu warten). Dasselbe auch wenn ich 1000ms oder 2000ms nehme. Hat das mit dem "blockieren" zu tun DieGoldeneMitte? Wenn ja, dann hab ich das Prinzip noch nicht verstanden.:unsure:
 
Wenn man mit mehreren Threads arbeitet, dann sollte man die Kommunikationsvariablen als "volatile" deklarieren, damit die Runtime nicht anfängt, Werte zu cachen.

Aber noch ein paar Anmerkungen zu deinem Code:

1.) Man ruft niemals run() in einem Runnable auf. So wird der Code im selben Thread ausgeführt. Du willst sicher auch in startRepeatingTask() ein postDelayed() machen.

2.) Deine ifs sind drollig:

Code:
if( x==true ) { } else if( x==false ) { }
(Insiderwitz): Da fehlt noch ein "else if( x==fileNotFound )" :D

Du kannst einfach:
Code:
if( x ) { } else { }
machen.
 
Den Code wie folgt geändert, aber ohne Ergebnis. Bei 1000ms schaltet das Licht ein, nach 1000ms wieder aus und sofort wieder an.

Deklaration von flashOn:
Code:
private static volatile boolean flashOn = false;

Strobo starten:
Code:
    /** Start STROBO */
    void startRepeatingTask()
    {
        if(!flashOn)
        {
            cam = Camera.open(); 
            Parameters p = cam.getParameters();
            p.setFlashMode(Parameters.FLASH_MODE_TORCH);
            cam.setParameters(p);
            mHandler.postDelayed(mStatusChecker, 1000);
        }
    }

und die runnable
Code:
      Runnable mStatusChecker = new Runnable() 
      {
          @Override 
          public void run()
          {
              updateStatus(); 
              mHandler.postDelayed(this, 1000);
          }
          
          private void updateStatus() 
          {
              if(!flashOn) //Schalte Kameralicht an
              {
                  cam.startPreview();
                  flashOn = true;
              }
              else if(flashOn) //Schalte Kameralicht aus
              {
                  cam.stopPreview();
                  flashOn = false;
              }
          }
      };
 
Komisch, komisch. Passiert das auch, wenn du dort etwas anderes als Kameraansteuerung machst? Sonst fällt mir nur auf, dass du das selbe Runnable immer wieder neu postest. Es kann sein, dass der Scheduler so etwas nicht mag und beim schedulen sieht, dass das Runnable noch da ist.

Versuch mal sowas in dieser Art:
Code:
volatile boolean running = true;

public void run() {
  while( running ) {
    postOnUIThread( new Runnable() {
      public void run() { updateStatus(); }
    });
    // 500msec schlafen
  }
}
Erzeuge den Runner erst dann wenn du startest und setze im stop() running auf false.
 
Der Effekt tritt in der Tat nur bei der Kamerasteuerung auf. Wenn ich in dem Runnable einfach nur einen Toast ausgeben lasse und die Kamera-An/Kamera-Aus Steuerung weglasse, dann funktioniert das mit den Intervallen.

Habe dann jetzt mal das Kind so aufgezogen:

Code:
    /** Start STROBO */
    void startRepeatingTask()
    {
        if(!flashOn)
        {
            cam = Camera.open(); 
            Parameters p = cam.getParameters();
            p.setFlashMode(Parameters.FLASH_MODE_TORCH);
            cam.setParameters(p);
            thread.start();
        }
    }

    Thread thread = new Thread()
    {
        @Override
        public void run() 
        {
            try 
            {
                while(true) 
                {
                    updateStatus();
                    sleep(1000);
                }
            } 
            catch (InterruptedException e) 
            {
                e.printStackTrace();
            }
        }
    };
Der Rest ist gleich.
Habe den selben Effekt: Die Kamera geht nach 1 Sekunde aus und sofort wieder an, wartet eine Sekunde, geht wieder aus (nur als leichtes Flackern zu merken) und sofort wieder an.
 
Ich bin jetzt nicht so fit mit der Camera, aber vielleicht besteht das Problem darin, dass du den "Flash Mode Torch" nie änderst.
 
Zurück
Oben Unten