Packageübergreifende Variable: "Die Anwendung ... wurde unerwartet beendet"

  • 14 Antworten
  • Neuester Beitrag
Diskutiere Packageübergreifende Variable: "Die Anwendung ... wurde unerwartet beendet" im Android App Entwicklung im Bereich Betriebssysteme & Apps.
A

Andro-medanebel

Neues Mitglied
Mein Programm soll solange warten, bis onSizeChanged ausgeführt wurde,
da dort Breite und Höhe meines Layouts ermittelt werden, die ich für
die weitere Programmausführung brauche.

Dafür wollte ich eine Hilfsvariable nutzen, die mit 0 initialisiert wird
und auf 1 gesetzt wird, wenn onSizeChanged() ausgeführt wurde.

Für diese Hilfsvariable habe ich ein Package mit einer Klasse,
die eine get- und eine set-Methode enthält, geschaffen.

Wenn ich jedoch ein Zugriff auf die Methoden erfolgt, bekomme
ich einen "Die Anwendung ... wurde unerwartet beendet"-Fehler.

Meine Programmstruktur:
Code:
com
 customview
   CustomLayout.java
 memory
   MemoryActivity.java
variablen_fuer_mehrere_klassen
   VariablenFuerActivityUndView.java
In CustomLayout.java und MemoryActivity.java importiere ich die Hilfsklasse:
Code:
import variablen_fuer_mehrere_klassen.VariablenFuerActivityUndView;
Meine Hilfsklasse im Package variablen_fuer_mehrere_klassen;:
Code:
package variablen_fuer_mehrere_klassen;

public class VariablenFuerActivityUndView {
 public static int hilfsvar = 0;
 public int getHilfsvar() {
    return hilfsvar;}
 public void setHilfsvar(int i) {
        hilfsvar = i;}
}
In CustomLayout.java versuche ich nun in der Methode onSizeChanged,
die Methode setHilfsvar aufzurufen, was jedoch zu dem Laufzeitfehler
"Die Anwendung ... wurde unerwartet beendet" führt.

Code:
...
import variablen_fuer_mehrere_klassen.VariablenFuerActivityUndView;
public class CustomLayout extends RelativeLayout{
    VariablenFuerActivityUndView h;
      protected void onSizeChanged(int w, int h, int ow, int oh)
      {
       ...
       setzeHilfsvarAufEins(); //direkter Aufruf von h.setHilfsvar(1); führt hier
                               //zu "Cannot invoke setHilfsvar(int) on the primitive type int"-Fehler
                               //deshalb Aufruf in Methode verschoben
      }
      
      public void setzeHilfsvarAufEins()
      {
       h.setHilfsvar(1); //Führt zu "Die Anwendung ... wurde unerwartet beendet"-Fehler
      }
...
Logcat-Error-Ausgabe:
Code:
02-09 17:15:39.056: ERROR/AndroidRuntime(4764): FATAL EXCEPTION: main
02-09 17:15:39.056: ERROR/AndroidRuntime(4764): java.lang.NullPointerException
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at com.customview.CustomLayout.setzeHilfsvarAufEins(CustomLayout.java:54)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at com.customview.CustomLayout.onSizeChanged(CustomLayout.java:48)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.View.setFrame(View.java:7101)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.View.layout(View.java:7028)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.View.layout(View.java:7034)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1249)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1125)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.widget.LinearLayout.onLayout(LinearLayout.java:1042)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.View.layout(View.java:7034)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.View.layout(View.java:7034)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.ViewRoot.performTraversals(ViewRoot.java:1049)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1744)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.os.Handler.dispatchMessage(Handler.java:99)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.os.Looper.loop(Looper.java:144)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at android.app.ActivityThread.main(ActivityThread.java:4937)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at java.lang.reflect.Method.invokeNative(Native Method)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at java.lang.reflect.Method.invoke(Method.java:521)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
02-09 17:15:39.056: ERROR/AndroidRuntime(4764):     at dalvik.system.NativeStart.main(Native Method)
02-09 17:16:00.056: ERROR/ClockWidget(250): weatherClock onReceive~ mUseAnimation:false
02-09 17:16:00.056: ERROR/ClockWidget(250): weatherClock onReceive~ mUseAnimation:false
Weshalb bekomme ich den Laufzeitfehler und was muss ich ändern,
um auf meine get- und set-Methoden in der Hilfsklasse zugreifen
zu können?
 
Kranki

Kranki

Ehrenmitglied
Wird h auch irgendwo initialisiert?
 
A

Andro-medanebel

Neues Mitglied
Kranki schrieb:
Wird h auch irgendwo initialisiert?
Habe folgende Änderungen vorgenommen:

Code:
...
import variablen_fuer_mehrere_klassen.VariablenFuerActivityUndView;
public class MemoryActivity extends Activity {
    VariablenFuerActivityUndView h = new VariablenFuerActivityUndView();
    //vorher: VariablenFuerActivityUndView h;
   ...
   do {} while (h.getHilfsvar()==0);
...
import variablen_fuer_mehrere_klassen.VariablenFuerActivityUndView;
public class CustomLayout extends RelativeLayout{
    VariablenFuerActivityUndView h = new VariablenFuerActivityUndView();
    //vorher: VariablenFuerActivityUndView h;
Kann man das
Code:
VariablenFuerActivityUndView h = new VariablenFuerActivityUndView();
als initialisieren bezeichnen?
Dann habe ich das hiermit getan.

Nun bekomme ich keinen Laufzeitfehler mehr.
Allerdings lande ich mit
Code:
public class CustomLayout extends RelativeLayout{
    VariablenFuerActivityUndView h = new VariablenFuerActivityUndView();
    ...
    do {} while (h.getHilfsvar()==0);
    ...
in einer Endlosschleife.

Die Schleife sollte aber nur solange durchlaufen werden, bis
die Methode onSizeChanged hilfsvar auf 1 setzt.

Habe ich mir jetzt zwei unabhängige Klassenvariablen geschaffen?
Wie kann ich eine schaffen, deren Attributänderung (hilfsvar)
sich sowohl in der Klasse CustomLayout als auch in der Klasse
MemoryActivity auswirkt?
 
Z

Zoopa

Stammgast
Da deine Variable static ist, gibts nur eine davon, das ist schon richtig so.
Dein Problem ist, dass du zeitintensive Dinge (hier deine Schleife) in einen eigenen Thread packen musst, sonst blockiert dir der GUI-Thread.
 
A

Andro-medanebel

Neues Mitglied
Zoopa schrieb:
Da deine Variable static ist, gibts nur eine davon, das ist schon richtig so.
Dein Problem ist, dass du zeitintensive Dinge (hier deine Schleife) in einen eigenen Thread packen musst, sonst blockiert dir der GUI-Thread.
Aber meinen Thread zu blockieren ist ja gerade das, was ich will.
Die do-while-Schleife soll solange warten, bis onSizeChanged
aufgerufen wurde, weil vorher Breite und Höhe meines Layouts
nicht bekannt sind, ich aber abhängig von Breite und Höhe
unterschiedlich viele Buttons erzeugen will.
 
Kranki

Kranki

Ehrenmitglied
Pack dir erstmal Debug-Ausgaben (Log.d()) in den Setter deiner Hilfsklasse, in onSizeChanged und vor und nach der Schleife.
Außerdem wäre es evtl. gut, die Schleife auszukommentieren und jeweils mit Thread.currentThread() zu gucken, ob die beiden Methoden nicht zufällig im selben Thread laufen, sonst kann die Schleife da warten bis sie schwarz wird.

Davon ganz abgesehen würd ich die Endlos-Schleife durch einen synchronized-Block und wait()/notify() ersetzen.
 
A

Andro-medanebel

Neues Mitglied
Kranki schrieb:
Pack dir erstmal Debug-Ausgaben (Log.d()) in den Setter deiner Hilfsklasse, in onSizeChanged und vor und nach der Schleife.
Außerdem wäre es evtl. gut, die Schleife auszukommentieren und jeweils mit Thread.currentThread() zu gucken, ob die beiden Methoden nicht zufällig im selben Thread laufen, sonst kann die Schleife da warten bis sie schwarz wird.

Davon ganz abgesehen würd ich die Endlos-Schleife durch einen synchronized-Block und wait()/notify() ersetzen.
Thread.currentThread() gibt sowohl in VariablenFuerActivityUndView.setHilfsvar(int i)
als auch in MemoryActivity die Ausgabe "Thread[main,5,main]".
Das interpretiere ich mal so, das beides im gleichen Thread läuft.

Nun habe ich versucht, die Klasse VariablenFuerActivityUndView als eigenen Thread
laufen zu lassen und habe folgende Änderungen im Programm vorgenommen:

VariablenFuerActivityUndView.java:
Code:
package variablen_fuer_mehrere_klassen;

import android.util.Log;

public class VariablenFuerActivityUndView extends Thread {
 public static int hilfsvar = 0;
 public int getHilfsvar() {
    return hilfsvar;}
 synchronized public void setHilfsvar(int i) 
 { 
   //Log.d("setter Hilfsklasse","qqq"+Thread.currentThread());
   hilfsvar = i;
 }
 public void run() {
     setHilfsvar(1);
 }

}
CustomLayout.java:
Code:
protected void onSizeChanged(int w, int h, int ow, int oh){
...
setzeHilfsvarAufEins();
}

public void setzeHilfsvarAufEins()
    {
        h.start();
        //h.setHilfsvar(1);
    }
In MemoryActivity.java soll gewartet werden, bis
h.hilfsvar auf 1 gesetzt wird:

Code:
synchronized (this) {
            if (h.getHilfsvar()==0){
                try {wait();} catch(InterruptedException e) {}

            }
            notify();
        }
Mit dem synchronized (this) lande ich allerdings in einer Endlosschleife.
Woran kann das liegen?
 
Zuletzt bearbeitet:
Z

Zoopa

Stammgast
Das Grundproblem ist immer noch dasselbe. Du wirst nicht drum herum kommen, deine Hilfsvar-Überprüfung in einen eigenen Thread zu packen.

So wie du es jetzt hast, wird vermutlich der UI-Thread beim wait() schlafen gelegt und nie mehr durch ein notify() eines anderen Threads geweckt, wodurch dann dein ganzes GUI nicht mehr reagiert.

Du willst vermutlich, dass ein Teil des Codes erst ausgeführt wird, wenn Hilfsvar = 1 ist. Daher könntest du die Überprüfung von Hilfsvar in einen eigenen Thread auslagern und zusätzlich in deiner Activity (MemoryActivity?) einen Handler definieren.

Dann kannst du z.B. in deinem Prüf-Thread, sobald Hilfsvar == 1 ist, eine Message an den Handler im Main-Thread schicken, und dieser Handler führt dann den weiteren Code aus.

Hier noch ein Blogeintrag und ein Google-Beispiel zum Thema.
 
M

maniac103

Experte
Für mich klingt's eher so, als ob die Activity (== UI-Thread) auf das Aufrufen eines View-Callbacks (UI-Callback aus dem Framework, also auch == UI-Thread) warten soll. Das geht nicht ;)

Ich frage mich eher, was mit dem Warten erreicht werden soll? Was spricht dagegen, die Activity erst mal loslaufen zu lassen und später umzukonfigurieren?
 
A

Andro-medanebel

Neues Mitglied
maniac103 schrieb:
Für mich klingt's eher so, als ob die Activity (== UI-Thread) auf das Aufrufen eines View-Callbacks (UI-Callback aus dem Framework, also auch == UI-Thread) warten soll. Das geht nicht ;)
Ja, ich glaube es ist so. Meine Activity soll warten, bis im (Custom-)View
automatisch onSizeChanged aufgerufen wurde.

Ich frage mich eher, was mit dem Warten erreicht werden soll? Was spricht dagegen, die Activity erst mal loslaufen zu lassen und später umzukonfigurieren?
Mein CustomLayout hat je nach Device eine unterschiedliche Breite
und Höhe, die aber erst - wie ich gelesen habe - bekannt ist, wenn
onSizeChanged aufgerufen wurde.

Die in onSizeChanged in der Klasse CustomLayout ermittelte
Breite und Höhe meines CustomLayout muß ich wissen, bevor
ich meine Activity fortführe, da ich abhängig von Breite und
Höhe des CustomView/CustomLayout eine unterschiedliche Zahl von
Buttons (Memorykarten) in meiner Activity erzeugen will.

kleines Display=wenig Memorykarten(Buttons):
***
***
***
***
großes Display=viele Memorykarten(Buttons):
*******
*******
*******
*******

Auf den Buttons (später dann wohl ImageButtons) sollen
Grafiken liegen, die ich aus res/drawable einlese.

Auch dafür muß ich wissen, wie viele Buttons im Layout dargestellt
werden können, denn wenn es z.B. nur 12 sind brauche ich nur
sechs Grafiken einlesen, wenn ich 28 Buttons darstellen kann,
muß ich dagegen 14 Images einlesen.
 
Kranki

Kranki

Ehrenmitglied
Dann hab ich hier sechs Links für dich:

Supporting Multiple Screens | Android Developers
Android-er: Exercise: Get screen resolution using android.util.DisplayMetrics
DisplayMetrics | Android Developers
getWindowManager().getDefaultDisplay().getMetrics()

Ach, und maniac103 hat dein Problem ganz richtig erkannt. Stell dir vor, du willst von deinem Schreibtisch aufstehen und in die Küche gehen, aber bleibst noch solange sitzen, bis du dir selber einen Kaffee gebracht hast - so läuft dein Programm gerade.
 
A

Andro-medanebel

Neues Mitglied
Kranki schrieb:
Dann hab ich hier sechs Links für dich:

Supporting Multiple Screens | Android Developers
Android-er: Exercise: Get screen resolution using android.util.DisplayMetrics
DisplayMetrics | Android Developers
getWindowManager().getDefaultDisplay().getMetrics()

Ach, und maniac103 hat dein Problem ganz richtig erkannt. Stell dir vor, du willst von deinem Schreibtisch aufstehen und in die Küche gehen, aber bleibst noch solange sitzen, bis du dir selber einen Kaffee gebracht hast - so läuft dein Programm gerade.
Von der Methode mit DisplayMetrics hatte ich zwar schon gelesen,
dachte aber, sie würde nicht taugen, da ich glaubte, die Beschreibung
"The absolute height of the display in pixels." für width/heightPixels
würde bedeuten, dass ich immer die gleiche Höhe und Breite
erhalten würde unabhängig davon, ob ich Titel/Statuszeile ein-/ausblende.
Das ist allerdings nicht so; Status- und Titelzeile werden von den
Pixeln abgezogen, wenn ich sie einblende. Daher kann ich die Methode
verwenden. Höhe und Breite über Höhe und Breite des Layouts
zu ermitteln, hätte noch den Vorteil gehabt, dass meine Anwendung
bei Android-Tablets, die Anwendungen nebeneinander anzeigen können,
neben einer anderen Anwendung angezeigt werden könnte. Mit
DisplayMetrics kann sie ja nur im Vollbildmodus laufen. Aber
das ist wohl erst mal nicht so wichtig.

Mit DisplayMetrics funktioniert es also :):

Code:
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
Log.d("displaymetrics.widthPixels", ""+displaymetrics.widthPixels);
Log.d("displaymetrics.heightPixels", ""+displaymetrics.heightPixels);
 
A

Andro-medanebel

Neues Mitglied
Habe mich jetzt ja für DisplayMetrics entschieden.
Hatte vorher aber noch versucht, meine dynamische Button-
Erzeugung von meiner Activity in mein CustomView zu verlegen;
so wäre es unnötig, eine Methode zu finden, in meiner
Activity solange zu warten, bis onSizeChanged des CustomView
aufgerufen wird, da der CustomView schon selbst die Buttons erzeugt.

Das Problem ist nur, dass das Button-Erzeugen dort nicht funktioniert:

Button-Erzeugung in class MemoryActivity extends Activity {:
Code:
...
//Funktioniert!
for(int i=0;i<anzahl_bilder;i++)
        {
         ib[i] = new ImageButton(this);
...
Button-Erzeugung in class CustomLayout extends RelativeLayout{
verschoben:
Code:
...
//Funktioniert nicht!
for(int i=0;i<anzahl_bilder;i++)
        {
         ib[i] = new ImageButton(this);
         //Fehler: The constructor ImageButton(CustomLayout) is undefined
         //habe es mit ib[i] = new ImageButton(null); versucht.
         //Das gibt keinen Syntax- aber einen Laufzeitfehler.
...
Gibt es eine einfache Änderung, mit der ich ImageButtons in
meinem CustomView erzeugen kann?

Wie gesagt, ich mache es jetzt mit DisplayMetrics, bin aber trotzdem
neugierig, ob es mit einer kleinen Änderung auch möglich wäre,
die Generierung der ImageButtons ins CustomView zu verschieben.

Vielen Dank an alle, die mir geholfen haben.:)
 
M

maniac103

Experte
Andro-medanebel schrieb:
Habe mich jetzt ja für DisplayMetrics entschieden.
Hatte vorher aber noch versucht, meine dynamische Button-
Erzeugung von meiner Activity in mein CustomView zu verlegen;
so wäre es unnötig, eine Methode zu finden, in meiner
Activity solange zu warten, bis onSizeChanged des CustomView
aufgerufen wird, da der CustomView schon selbst die Buttons erzeugt.

Das Problem ist nur, dass das Button-Erzeugen dort nicht funktioniert:

Button-Erzeugung in class MemoryActivity extends Activity {:
Code:
...
//Funktioniert!
for(int i=0;i<anzahl_bilder;i++)
        {
         ib[i] = new ImageButton(this);
...
Button-Erzeugung in class CustomLayout extends RelativeLayout{
verschoben:
Code:
...
//Funktioniert nicht!
for(int i=0;i<anzahl_bilder;i++)
        {
         ib[i] = new ImageButton(this);
         //Fehler: The constructor ImageButton(CustomLayout) is undefined
         //habe es mit ib[i] = new ImageButton(null); versucht.
         //Das gibt keinen Syntax- aber einen Laufzeitfehler.
...
Gibt es eine einfache Änderung, mit der ich ImageButtons in
meinem CustomView erzeugen kann?
Wie wär's damit?
Code:
for(int i=0;i<anzahl_bilder;i++)
        {
         ib[i] = new ImageButton(getContext());
;)

Eine Activity implementiert immer einen Context, deswegen kannst du da direkt 'this' hinschreiben. Das Layout hat den Context als Member-Variable, deshalb der Getter.
 
A

Andro-medanebel

Neues Mitglied
maniac103 schrieb:
Wie wär's damit?
Code:
for(int i=0;i<anzahl_bilder;i++)
        {
         ib[i] = new ImageButton(getContext());
;)

Eine Activity implementiert immer einen Context, deswegen kannst du da direkt 'this' hinschreiben. Das Layout hat den Context als Member-Variable, deshalb der Getter.
Aha, danke.
 
Ähnliche Themen - Packageübergreifende Variable: "Die Anwendung ... wurde unerwartet beendet" Antworten Datum
11