Threading Frage

  • Antworten:5
Hermann S.
  • Forum-Beiträge: 45

30.04.2011, 18:05:39 via Website

Hallo, ich habe eine kleine Threading Frage.

In der Startklasse werden Serverdaten innerhalb eines eigenen Threads geladen. Damit soll verhindert werden, dass die GUI einfriert, falls es Serverprobleme gibt. Wenn die Daten geladen sind sollen sie in die Felder der GUI eingetragen werden.

Das Problem mit dem Thread ist, man kann das Befüllen der Felder nicht während onCreate machen, weil die Serverdaten zum Zeitpunkt des Befüllens noch nicht da sind.

Ohne Thread geht es. Zuerst werden die Daten geladen, dann wird die GUI mit den Daten gefüllt (Alles in onCreate und GUI ist langsam)
Mit Thread wird die GUI gefüllt -> Exception und dann erst sind die Daten da.

Mein Problem ist , dass ich zwei Activities habe. In der ersten liegt die Logik zum Holen der Daten und die zweite Activity stellt die Daten dar, sie wird von der ersten Activity über new Intent().setClass(..) geöffnet. Ist es möglich eine Methode der zweiten Activity von der ersten aus aufzurufen, oder muss man bei diesem Problem mit PropertyChangeEvents arbeiten?

— geändert am 30.04.2011, 18:24:21

Antworten
Markus B.
  • Forum-Beiträge: 636

30.04.2011, 22:41:41 via Website

Hi,
also prinzipiell wäre es erst mal interessant zu wissen was für eine Exception du bekommst.
Desweiteren frage ich mich warum die Activity 1 die Logik zum Laden des Daten enthält. Wenn du eh schon einen Thread startest, kannst du dem doch auch die Logik zum Laden der Daten mit geben. Dann hast du das Problem mit dem aufrufen der Methoden nicht.


Gruß,
Markus

Antworten
Hermann S.
  • Forum-Beiträge: 45

01.05.2011, 02:12:51 via Website

Es handelt sich um eine NullpointerException, weil das Objekt noch nicht mit den geparsten Werten gesetzt ist. Der Aufbau der Klassen ist etwas schwierig zu erklären. Es sind eigentlich 4 verschiedene Klassen beteiligt.

1. ApplicationActivity, (Speichert die geparsten Daten in einem Java-Objekt)
2. TabActivity ladet die Daten von einem Server im Thread und legt sie in 1. ab.
3. Aktivity zum Anzeigen der Daten aus 1. (eingebettet als Tab in 2)
4. Aktivity zum Anzeigen der Daten aus 1. (eingebettet als Tab in 2)

Wenn man das Laden über ein onClickevent macht, dann gibt es auch keine Probleme. Beim Start der Applikation werden aber die gespeicherten Benutzerdaten und Serververbindung automatisch überprüft, die Tabs geladen und die Activities mit den Werten versorgt, dabei kommt es zu der Exception, weil das Befüllen in 3. und 4. im onCreate stattfindet und die wartet nicht auf die parallel ladenden Serverdaten :).

Ich habe zwei Lösungen für das Problem gefunden.
1. Auf den Thread verzichten, dann hängt die Gui einwenig während des Ladevorgangs.
2. Den PropertyChangeListener verwenden. Die Activity in 2. feuert ein Ereignis ab, wenn alle Daten geladen sind. Das löst in 3. und 4. das Befüllen aus.

EDIT:
Frage mich, ob man die Methode zum Befüllen in 3. auch direkt aus 2. starten könnte? Weiß nicht wie man die Referenz auf 3. bekommt, da es kein Objekt von 3. gibt, sondern nur einen Intent. 8o

— geändert am 01.05.2011, 12:59:09

Antworten
Markus B.
  • Forum-Beiträge: 636

01.05.2011, 22:00:18 via Website

Hi,
du solltest deine erste Lösung tunlichst vermeiden.
Android ist in Sachen GUI "hängen" ziemlich restriktiv und beendet deine Anwendung sollte es zulange anhalten.
Dazu solltest du dir mal das Thema ANR ansehen.

Gruß,
Markus

Ps.: Wenn du bei deinem Problem evtl. Unterstützung möchtest kann ich gerne mal über die Code schauen :) Evtl. können wir beide was lernen.

Antworten
Hermann S.
  • Forum-Beiträge: 45

02.05.2011, 02:39:31 via Website

Die Fehlermeldung kommt mir bekannt vor. :)

Der Code ist zu umfangreich, um ihn hier rein zu stellen. Ich hab eine kleine Beispielanwendung gebastelt. Sie ist etwas übersichtlicher. Soll das Problem zusammenfassen und simuliert folgendes Verhalten.

In MyStartActivity ändert sich irgendwas zB. Serverdaten sind fertig geladen. (Die Änderung wird im Beispiel über eine Endlosschleife im Thread simuliert)
In MySecondActivity soll auf die Änderung reagiert werden zB. Textfelder mit Inhalt füllen. (Das Befüllen wird über eine Laufvariable in einem Textfeld simuliert, damit man die Änderung sehen kann)

Hier verwende ich den PropertyChangeListener, so müssen sich die Activities nicht kennen. Allerdings bin ich auf eine dritte MyApp-Klasse angewiesen, welche den PropertyChangeSupport für die beiden Activities bereitstellt.

Wäre genial wenn man auf die Klasse MyApp verzichten könnte und die Methode setMessage() anstelle von firePropertyChange im Thread direkt aufrufen könnte, glaube aber, dass es nicht möglich ist.

MyApp:
1public class MyApp extends Application {
2
3 private PropertyChangeSupport support;
4
5 @Override
6 public void onCreate() {
7 super.onCreate();
8 support = new PropertyChangeSupport(this);
9 }
10
11 public PropertyChangeSupport getSupport() {
12 return support;
13 }
14}

MyStartActivity:
1public class MyStartActivity extends Activity {
2
3 int i = 0;
4 int oldState;
5 int newState;
6
7 private MyApp myApp;
8
9 @Override
10 public void onCreate(Bundle savedInstanceState) {
11 super.onCreate(savedInstanceState);
12 setContentView(R.layout.main);
13
14 myApp = (MyApp)getApplicationContext();
15
16 Thread thread = new Thread() {
17 public void run() {
18 while(true) {
19 machwas();
20 }
21 }
22
23 private void machwas() {
24 try {
25 sleep(3600);
26 change();
27 } catch (Exception e) {
28 Log.e("test","Fehler: "+e.toString());
29 }
30 }
31
32 };
33 thread.start();
34
35
36 Button sf_ok = (Button) findViewById(R.id.main_sf_ok);
37 sf_ok.setOnClickListener(
38 new OnClickListener() {
39
40 @Override
41 public void onClick(View v) {
42 Intent intent = new Intent(getApplicationContext(), MySecondActivity.class);
43 startActivity(intent);
44 removeDialog(0);
45 }
46 }
47 );
48
49 }
50
51 private void change() {
52 MyStartActivity.this.runOnUiThread(
53 new Runnable() {
54 public void run() {
55 oldState = i;
56 newState = ++i;
57 myApp.getSupport().firePropertyChange("CHANGE", oldState, newState);
58 }
59 }
60 );
61 }
62
63}

MySecondActivity:
1public class MySecondActivity extends Activity{
2
3 private class ThreadListener implements PropertyChangeListener{
4 @Override
5 public void propertyChange(PropertyChangeEvent evt) {
6 if(evt.getPropertyName().equals("CHANGE")){
7 Log.v("test", "Changed");
8 setMessage();
9 }
10 }
11 }
12
13 private int i = 0;
14 private TextView tv_message;
15 private MyApp myApp;
16
17 @Override
18 protected void onCreate(Bundle savedInstanceState) {
19 super.onCreate(savedInstanceState);
20 setContentView(R.layout.mysecondactivity);
21 myApp = (MyApp)getApplicationContext();
22 myApp.getSupport().addPropertyChangeListener(new ThreadListener());
23
24 tv_message = (TextView) findViewById(R.id.main_tv_message);
25 }
26
27 private void setMessage() {
28 tv_message.setText("Property Changed: "+i++);
29 }
30
31}

— geändert am 02.05.2011, 02:44:59

Antworten
Markus B.
  • Forum-Beiträge: 636

02.05.2011, 23:57:29 via Website

Hi,
also vom Prinzip her macht das doch alles Sinn.
Du startest die Aktivity und lädst Daten nach. Wenn die Daten geladen wurden sorgst du über einen Listener das alle relevanten Klassen darüber bescheid wissen. Jetzt man das Prozedere etwas "aufhübschen" und darüber mache ich mir gerade so meine Gedanken :)
Aber an und für sich ist das hier auch ein "standard Problem", welches sich sehr gut über Listener Lösen lässt.
Ich hoffe das ich bis Ende der Woche was brauchbares zusammen geschrieben bekomme. Mein Job hält mich im Moment auf Trapp.

Gruß,
Markus

Antworten