Canvas Fehler in eigenem Thread

  • Antworten:20
  • OffenNicht stickyNicht beantwortet
  • Forum-Beiträge: 89

19.12.2014, 22:57:51 via Website

Hallo zu später Stunde liebe Android Entwickler,

ich muss zugeben, in den letzten Wochen hat mir die Android Welt einige graue Haare beschert. Zur Zeit versuche ich einen kleinen "Computergrafikrenderer" zu programmieren. Die Rendertechnik und so funktioniert soweit alles nur ich krieg es seit einer Woche nicht gebacken, dass Ergebnis ohne ständige Abstürzte auf eine SurfaceView abzubilden. Da ich nicht genau weiß, wo der Fehler liegt, muss ich eventuell recht viel Code zeigen und weiter ausholen.

Also erstmal zum aktuellen Fehler (wird wahrscheinlich nicht der einzige bleiben):

Direkt beim Starten meiner App kommt bei diesem Codeabschnitt:

    @Override
public void run(){
    while(running){
        Canvas canvas = this.holder.lockCanvas();
        if(canvas == null){
            this.holder.unlockCanvasAndPost(canvas);
            continue;
        }

        this.render.canvas = canvas;
        this.render.RenderOneStep();
        this.holder.unlockCanvasAndPost(this.render.DisplayResult());
        this.view.postInvalidate();
    }
}

diese Fehlermeldung:

java.lang.IllegalArgumentException: canvas object must be the same instance that was previously returned by lockCanvas

und zwar bei der Zeile this.holder.unlockCanvasAndPost(canvas). Das passiert in den letzten Tagen leider sehr gehäuft, dass mir Fehlermeldungen hingeworfen werden, die für mich rein logisch her überhaupt keinen Sinn machen. Das Canvas Objekt, dass ich vom Holder bekomme, wird doch von mir gar nicht verändert? Oder reicht dafür schon eine simple canvas == null Abfrage?

Dann hole ich mal weiter aus und versuche den Rest von meinem abstrusen Code mal zu erklären:

In meiner MainActivity hole ich mir meine DrawingView, die von SurfaceView erbt (in der mach ich aber vorerst mal nichts spannendes):

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    DrawingView view = (DrawingView)findViewById(R.id.surface);
    this.holder = view.getHolder();
    this.dView = view;

    Thread thread = new Thread(this);
    thread.start();
}

Wie ihr bereits sehen könnt, erstelle ich da einen neuen Thread. Meine MainActivity implementiert nämlich die Runnable-Schnittstelle (warum? Hab ich mal irgendwo aufgeschnappt). Der neu erstellte Thread wird auch gleich gestartet, die run()-Methode sieht dann so aus:

    @Override
public void run(){
    while(locker) {
        if (this.holder.getSurface().isValid()) {

            Canvas cav = this.holder.lockCanvas();
            this.rend = new Renderer(new Scene(),cav, new Vector3D(0.0f,0.0f,10.0f),4);
            this.holder.unlockCanvasAndPost(cav);

            MyThread myThread = new MyThread(this.dView, this.holder, this.rend);
            myThread.start();

            this.runningThread = myThread;

            locker = false;
        }
    }
}

In der Schleife warte ich erstmal solange, bis die Surface des Holders gültig ist. Danach hole ich mir das Canvas um der Renderer Klasse eine Zeichenoberfläche zu geben. Danach poste ich sie wieder zurück zum Handler. Anschließend erstelle ich einen neuen MyThread (meine eigene Klasse, die von Thread erbt). Dieser übergebe ich meine zuvor erstellte DrawingView, den SurfaceHolder und das Renderer Objekt. Danach starte ich den Thread und beende die Ausführung der Schleife (mit locker = false).

So nun weiter zu meiner MyThread-Klasse, diese zeichnet dann schlussendlich auf ein Canvas und updated es auch auch regelmäßig auf der DrawingView (das ist der ganz oberste Codeabschnitt).

Entschuldigt bitte, dass ich hier meinen ganzen Code poste, aber ich weiß inzwischen nicht mehr, wo der Fehler liegt. Ich hab auch schon in diversen Foren nach diesen Problemen gefragt, ohne dass mir einer eine endgültige Lösung für das Problem liefern konnte. Wäre also super, wenn ihr da ein paar Antworten parat hättet :)

Vielen Dank schon einmal fürs Durchlesen und schönen Abend noch

— geändert am 19.12.2014, 22:59:47

Antworten
  • Forum-Beiträge: 434

20.12.2014, 00:01:05 via Website

Hallo & puhh :D

leider sehe ich ehrlich gesagt kaum einen Ansatz dir großartig zu helfen. :-/ Bzw. müsste ich das Programm vielleicht mal als "Ganzes" sehen, so richtig schlau werde ich jedenfalls aus deinen Fehlerbeschreibungen und dem Code bzw. deiner Beschreibung nicht... (Liegt aber nicht zwangsläufig daran, dass dein Programm oder deine Beschreibung schlecht wären, sondern an mir bzw. dem Thema als solchem...)
Kannst du grob erklären welchen Ablauf dein Programm insgesamt hat? Eine Art Ablaufdiagramm wäre hilfreich...

Hier eine GameLoop aus einem Programm von mir - ich weiß nicht in wie weit dir das weiterhilft, aber vielleicht erkennst du schon Unterschiede, ich habe mal bewusst nichts rausgelöscht, die Loop kommt, wie gesagt, so bei mir zum Einsatz und funktioniert... "FAC" ist eine Factory-Klasse und stellt statische Konstanten bereit, also eher irrelevant. Und MainInstance stellt, wie der Name schon sagt, einen Bezug zur Main her (Singleton) - ob das alles best Practise ist, ist umstritten, aber es funktioniert...

/*-------------------
 |    GAME LOOP     |
 ------------------*/
@Override
public void run() {
    long TPS = 1000 / FAC.FPS;
    long startTime, sleepTime = 0;
    long syncTime = System.currentTimeMillis();
    // erstes Senden verzoegern
    final long fixedStartTime = System.currentTimeMillis();

    /** MAIN GAME LOOP */
    while (keepPlaying) {

        Canvas canvas = null;
        startTime = System.currentTimeMillis();
        try {
            canvas = gameView.getHolder().lockCanvas();
            synchronized (gameView.getHolder()) {
                gameView.onDraw(canvas);
            }
        } finally {
            sleepTime = TPS - (System.currentTimeMillis() - startTime);
            if (canvas != null) {
                gameView.getHolder().unlockCanvasAndPost(canvas);
                // erst nach Startveroegerung Daten synchronisieren
                if (isMultiplayer) {
                    if ((System.currentTimeMillis() - syncTime) > FAC.UPDATE_RATE_MP
                            && fixedStartTime + FAC.START_DELAY_MP < System.currentTimeMillis()) {

                        myMainInst.broadcastData();

                        // Zuruecksetzen auf "nicht geschossen" (sonst Dauerfeuer)
                        gameView.getSprite(FAC.P1).setSpriteShot(FAC.FALSE);
                        gameView.getSprite(FAC.P1).setIsPlayerHitten(FAC.FALSE);
                        gameView.getSprite(FAC.P2).setSpriteShot(FAC.FALSE);
                        gameView.getSprite(FAC.P2).setIsPlayerHitten(FAC.FALSE);
                        syncTime = System.currentTimeMillis(); // aktualisieren
                    }
                }
            }
        }
        /* GAME OVER Bedingungen */
        try {
            /* Singleplayer */
            if (!isMultiplayer) closeSinglePlayerSessionClean();

            /* Multiplayer */
            else if (isMultiplayer) {

                // mit loopController Einfluss nehmen
                if (loopController == FAC.DISABLE_LOOP) keepPlaying = false;
                // Ende MP-Session
                if (!keepPlaying && myMainInst.isOpponentConnected()) closeMultiplayerSessionClean();

                // Ende MP-Runde
                if (myMainInst.getChampionsIdentifyer() != FAC.NOBODY) {

                    // Punktvergabe (Nur 1x - Bei neuer Runde wieder auf false setzen!)
                    if (isArchievmentLocked == false) {
                        isArchievmentLocked = true;

                        /*Punkt fuer Gewinner und Archievment*/
                        if (myMainInst.getChampionsIdentifyer() == gameView.getCtrlHandle()) {
                            // offline Punktevergabe fuer Anzeige
                            gameView.addOneToPlayerVictoryAmount(1);
                            myMainInst.increaseGPG_Archievments();
                            if (FAC.DEBUG) Log.d("GameLoop", "+1 to WINNER");
                        }
                        /*Punkt fuer Verlierer*/
                        else {
                            gameView.addOneToOpponentVictoryAmount(1);
                            if (FAC.DEBUG) Log.d("GameLoop", "+1 to LOSER");
                        }
                    }
                    boolean log = false; 
                    if (FAC.DEBUG && log) writeLog();

                } // Ende Rundenbedingung
                gameView.newRoundLogic();
            } // Ende der Multiplayer 
            sleep(injectSleepTime);
            injectSleepTime = 0;

            if (sleepTime > 0) sleep(sleepTime);
            else sleep(realSleepTime);
        } catch (Exception e) {
        }
    } // Ende while (keepPlaying)
    if (FAC.DEBUG)Log.d("GameLoop", "GameLoop beendet!");
}

Andere Idee:
Bist du dir sicher, dass deine Threads synchron (ab)laufen? Vgl diese Frage auf SO...
Wenn gar nichts mehr hilft, gibts immer noch den Debugger - oder es hat natürlich noch jemand anderes eine Idee...

Open Source

Antworten
  • Forum-Beiträge: 89

20.12.2014, 10:35:33 via Website

Kannst du grob erklären welchen Ablauf dein Programm insgesamt hat? Eine Art Ablaufdiagramm wäre hilfreich...

Ablaufdiagramm wäre meiner Meinung nach etwas übertrieben, da das Programm zum größten Teil aus dem Renderer und die benötigten Objekte dafür besteht und diese nichts mit dem Zeichnen zu tun haben.

In meiner MainActivity hole ich mir meine DrawingView, die von SurfaceView erbt, mittels findViewById. In meiner run() Methode warte ich solange bis holder.getSurface().isValid() true ist. Dann hole ich das Canvas, übergebe es dem Renderer und unlocke das Canvas wieder. Anschließend starte ich meinen eigenen Thread, diesem übergebe ich die DrawingView, den SurfaceHolder und den Renderer. Anschließend wird in der run() Methode meines Threads wieder das Canvas gelockt um zu schaun, ob es null ist. Wenn es null ist poste ich es mit unlock wieder zurück und fahre fort in der Schleife. Hier kommt es zu diesem komischen Fehler.

Bist du dir sicher, dass deine Threads synchron (ab)laufen?

Eigentlich läuft ja nur ein Thread, der Thread in der MainActivity wird ja sobald beendet, bis die getSurface valid ist.

Wenn gar nichts mehr hilft, gibts immer noch den Debugger

Beim Debuggen läuft alles wie vorhergesehen, die Fehlermeldung kommt trotzdem. Wenn ich nicht prüfe, ob das Canvas null ist sondern einfach so alles durchgehen lasse, kommt eben die Fehlermeldung, dass das Canvas null ist und ich deswegen nicht drauf zeichnen kann.

— geändert am 20.12.2014, 16:37:04

Antworten
  • Forum-Beiträge: 434

20.12.2014, 16:21:50 via Website

Okay, habs geladen. Ich schaus mir gleich mal an. Den Link kannst du wieder rausnehmen (wird wahrscheinlich in Kürze sowieso von einem Mod entfernt, da hier nicht erlaubt :) )

Open Source

Antworten
  • Forum-Beiträge: 434

20.12.2014, 17:44:44 via Website

Funktioniert wieder... (jedenfalls so halb)

MyThread:

package com.example.standardbenutzer.adelpath;

import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

/**
 * Created by Standardbenutzer on 14.12.2014.
 */
public class MyThread extends Thread {

    private static final String TAG = "MyThread";
    public SurfaceHolder holder;
    public Renderer      renderer;
    public boolean running = true;
    public DrawingView view;
    public Renderer    render;


    public MyThread(DrawingView view, SurfaceHolder holder, Renderer render) {
        this.holder = holder;
        this.view = view;
        this.render = render;
    }


    @Override
    public void run() {
        while (running) {
            try {
                if (holder != null) {

                    Canvas canvas = this.holder.lockCanvas();

                    if (canvas == null) return;


                    this.render.canvas = canvas;
                    synchronized (view.getHolder()) {
                        this.render.RenderOneStep();
                    }
                    this.holder.unlockCanvasAndPost(this.render.DisplayResult());
                    this.view.postInvalidate();
                }
            } catch (Exception e) {
                Log.d(TAG, "run " + e);
            }
        }
    }


    public void Stop() {
        this.running = false;
    }
}

MainActivity:

package com.example.standardbenutzer.adelpath;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;


public class MainActivity extends ActionBarActivity implements Runnable {

    public DrawingView   surface;
    public SurfaceHolder holder;
    public boolean locker = true;

    public  MyThread    runningThread;
    public  DrawingView dView;
    public  Renderer    rend;
    private MyThread    myThread;
    private MyThread    mThread;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DrawingView view = (DrawingView) findViewById(R.id.surface);
        this.holder = view.getHolder();
        this.dView = view;

        Thread thread = new Thread(this);
        thread.start();
    }


    @Override
    public void run() {
        while (locker) {
            if (this.holder.getSurface().isValid()) {

                Canvas cav = this.holder.lockCanvas();
                this.rend = new Renderer(new Scene(), cav, new Vector3D(0.0f, 0.0f, 10.0f), 4);
                this.holder.unlockCanvasAndPost(cav);

                if (myThread == null) {
                    myThread = new MyThread(this.dView, this.holder, this.rend);
                    myThread.start();

                    this.runningThread = myThread;
                }
                locker = false;
            }
        }
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }


    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_exit) {
            this.runningThread.Stop();
            return true;
        } else if (id == R.id.action_restart) {
            this.runningThread.Stop();
            Canvas canv = this.holder.lockCanvas();
            this.rend = new Renderer(new Scene(), canv, new Vector3D(0.0f, 0.0f, 10.0f), 4);
            if (myThread == null) {
                mThread = new MyThread(this.dView, this.holder, this.rend);
                this.holder.unlockCanvasAndPost(canv);
                mThread.start();
                this.runningThread = mThread;
            }
            return true;
        } else if (id == R.id.action_saveImage) {
            saveImage();
        }

        return super.onOptionsItemSelected(item);
    }


    protected void onStop() {
        super.onStop();
        this.runningThread.Stop();
    }


    protected void onResume() {
        super.onResume();
        MyThread thr = new MyThread(this.dView, this.holder, this.rend);
        thr.start();
        this.runningThread = thr;
    }


    public void saveImage() {
        Bitmap bitmap = this.rend.GetBitmap();

        try {
            Date d = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("hhmm ddMMyy");
            sdf.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam"));
            String dateString = sdf.format(d);

            File file = new File(Environment.getExternalStorageDirectory(), "AdelPath_" + dateString + ".png");

            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
            //fos.close();
        } catch (Exception e) {
            Log.e("Error --->", e.toString());
        }
    }
}

ist aber immer noch einiges im Argen... Ich schaue es mir später nochmal an, bin gerade etwas in Eile.

Open Source

superSharp

Antworten
  • Forum-Beiträge: 89

20.12.2014, 17:58:15 via Website

Hi pepperonas und vielen vielen Dank für deine Mühe! :)

Das Problem ist jetzt aber, dass gleich aus der Funktion run() in MyThread rausgesprungen wird, sobald Canvas einmal null war. Warum auch immer canvas null ist, ich verstehs nicht (thinking)

— geändert am 20.12.2014, 17:58:42

Antworten
  • Forum-Beiträge: 434

20.12.2014, 19:12:26 via Website

Hey, hehe bis jetzt konnte ich ja nicht viel helfen...
Hmm, warum der canvas null ist, sehe ich gerade auch nicht. Bist du sicher, dass der Canvas null ist, oder vielleicht die lock-Methode "nur" null zurückgibt?
siehe hier

Ein paar gut gemeinte Ratschläge:
1. Am Programm sehe ich, dass du offensichtlich ein paar Sachen zusammen kopiert hast (zB daran dass eine lokale Funktionsvariable als Member geschrieben ist, was normalerweise nur für globale (Klassen)member gemacht wird -> konkret die "mThread" in der onOptionsItem...).
2. meiner Meinung nach tust du dir mit der "this"-Schreibweise in Java keinen Gefallen (jedenfalls nicht an den Stellen, an denen du sie verwendest).
3. Ggf. bekommst du irgendwann Probleme mit dem Scope, wenn du weiterhin alle Member als public deklarierst - spätestens wenn das Projekt mal größer wird.
4. Das Ablaufdiagramm ist für dich selbst ebenfalls eine große Hilfe (ebenso Kommentierungen im Code)... So stringent ist dein Programm schon jetzt nicht, als dass man alles im Hand um Drehen runterprogrammiert. (wahrscheinlich kämpft mit dieser Materie sogar der ein oder andere Profi, wenn er keinen Überblick darüber hat, wann, welches Objekt "vorhanden" ist) ;) Hilfreich ist der Ablaufplan allemal.

Bitte nicht falsch verstehen, ist nicht böse gemeint und ich weiß auch wie schwierig die Android-Programmierung ist (bzw. sein kann) und genau deshalb weise ich lieber einmal zu viel auf ein paar "Tricks" hin, als einmal zu wenig. (Ist nicht lange her, da habe ich auch diese Fehler gemacht :P )..

Was den Canvas angeht, findest du hier sehr gute Infos.

Ich schaue mir später nochmal den Code an..

Beste Grüße ;)

Open Source

Antworten
  • Forum-Beiträge: 89

20.12.2014, 21:07:21 via Website

Vielen dank für deine Tipps :)

zB daran dass eine lokale Funktionsvariable als Member geschrieben ist, was normalerweise nur für globale (Klassen)member gemacht wird -> konkret die "mThread" in der onOptionsItem...

Naja wenn ich die Variablen nun mal brauche? Den runningThread brauche ich, damit ich falls ich das Rendern neu starten will, den alten deaktivieren kann (sonst malen zwei Threads gleichzeitig auf das Canvas). Den Renderer brauche ich, damit ich das Bild speichern kann.

meiner Meinung nach tust du dir mit der "this"-Schreibweise in Java keinen Gefallen (jedenfalls nicht an den Stellen, an denen du sie verwendest).

Danke, hab ich geändert.

  1. Ggf. bekommst du irgendwann Probleme mit dem Scope, wenn du weiterhin alle Member als public deklarierst - spätestens wenn das Projekt mal größer wird.

Danke, werd ich in Zukunft im Auge behalten.

  1. Das Ablaufdiagramm ist für dich selbst ebenfalls eine große Hilfe (ebenso Kommentierungen im Code)... So stringent ist dein Programm schon jetzt nicht, als dass man alles im Hand um Drehen runterprogrammiert. (wahrscheinlich kämpft mit dieser Materie sogar der ein oder andere Profi, wenn er keinen Überblick darüber hat, wann, welches Objekt "vorhanden" ist) ;) Hilfreich ist der Ablaufplan allemal.

Danke für den Tipp, aber ich will das ganze erstmal richtig zum Laufen bekommen ^^. Danach kümmere ich mich um PAP etc.

Hmm, warum der canvas null ist, sehe ich gerade auch nicht. Bist du sicher, dass der Canvas null ist, oder vielleicht die lock-Methode "nur" null zurückgibt?
siehe hier

Danke für den Link. Ich hab es jetzt mal so gemacht, wie der Typ, der geantwortet hat:

-Meine MainActivity implementiert nun statt runnable SurfaceHolder.Callback
-in meiner onCreate()-Funktion weise ich den Callback mittels: holder.addCallback(this); zu.
-in der surfaceCreated() Funktion rufe ich nun folgendes auf:

    @Override
public void surfaceCreated(SurfaceHolder holder) {
    createMyThread();
}

private void createMyThread(){
    Canvas cv = holder.lockCanvas(new Rect(0,0,300, getWindowManager().getDefaultDisplay().getHeight()));
    holder.unlockCanvasAndPost(cv);

    this.render = new Renderer(new Scene(), cv, new Vector3D(0.0f, 0.0f, 10.0f), 4);
    MyThread myThread = new MyThread(dView, holder, render);
    myThread.start();
    runningThread = myThread;
}

Komischerweise kommt jetzt überhaupt kein Fehler mehr im Logcat. Die App schmiert aber trotzdem direkt nach dem Start ab. Und zwar weil das Canvas weder Höhe noch Breite "besitzt" (ich frag mich nur was ich dann hier zurückbekomme, nix ganzes sondern so ein halbes Canvas?). Im Renderer versuche ich dann nämlich mein Array entsprechend anzupassen:

this.accumulator = new float[(canvas.getWidth() * canvas.getHeight()) * 3 + 1];

Puhh, dieses Android (angry)

— geändert am 20.12.2014, 21:07:46

Antworten
  • Forum-Beiträge: 434

20.12.2014, 22:01:24 via Website

Okay, kannst ja noch mal deinen aktuellen Code rüberschicken (halt nur die Klassen, die du verändert hast - nicht das ganze Projekt)...
... dann zeige ich dir gerne noch mal genauer, was ich mit dem Scope-Problem bzw. mit dem "m" vor den Membern meine... (mir gings nicht so sehr darum, "dass" die Variablen angelegt sind, sondern eher "wie") :)
Btw: Deine Grafik, die du da darstellst ist auf jeden Fall gelungen. Sieht gut aus ;)

— geändert am 20.12.2014, 22:05:59

Open Source

Antworten
  • Forum-Beiträge: 89

20.12.2014, 22:27:43 via Website

mir gings nicht so sehr darum, "dass" die Variablen angelegt sind, sondern eher "wie"

Also eher um die Namen? Generell möchte ich das Grundgerüst meiner App (also dass das Bild gerendert wird, Neustarten des Renderers, Bild speichern, dass die App nicht abschmiert wenn ich zu einer anderen App oder auf den Homescreen wechsel) erstmal zusammenhaben bevor ich mich um Schönheitssachen kümmere.

Hier der Downloadlink zur veränderten MainActivity:



Edit by Mod Pascal P. Filehosterlink entfent, bitte schickt euch Links per PN


Vielen Dank das du dir so viel Zeit nimmst :)

Btw: Deine Grafik, die du da darstellst ist auf jeden Fall gelungen. Sieht gut aus

Danke ^^ Das ist der Trick hinter dem Renderer: man muss den nur einmal schreiben und dann sieht eigentlich jedes Bild am Ende gut aus :)

— geändert am 20.12.2014, 22:29:18 durch Moderator

Antworten
  • Forum-Beiträge: 434

22.12.2014, 16:42:01 via Website

Hey, hab deine Nachricht eben erst gesehen :/ ich schau später nochmal drüber... (nur die Datei ist weg :-/ )

— geändert am 22.12.2014, 16:50:25

Open Source

Antworten
  • Forum-Beiträge: 89

22.12.2014, 17:43:37 via Website

Ich hab nun noch etwas mehr geändert, deswegen hab ich wieder das ganze Projekt hochgeladen. Jetzt scheint es zu funktionieren. Wenn das Event SurfaceDestroyed startet, stoppe ich den aktuellen Thread (das passiert, wenn der User ins Home Menü oder zu einer anderen App wechselt). Bei SurfaceCreated prüfe ich nun, ob der runningThread null ist (ist er nur beim ersten Starten der App), wenn ja erstelle ich einen neuen Thread mit neuem Renderer. Wenn running Thread ungleich null ist, erstelle ich auch einen neuen Thread (sonst hat die GUI bei mir rumgezickt und das Optionsmenü ging nicht mehr auf) aber mit dem alten Renderer (so geht der Stand nicht verloren).

Im Großen und Ganzen funktioniert das ganze jetzt allerdings gibt es immer noch zwei kleine "Probleme". Beim Wechseln vom Homescreen in die App passiert manchmal, dass sich die Anzeige etwas verschiebt und dann solche Streifen entstehen:

http://img5.fotos-hochladen.net/uploads/screenshot2014q2y4pkbtj7.png

Das legt sich nach dem nächsten Durchlauf zwar wieder und beim Speichern des Bildes sieht man die auch nicht (die entstehen ja nicht durchs Rendern). Aber trotzdem etwas komisch.

Dann zum zweiten "Problem":

Beim wechseln vom Homescreen in die App dauert es manchmal bis zu 7 Durchläufe bis ich wieder ein Bild sehe (mein Logcat sagt mir zwar, dass ein Bild berechnet wurde und ich update die SurfaceView ja auch direkt danach, sehen tu ich aber trotdzem nichts).

Den Link hab ich dir wieder per PM geschickt. Jeder der noch Ideen hat erhält den Link auch gerne per PM.

— geändert am 22.12.2014, 17:43:56

Antworten
  • Forum-Beiträge: 434

22.12.2014, 18:42:55 via Website

package com.example.standardbenutzer.adelpath;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Environment;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

public class MainActivity extends ActionBarActivity implements SurfaceHolder.Callback {

private SurfaceHolder mHolder;

private MyThread    mRunningThread;
private DrawingView mView;
private Renderer    mRender;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    mView = (DrawingView) findViewById(R.id.surface);

    mHolder = mView.getHolder();
    mHolder.addCallback(this);
}


@Override
public void surfaceCreated(SurfaceHolder holder) {
    Log.e("Info-->", "Surface Created.");
    if (mRunningThread == null) {
        Log.e("Info-->", "Running thread was null.");
        Canvas cv = holder.lockCanvas();
        mRender = new Renderer(new Scene(), cv, new Vector3D(0.0f, 0.0f, 10.0f), 4);
        mRunningThread = new MyThread(mView, holder, mRender);
        mRunningThread.start();
        holder.unlockCanvasAndPost(cv);
    } else { //falls vom Homescreen wieder zurück in die App gewechselt wird
        Log.e("Info-->", "Running thread was not null.");
        createNewThread(false); //keinen neuen Renderer erstellen, da nur zurück in die App gewechselt wurde
        Log.e("Info-->", "Running Thread Restart.");
    }
}


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

}


@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    //Passiert, wenn der Benutzer auf den Homescreen oder zu einer anderen App wechselt
    Log.e("Info-->", "Surface was destroyed.");
    mRunningThread.Stop();
    Log.e("Info-->", "Running Thread Stop.");
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == R.id.action_exit) {
        mRunningThread.Stop();
    } else if (id == R.id.action_restart) {
        createNewThread(true); //Neuen Renderer erstellen, da explizit ausgewählt
    } else if (id == R.id.action_saveImage) {
        saveImage();
    }

    return super.onOptionsItemSelected(item);
}


public void saveImage() {
    Bitmap bitmap = mRender.GetBitmap();

    try {
        Date d = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("hhmm ddMMyy");
        sdf.setTimeZone(TimeZone.getTimeZone("Europe/Amsterdam"));
        String dateString = sdf.format(d);

        File file = new File(Environment.getExternalStorageDirectory(), "AdelPath_" + dateString + ".png");

        FileOutputStream fos = new FileOutputStream(file);
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
        //fos.close();
    } catch (Exception e) {
        Log.e("Error --->", e.toString());
    }
}


public void createNewThread(boolean newRenderer) {
    mRunningThread.Stop();
    Canvas canv = mHolder.lockCanvas();

    mRunningThread = null;
    if (newRenderer) { //nur wenn explizit ausgewählt
        mRunningThread = new MyThread(mView, mHolder, new Renderer(new Scene(), canv, new Vector3D(0.0f, 0.0f, 10.0f), 4));
        mRunningThread.start();
    } else { //es wird mit dem alten Renderer weitergerendert, so geht der Stand nicht verlohren
        mRunningThread = new MyThread(mView, mHolder, mRender);
        mRunningThread.start();
    }

    mHolder.unlockCanvasAndPost(canv);
    mView.invalidate();
}

}


package com.example.standardbenutzer.adelpath;

import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;

/**
* Created by Standardbenutzer on 14.12.2014.
*/
public class MyThread extends Thread {

private SurfaceHolder mHolder;

private Renderer mRender;

private boolean mIsRunning = true;

private DrawingView mView;


public MyThread(DrawingView view, SurfaceHolder holder, Renderer render) {
    mHolder = holder;
    mView = view;
    mRender = render;
}


@Override
public void run() {
    while (mIsRunning) {
        try {
            if (mHolder != null) {
                Canvas canvas = mHolder.lockCanvas();
                if (canvas == null) {
                    Log.e("Info-->", "Canvas was null in MyThread.");
                    mHolder.unlockCanvasAndPost(canvas);
                    Log.e("Info-->", "Post canvas back to Holder.");
                }

                mRender.setmCanvas(canvas);
                synchronized (mView.getHolder()) {
                    mRender.RenderOneStep();
                }
                mHolder.unlockCanvasAndPost(mRender.DisplayResult());
                mView.invalidate();
                Log.e("Info-->", "Computed Sample.");
            }
            sleep(350);
        } catch (Exception e) {
            Log.d("Error-->", "run " + e);
        }
    }
}


public void Stop() {
    mIsRunning = false;
}


public void Restart() {
    mIsRunning = true;
}

}

package com.example.standardbenutzer.adelpath;

import java.util.ArrayList;

/**
* Created by Standardbenutzer on 07.12.2014.
*/
public class Scene {

private static ArrayList<BaseObject> sObjectList = new ArrayList<BaseObject>();


public Scene() {
    sObjectList.add(new Sphere(new CColor(255, 255, 255, 255), true, new Vector3D(0.0f, 0.0f, 0.0f), 1.5f, Material.Diffuse));
    sObjectList.add(new Sphere(new CColor(255, 125, 0, 0), false, new Vector3D(-2.0f, -2f, 0.0f), 1.0f, Material.Diffuse));
    sObjectList.add(new Sphere(new CColor(255, 255, 140, 0), false, new Vector3D(2.0f, 1.0f, 0.0f), 1.0f, Material.Diffuse));

    sObjectList.add(new Plane(new CColor(255, 200, 200, 200), false, new Vector3D(0.0f, -0.5f, 0.0f), 1.0f, Material.Diffuse));
    sObjectList.add(new Plane(new CColor(255, 200, 200, 200), false, new Vector3D(1.0f, 0f, 0.0f), -3.0f, Material.Diffuse));
    sObjectList.add(new Plane(new CColor(255, 200, 200, 200), false, new Vector3D(1.0f, -1.0f, 0.0f), 1.0f, Material.Diffuse));
}


public static ArrayList<BaseObject> getObjectList() {
    return sObjectList;
}

}


    package com.example.standardbenutzer.adelpath;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
 * Created by Standardbenutzer on 07.12.2014.
 */
class DrawingView extends SurfaceView implements SurfaceHolder.Callback {

    private SurfaceHolder mSurfaceHolder = this.getHolder();
    private Canvas canvas;
    private Vector3D Camera         = new Vector3D(0.0f, 0.0f, 10f);
    private int      MAX_DEPTH      = 2;
    private int      RAYS_PER_PIXEL = 1;
    private Scene    scene          = new Scene();


    public DrawingView(Context context) {
        super(context);
        this.setDrawingCacheEnabled(true);
        mSurfaceHolder.addCallback(this);
    }


    public DrawingView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setDrawingCacheEnabled(true);
        mSurfaceHolder.addCallback(this);
    }


    public DrawingView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.setDrawingCacheEnabled(true);
        mSurfaceHolder.addCallback(this);
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            if (mSurfaceHolder.getSurface().isValid()) {

            }
        }
        return false;
    }


    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int one, int two, int three) {

    }


    @Override
    public void surfaceCreated(SurfaceHolder holder) {

    }
}


--------


package com.example.standardbenutzer.adelpath;

/**
 * Created by Standardbenutzer on 08.12.2014.
 */
public class CColor {

    public float getA() {
        return a;
    }


    public float getR() {
        return r;
    }


    public float getG() {
        return g;
    }


    public float getB() {
        return b;
    }


    private float a;
    private float r;
    private float g;


    public void setB(float b) {
        this.b = b;
    }


    public void setA(float a) {
        this.a = a;
    }


    public void setR(float r) {
        this.r = r;
    }


    public void setG(float g) {
        this.g = g;
    }


    private float b;


    public CColor(float a, float r, float g, float b) {
        this.a = a;
        this.r = r;
        this.g = g;
        this.b = b;
    }


    public CColor(float r, float g, float b) {
        this.a = 255;
        this.r = r;
        this.g = g;
        this.b = b;
    }


    public CColor add(CColor color) {
        float red = this.r += color.r;
        float green = this.g += color.g;
        float blue = this.b += color.b;

        return new CColor(red, green, blue);
    }


    public CColor divide(float value) {
        float red = this.r / value;
        float green = this.g / value;
        float blue = this.b / value;

        return new CColor(red, green, blue);
    }
}

package com.example.standardbenutzer.adelpath;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;

/**
* Created by Standardbenutzer on 14.12.2014.
*/
public class Renderer {

private Scene    mScene;


public Canvas getmCanvas() {
    return mCanvas;
}


public void setmCanvas(Canvas mCanvas) {
    this.mCanvas = mCanvas;
}


private Canvas   mCanvas;
private Vector3D mCamera;
private int      RAY_DEPTH;

private float[] accumulator;
private int samples = 0;


public Renderer(Scene scene, Canvas canvas, Vector3D Camera, int RAY_DEPTH) {
    this.mScene = scene;
    this.mCanvas = canvas;
    this.mCamera = Camera;
    this.RAY_DEPTH = RAY_DEPTH;

    this.accumulator = new float[(canvas.getWidth() * canvas.getHeight()) * 3 + 1];
}


public void RestartProgressiveRendering() {
    this.accumulator = new float[(mCanvas.getWidth() * mCanvas.getHeight()) * 3 + 1];
    this.samples = 0;
}


public void RenderOneStep() {
    int width = this.mCanvas.getWidth();
    int height = this.mCanvas.getHeight();
    int i = 0;
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            CColor cPixel = ComputeSample(x, y, width, height);
            accumulator[i * 3 + 0] += cPixel.getR();
            accumulator[i * 3 + 1] += cPixel.getG();
            accumulator[i * 3 + 2] += cPixel.getB();
            i++;
        }
    }

    samples++;
}


public Canvas DisplayResult() {
    int width = this.mCanvas.getWidth();
    int height = this.mCanvas.getHeight();

    Paint p = new Paint();
    int i = 0;
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            int r = (int) (accumulator[i * 3 + 0] / (float) samples); //die gespeicherte Farbe wird durch die Anzahl der Samples geteilt
            int g = (int) (accumulator[i * 3 + 1] / (float) samples);
            int b = (int) (accumulator[i * 3 + 2] / (float) samples);

            p.setColor(Color.rgb(r, g, b));
            this.mCanvas.drawPoint(x, y, p);
            i++;
        }
    }

    Paint myPaint = new Paint();
    myPaint.setColor(Color.WHITE);
    mCanvas.drawText("Samples: " + String.valueOf(this.samples), 10, 25, myPaint);

    return this.mCanvas;
}


//Liefert das Bitmap um das Renderergebnis speichern zu können
public Bitmap GetBitmap() {
    int width = this.mCanvas.getWidth();
    int height = this.mCanvas.getHeight();

    Bitmap rBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    rBitmap.hasAlpha(); //hilft evtl. dass das Bild richtig gespeichert wird

    int i = 0;
    for (int x = 0; x < width; x++) {
        for (int y = 0; y < height; y++) {
            int r = (int) (accumulator[i * 3 + 0] / (float) samples);
            int g = (int) (accumulator[i * 3 + 1] / (float) samples);
            int b = (int) (accumulator[i * 3 + 2] / (float) samples);

            rBitmap.setPixel(x, y, Color.rgb(r, g, b));
            i++;
        }
    }

    return rBitmap;
}


private CColor ComputeSample(int x, int y, int width, int height) {
    float fov = 160.0f * (float) Math.PI / 180.0f;
    float zdir = 1.0f / (float) Math.tan(fov);
    float aspect = (float) height / (float) width;

    float xdir = (x / (float) width) * 2.0f - 1.0f;
    float ydir = ((y / (float) height) * 2.0f - 1.0f) * aspect;

    //        CColor color = new CColor(0.0f, 0.0f, 0.0f);

    Vector3D direction = new Vector3D(xdir, ydir, zdir).normalize();
    Ray ray = new Ray(mCamera, direction);

    //        for(int row = 0; row < 2; row++){
    //            for(int col = 0; col < 2; col++){
    //                mCamera.x = mCamera.x + (col + 0.5f) / 2.0f;
    //                mCamera.y = mCamera.y + (row + 0.5f) / 2.0f;
    //                Ray ray = new Ray(mCamera, direction);
    //                color.add(Trace(ray,1));
    //            }
    //        }
    //
    //        return color.divide(4.0f);

    return Trace(ray, 1);
}


public CColor Trace(Ray ray, int depth) {
    float hitDistance = 5000.0f;
    BaseObject hitObject = null;

    for (BaseObject obj : Scene.getObjectList()) {
        float intersect = obj.intersect(ray);
        if (intersect < hitDistance && intersect > -1.0f) {
            hitDistance = intersect;
            hitObject = obj;
        }
    }

    //kein Objekt getroffen
    if (hitDistance == 5000.0f) {
        return new CColor(0, 0, 0);
    }

    //das getroffene Objekt ist eine Lichtquelle
    if (hitObject.isEmitter) {
        return hitObject.color;
    }

    //die maximale Tiefe ist erreicht
    if (depth == this.RAY_DEPTH) {
        return new CColor(0, 0, 0);
    }

    Vector3D hitPoint = ray.origin.add(ray.direction.mult(hitDistance * 0.99f)); //Behebung der floating point precision mit *0.99f
    Vector3D normal = hitObject.normal(hitPoint);

    Ray reflectionRay = null;

    if (hitObject.material == Material.Diffuse) {
        Vector3D randomVector = Vector3D.getRandomVectorInHemisphere(); //zufälliger Punkt bei diffusem Material
        if (randomVector.dot(normal) < 0.0f) //falls der Punkt in der falschen Hemisphere liegt
            randomVector = randomVector.negate();

        reflectionRay = new Ray(hitPoint, randomVector.normalize()); //Reflektionsvector
    }

    CColor returnColor = Trace(reflectionRay, depth + 1);

    float r = hitObject.color.getR() * returnColor.getR();
    float g = hitObject.color.getG() * returnColor.getG();
    float b = hitObject.color.getB() * returnColor.getB();

    r /= 255.0f;
    g /= 255.0f;
    b /= 255.0f;

    return new CColor(r, g, b);
}

}

Das müssten alle Änderungen gewesen sein, die ich bis jetzt vorgenommen habe. Wenn du dir den Code genau ansiehst, wird dir auffallen, dass an vielen Stellen nicht mehr auf die Variablen dirket zugegriffen wird.
Aktuell hast du nämlich schon Probleme mit dem Scope und das nicht nur an 1-2 Stellen, sondern im ganzen Programm...

An die Mods: Es wäre hilfreich, wenn in diesem Thread eine Ausnahme bestehen würde was die externen Links angeht. Jedenfalls ist das meine Ansicht (muss ja niemand teilen^^)... Gleichzeitig kann ich es aber auch nachvollziehen, dass es natürlich ein Sicherheitsrisiko darstellt und wenn man es in einem Thread erlaubt ggf. in anderen Threads ebenfalls eine "Extrawurst" gewünscht wird... Daher geht meine Idee in Richtung "Leute für Links freischalten", oder eine temporäre Linkbereitstellung, oder dass AndroidPIT als Hoster für Textdateien* bis vll. 100KB bereitsteht o.Ä.
Per PM zu versenden wäre natürlich auch eine Möglichkeit, aber ehrlich gesagt finde ich keinen "Posteingang" bei mir (nur in den Mails und dort übersehe ich gerne mal was)
*wobei das mit den Textdateien wohl der Grundstein alles Übles sein dürfte, daher ist dieser Vorschlag wohl eher mit "vorsicht" zu genießen :)

— geändert am 22.12.2014, 18:55:40

Open Source

superSharp

Antworten
  • Forum-Beiträge: 89

23.12.2014, 22:46:02 via Website

Danke für die Ladung an Code :)

Allerdings hilft mir diese "schöne" Programmieren jetzt erstmal nicht bei meinen Problemen. Wenn später irgendwann mal die Darstellung reibungslos läuft kümmere ich mich um den Zugriff und die Sichtbarkeit. Aktuell funktioniert die App auf meiner Smartwatch einwandfrei (beim Wechseln auf den Homescreen stürzt die App nicht ab und beim Zurückwechseln wird weitergerendert, auch wenn das Bild nicht sofort beim neuen Sample geupdated wird, warum auch immer? Hier vielleicht noch eine Idee?).

Auf meinem großen Smartphone mit entsprechend höherer Auflösung dauert die Berechnung eines Samples schon mal gut 30 Sekunden. Beim Zurückwechseln vom Hauptmenü in die App funktioniert auch alles noch auch wenn ich jetzt schon gut eine Minute auf das Updaten des Bildinhalts warte. Beim Drücken rechts oben auf die drei Punkte (Optionsmenü) passiert erstmal eine halbe Minute lang nichts bevor dann die App mit dieser Meldung abstürzt:

12-23 22:39:55.445 23515-23515/com.example.standardbenutzer.adelpath A/libc﹕ Fatal signal 6 (SIGABRT) at 0x00000909 (code=0), thread 23515 (nutzer.adelpath)

— geändert am 23.12.2014, 22:47:03

Antworten
  • Forum-Beiträge: 434

23.12.2014, 23:33:50 via Website

Es ist keine Frage von "schönem Code" - glaube mir, ich bin der letzte, der leicht lesbaren Code bzw. "schönen Code" schreibt, aber es gibt ein paar Sachen, die du beherzigen solltest, da anderenfalls daraus "harte Fehler" entstehen.
Dass die App auf der Smartwatch läuft, ist zwar ein gutes Zeichen, kann aber ganz verschiedene Gründe haben.
Vielleicht ist die API der Smartwatch beschränkter und deswegen wirken sich die Überscheidungen des Scopes nicht in der Form aus, wie auf dem Smartphone - keine Ahnung, ich kenne mich mit der API nicht aus. Möglich ist dieses Szenario definitiv. Vielleicht ist die Smartwatch auch hinsichtlich der Anzahl maximaler Threads limitiert und deswegen kommt dein Code zu einer schnelleren Ausführung.
Ich sehe jedenfalls keinen Sinn darin nur aus "Denk-Faulheit" oder "schlechter Praxis" einfach auf Teufel komm raus ein paar Threads zu starten um dann davon auszugehen, dass einer von Ihnen die Arbeit vollrichtet.
Vor zwei Tagen erschien mir in deinem Code genau das der Fall zu sein... Aber vielleicht irre ich mich auch ;)

Bin mal gespannt, wer dir hier weiterhilft. Ohne einen Ablaufplan, der sinnvollen Nutzung von Konstruktoren und einer in sich geschlossenen Logik wird man dir aller Wahrscheinlichkeit nach nicht helfen können. Sprich: Jeder der dir helfen soll kann sich dran machen 6-7 Klassen zu verbessern, in denen allesamt Java-Basics missachtet wurden.

Open Source

superSharp

Antworten
  • Forum-Beiträge: 89

24.12.2014, 00:15:43 via Website

Hallo und danke für deine Antwort,

Ich sehe jedenfalls keinen Sinn darin nur aus "Denk-Faulheit" oder "schlechter Praxis" einfach auf Teufel komm raus ein paar Threads zu starten um dann davon auszugehen, dass einer von Ihnen die Arbeit vollrichtet.
Vor zwei Tagen erschien mir in deinem Code genau das der Fall zu sein... Aber vielleicht irre ich mich auch

Ich starte doch nicht willkürlich beliebig viele Threads!? Es läuft immer nur einer, beim Wechseln vom Hauptmenü in die App wird der alte beendet und ein neuer gestartet

Sprich: Jeder der dir helfen soll kann sich dran machen 6-7 Klassen zu verbessern, in denen allesamt Java-Basics missachtet wurden.

Du meinst also für jede Variable eine getter und setter Methode zu schreiben ändert den kompletten Ablauf des Programms? Dann werde ich das mal ausprobieren.

Antworten
  • Forum-Beiträge: 89

24.12.2014, 15:58:43 via Website

Ich hab die Änderungen im Code so umgesetzt wie du in deinem letzten Post geschrieben hast. Die verzerrte Darstellung der SurfaceView tritt aber immer noch nach dem Wiederanschalten des Bildschirms auf, zwar nicht immer, aber ab und zu. Ist das Willkür von Android dass die View einfach verzerrt wird oder liegt das an meiner App?

Antworten
  • Forum-Beiträge: 1.904

24.12.2014, 16:42:16 via App

Ich kenne mich zwar in der Canvastechnik überhaupt nicht aus, aber ich denke die Verzerrung kommt von Orientations Changes. Die App scheint ein Spiel zu sein, die meisten Spiele verbieten gleich im Manifest Orientation Changes. Probier mal aus.

Wenn dir mein Beitrag gefällt, kannst dich einfach mit dem 👍 "Danke"-Button auf der Website dieses Forums bedanken. 😀

Why Java? - Because I can't C#

Antworten
  • Forum-Beiträge: 434

24.12.2014, 16:48:44 via Website

Hehe, das freut mich :) Also funktioniert das Programm jetzt (etwas) besser?
Den Fehler, den du hinsichtlich der verzerrten Darstellung beschreibst, kann leider mehrere Ursachen haben und ist unter Umständen nicht ganz einfach zu beheben.

Sofern der Fehler nicht durch die Darstellung selbst hervorgerufen wird (sprich: durch den Code, der für die Darstellung ausgeführt wird), ist meiner Einschätzung nach der Zwischenspeicher für die fehlerhafte Darstellung verantwortlich..

Oder "einfach" ausgedrückt: Wenn der Fehler wirklich nur beim Wiedereinschalten entsteht und du deinen Thread korrekt pausierst / beendest bevor du die Activity verlässt, ist die Wahrscheinlichkeit groß, dass der RAM hier die Probleme macht.

Aber wirklich sicher bin ich mir nicht..

Tipp 1
Tipp 2

Open Source

Antworten
  • Forum-Beiträge: 89

24.12.2014, 17:05:57 via Website

Die App scheint ein Spiel zu sein

Ne eher kein Spiel (zumindest kein typisches).

verbieten gleich im Manifest Orientation Changes. Probier mal aus.

Hab ich mal probiert:

        <activity
        android:name="com.example.standardbenutzer.adelpath.MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:configChanges="keyboardHidden|orientation"
        >

Hat aber leider nichts geholfen. Es tritt auch nicht immer auf, jetzt gerade musste ich zweimal zwischen Homescreen und App hin- und her wechseln und den Bildschirm ausschalten, bis das Problem auftritt.

Also funktioniert das Programm jetzt (etwas) besser?

Auf der Smartwatch hat es ja vorher schon gut funktioniert. Auf dem Smartphone wird die App aber nach wie vor beendet, sobald ich vom Homemenü wieder zurück in die App wechsel, und zwar mit dieser Meldung:

12-24 17:02:41.860  10118-10118/com.example.standardbenutzer.adelpath A/libc﹕ Fatal signal 6 (SIGABRT) at 0x00000909 (code=0), thread 10118 (nutzer.adelpath)

Sofern der Fehler nicht durch die Darstellung selbst hervorgerufen wird (sprich: durch den Code, der für die Darstellung ausgeführt wird)

Da die Verzerrung nicht auftritt wenn ich das Display einfach angeschaltet lasse bzw. nicht ins Homemenü wechsel und der Fehler auch nicht immer auftritt kann ich das eigentlich fast ausschließen.

ist die Wahrscheinlichkeit groß, dass der RAM hier die Probleme macht.

Puhh, okay... :(

— geändert am 24.12.2014, 17:07:03

Antworten
  • Forum-Beiträge: 89

26.12.2014, 22:15:29 via Website

Schöne Weihnachten zusammen :)

Hat wer Lust, die App bei sich zu testen und zu schauen, ob die gleiche Fehlermeldung kommt?

— geändert am 26.12.2014, 22:15:51

Antworten