2016-08-22 3 views
14

Ich habe ein Android-Projekt von einem einzigen Layout mit einem ImageView zusammengesetzt.Android - Bitmap vom Timer Thread aktualisieren

public class MainActivity extends AppCompatActivity { 

    /* original and stretched sized bitmaps */ 
    private Bitmap bitmapOriginal; 
    private Bitmap bitmapStretched; 

    /* the only view */ 
    private ImageView iv; 

    .... 
} 

Dieses Image wird durch diese runnable Funktion aktualisiert

runnable = new Runnable() { 
     @Override 
     public void run() { 
      iv.setImageBitmap(bitmapStretched); 
     } 
    }; 

und die runnable wird durch eine temporized JNI-Funktion läuft, auf einem Hintergrund-Thread ausgeführt wird, dass es 60-mal pro Sekunde nennen.

public void jniTemporizedCallback(int buf[]) { 

    /* set data to original sized bitmap */ 
    bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight); 

    /* calculate the stretched one */ 
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false); 

    /* tell the main thread to update the image view */ 
    runOnUiThread(runnable); 
} 

Nachdem ein Frame gezeichnet wurde, stürzt die App mit der folgenden Meldung ab.

Ich denke, das liegt daran, dass der Renderer das vorherige Bild des ImageView nicht vollständig gerendert hat und wütend wird.

Wenn ich entfernen runOnUiThread(runnable); das Problem verschwinden (natürlich)

Wie kann dies vermeiden? Wie kann ich meine Anwendung mit dem OpenGL-Renderer synchronisieren?

Ich habe auch versucht Image und ziehen das Bitmap auf Leinwand in die OnDraw Funktion, aber ich habe das gleiche Ergebnis

+0

Möge dies Ihnen helfen. http://stackoverflow.com/questions/5869114/loading-new-image-each-second –

+0

versuchen Sie dies, um zu überprüfen, ob der vorherige Thread fertig ist oder nicht. Überprüfen Sie diese http://stackoverflow.com/questions/8799373/waiting-for-a-runnable-to-complete-before-running-another-runable – Abhishek

Antwort

4

Problem war völlig unabhängig von der Bitmap selbst ....

Es ist das Echtzeit-Taktsignal war, das mit Android RenderThread verwirrte.

Eine weitere Erläuterung hier:

Android and JNI real time clock

6

Ich glaube, Sie versuchen, erstellen bitmapOriginal ouside den Faden zu verlängern. Wenn der Compiler versucht, nach 60 Sekunden erneut aufzurufen, werden daher die gleichen Objekte abgerufen und die Task konnte nicht identifiziert werden. Ich würde vorschlagen, besser als unten.

1

Ich denke, Sie haben Recht mit Grund, weil Sie nicht sicher sein können, dass Android Bilder in 60 FPS rendern. Und ja, ich denke, Sie müssen nur Bitmap Native Callback mit Android Render synchronisieren. So lass uns anfangen.

Ich bevorzuge die Verwendung von Sperren aus Parallelstapel Java. Weil Sie sehen, wenn Sie ein Objekt sperren und wenn Sie entsperren. Wenn Sie ein Bitmap-Objekt flüchtig verwenden (z. B. dort auch Referenzeinschränkungen beachten), müssen Sie das Sperren dieses Objekts an bestimmten Stellen überprüfen, an denen Sie Bitmap verwenden.

Auch ich denke, Sie sollten Sperren von THIS EXAMPLE verwenden (um Lock-Objekt von einem anderen Thread zu entsperren). Also, hier ist ein Beispiel. Das folgende Beispiel funktioniert ordnungsgemäß. Nur nicht vergessen, über Kontext zu löschen und Stoppen Aufgabe:

public class MainActivity extends AppCompatActivity { 

    /* Initialize lock (avoid lazy init, with your methods) */ 
    private ReentrantLock lock = new ReentrantLock(); 

    ............ 

    private runnableDrawImage = new Runnable() { 
     @Override 
     public void run() { 
      iv.setImageBitmap(bitmapStretched); 
      lock.unlock(); 
     } 
    }; 

    .......... 


    public void jniTemporizedCallback(int buf[]) { 
     /* synchronize by locking state*/ 
     lock.lock(); 

     bitmapOriginal = Bitmap.createBitmap(///) 
     bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight); 

     bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false); 
     MainActivity.this.runOnUiThread(runnableDrawImage); 
    } 

} 
5

Der richtige Weg, Ihre Zeichnung Logik mit dem Gerät des Framerate zu synchronisieren ist ein Surface anstelle ein Image zu verwenden. Anstatt Frames mit Ihrem eigenen Timer zur View zu schieben, sollten Sie einen Rendering-Thread erstellen, der versucht, Frames so schnell wie möglich zu rendern. Wenn Sie surfaceHolder.lockCanvas() anrufen, wird das Android-System automatisch blockieren, bis es Zeit ist, den Rahmen zu rendern. Wenn Sie die Zeichenfläche mit unlockCanvasAndPost() entsperren, zeichnet das System den Puffer auf dem Bildschirm.

Weitere Informationen finden Sie unter https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview. Hoffe das hilft!

4

hier Zwecke zum Rendern Verwendung solcher Verfahren zur Verfügung stellen? Was Sie tun möchten, gibt es große Animationsfunktion in Android-Engine, kann diese Aufgabe mit dieser Animation durchgeführt werden.

Ein mehr, wenn Sie Codes wie Ihre verwenden, wird die Batterie des Telefons auf Null sehr schnell laufen, da dies cpu/gpu auf max laden wird.

in sowieso - versuchen Blöcke zu platzieren von Aufgabe ausgeführt wird, legen Bool taskRun = true auf Start- und prüfen if (!taskRun){ taskRun = true; //here start your task..} und auf UI-Thread nach ui Aktualisierung können Sie wechseln zu taskRun = false; Damit können sie einige Frames überspringen können, sollte aber nicht zum Absturz bringen.

3

Das Problem ist, dass die Handler des Hauptfadens einen Verweis auf Ihre Runnable hält. Wenn Sie Ihre Runnable zum zweiten Mal ausführen möchten, ist die alte Runnable bereits in der Message Queue, daher Task is already in the queue Nachricht. Wenn Sie ein Runnable jedes Mal erstellen, wenn Sie die Runnable wie im folgenden Code ausführen möchten, denke ich, dass das Problem gelöst wird.

public void jniTemporizedCallback(int buf[]) { 
    /* set data to original sized bitmap */ 
    bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth,   origHeight); 

    /* calculate the stretched one */ 
    bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false); 

    /* tell the main thread to update the image view */ 
    runOnUiThread(new Runnable() { 
     @Override 
     public void run() { 
      iv.setImageBitmap(bitmapStretched); 
     } 
    });  
}