Fragen zum Thmea Thread / Callback

Dieses Thema im Forum "Android App Entwicklung" wurde erstellt von GENiALi, 06.06.2012.

  1. GENiALi, 06.06.2012 #1
    GENiALi

    GENiALi Threadstarter Erfahrener Benutzer

    Beiträge:
    248
    Erhaltene Danke:
    10
    Registriert seit:
    25.08.2011
    Hallo

    Ich bin an einem grösseren Projekt. Deshalb will ich einige Funktionalitäten kapseln und nicht alles in Activities machen. Ich muss Aufgaben asynchron ausführen können und dabei auch gewisse Events empfangen können. So ähnlich wie bei einem Clickevent bei einem Button.

    Deshalb habe ich mir ein kleines Testprojekt gebaut das mir die Fibonacci Zahl berechnet. In zwei Versionen. Einmal mit einer Klasse die von Runnable erbt und einmal von AsyncTask.

    Dabei sollen im Gui ein paar Views aktuallisiert werden. Mein Problem nun, dass mit Runnable tut nicht wie gewünscht, dass mit dem AsyncTask tut.

    Die Berechnung läuft auf beiden Versionen, aber das aktuallisieren des Guis tut nicht bei der Runnable Version.

    Ich habe das Projekt auf Bitbucket. Denke das ist am einfachsten.
    https://bitbucket.org/GENiALi/thread-callback

    Was muss ich anpassen damit auch die Version mit Runnable funktioniert?
    Gibt es noch andere Möglichkeiten das Ziel zu erreichen als mit Runnable / AsyncTask?

    Ich bin für jeden Patch dankbar der mich weiterbringt. Würde die Lösung dann auch stehen lassen, so quasi als Sample.

    PS: Wenn man dem Code anmerkt das ich aus der .NET (C#) komme, entschudligt dass und sagt mir was ich anders machen soll. :tongue:
     
  2. Tom299, 06.06.2012 #2
    Tom299

    Tom299 Android-Experte

    Beiträge:
    602
    Erhaltene Danke:
    120
    Registriert seit:
    31.08.2011
    Die Stop-Methode kommt mir komisch vor, du fragst false ab und setzt auf true, hätte ich jetzt umgekehrt erwartet. Aber hab mir den Code nicht im Detail angeschaut.

    Deine Methoden sollten auch FiBonacci und nicht FiVonacci heißen :flapper:

    Aber zum eigentlichen Thema: Ich würde das mit nem Handler machen, das ist viel einfacher als mit EventListener. Den Handler implementierst du in deiner Activity und gibst ihn den Thread-Klassen (z.B. im Konstruktor) mit. Wenn der Thread seine Arbeit verrichtet hast, erzeugst du auf dem Handler ne Message:
    Code:
    Message msg = Message.obtain();
    msg.arg1 = ACTION_XYZ;
    handler.sendMessage(msg);
    
    Deine Activity reagiert dann in der Handler-Implementierung darauf. Dann brauchst du auch kein Interface.

    Das Message-Objekt hat mehrere Parameter, u.a. kannste sogar ein richtiges Object reinpacken und bist nicht auf Parcelable beschränkt.

    Ansonsten, wenn die GUI nicht aktualisiert wird, versuch mal ein runOnUIThread(), das nutze ich oft.

    Oder debug mal, ob dein 2. Thread im Listener ankommt. Bin mir nämlich nicht sicher, ob das mit 1 Listener funktioniert oder ob du nicht 2 brauchst. Kann man aber ausprobieren.
     
    GENiALi bedankt sich.
  3. GENiALi, 06.06.2012 #3
    GENiALi

    GENiALi Threadstarter Erfahrener Benutzer

    Beiträge:
    248
    Erhaltene Danke:
    10
    Registriert seit:
    25.08.2011
    Und ich dachte ich hätte den Fivonacci korrigiert. :)
    Werde das mit den Handlern mal anschauen. Ich nehme an du meinst bei der Runnable Version. Die mit dem AsyncTask tut ja.
     
  4. Tom299, 06.06.2012 #4
    Tom299

    Tom299 Android-Experte

    Beiträge:
    602
    Erhaltene Danke:
    120
    Registriert seit:
    31.08.2011
    ok, hab deinen Fehler gefunden ...

    ist mir erst aufgefallen, als ich gesehen habe, daß du die run()-Methode direkt aufrufst. das macht man nicht, man ruft immer die start()-Methode auf. aber es gibt keine start()-Methode, warum nicht? weil du keinen Thread hast :D

    du mußt dein Runnable einem thread übergeben und diesen starten, dann klappts auch:

    Code:
    		tvFinsih.setText("running....");
    		int number = Integer.parseInt(etNumber.getText().toString());
    		fibonacciOne = new FibonacciOne(number);
    		fibonacciOne.setListener(this);
    //		fibonacciOne.run();
    		Thread thread = new Thread(fibonacciOne);
    		thread.start();
    
     
    GENiALi bedankt sich.
  5. GENiALi, 06.06.2012 #5
    GENiALi

    GENiALi Threadstarter Erfahrener Benutzer

    Beiträge:
    248
    Erhaltene Danke:
    10
    Registriert seit:
    25.08.2011
    Jetzt habe ich das gefürchtete Sync Problem mit dem UI.
    ViewRoot$CalledFromWrongThreadException

    Jetzt wäre es wohl an der Zeit etwas mit Handler'n zu machen. Oder?
     
  6. Tom299, 06.06.2012 #6
    Tom299

    Tom299 Android-Experte

    Beiträge:
    602
    Erhaltene Danke:
    120
    Registriert seit:
    31.08.2011
    Code:
    	public void progress(final int value)
    	{
    //		tvCurrent.setText("Current: " + String.valueOf(value));
    		runOnUiThread(new Runnable() {
    			public void run() {
    				tvCurrent.setText("Current: " + String.valueOf(value));
    			}
    		});
    	}
    
    	public void finished(final int value)
    	{
    //		tvFinsih.setText("Finish: " + String.valueOf(value));
    		runOnUiThread(new Runnable() {
    			public void run() {
    				tvFinsih.setText("Finish: " + String.valueOf(value));
    			}
    		});
    	}
    
    	public void failed(final Exception ex)
    	{
    //		tvException.setText("Exception: " + ex.toString());
    		runOnUiThread(new Runnable() {
    			public void run() {
    				tvException.setText("Exception: " + ex.toString());
    			}
    		});
    	}
    
     
    GENiALi bedankt sich.
  7. GENiALi, 06.06.2012 #7
    GENiALi

    GENiALi Threadstarter Erfahrener Benutzer

    Beiträge:
    248
    Erhaltene Danke:
    10
    Registriert seit:
    25.08.2011
    Cool. Tut auch.
    Gäbe es sonst noch eine Art die Aufgabe so zu lösen wie ich es mir hier angedacht habe?
     
  8. Tom299, 06.06.2012 #8
    Tom299

    Tom299 Android-Experte

    Beiträge:
    602
    Erhaltene Danke:
    120
    Registriert seit:
    31.08.2011
    mit nem Handler z.B.:

    Code:
    public class FibonacciThird implements Runnable
    {
    	private int					startValue	= 0;
    	public boolean				keepRunning;
    	private int					count		= 0;
    	private Handler 			handler;
    	
    	public FibonacciThird(Handler handler, int value)
    	{
    		this.startValue = value;
    		this.handler = handler;
    	}
    
    	public void run()
    	{
    		int result = 0;
    
    		try
    		{
    			result = fib(startValue);
    		}
    		catch (Exception ex)
    		{
    			failed(ex);
    		}
    
    		finished(result);
    		
    	}
    
    	private int fib(int value)
    	{
    		count++;
    		
    		if (keepRunning)
    		{
    			return value;
    		}
    
    		if(count % 10 == 0)
    		{
    			progress(count);
    		}
    
    		if (value <= 1)
    		{
    			return value;
    		}
    		else
    		{
    			return fib(value - 1) + fib(value - 2);
    		}
    	}
    
    	public void progress(int value)
    	{
    		// handler
    		Message msg = Message.obtain();
    		msg.what = MainActivity.ACTION_PROGRESS;
    		msg.arg1 = value;
    		handler.sendMessage(msg);
    	}
    
    	public void finished(int value)
    	{
    		// handler
    		Message msg = Message.obtain();
    		msg.what = MainActivity.ACTION_FINISHED;
    		msg.arg1 = value;
    		handler.sendMessage(msg);		
    	}
    
    	public void failed(Exception ex)
    	{
    		// handler
    		Message msg = Message.obtain();
    		msg.what = MainActivity.ACTION_FAILED;
    		msg.obj = ex;
    		handler.sendMessage(msg);		
    	}
    
    }
    
    Code:
    public class MainActivity extends Activity implements OnClickListener, ThreadEvtListener
    {
    	private Button			btnStartFibonacciOne;
    	private Button			btnStartFibonacciSecond;
    	private Button			btnStop;
    	private TextView		tvCurrent;
    	private TextView		tvFinsih;
    	private TextView		tvException;
    	private FibonacciOne	fibonacciOne;
    	private FibonacciSecond fibonacciSecond;
    	private EditText		etNumber;
    
    	private Button			btnStartFibonacciThird;
    	private Handler 		handler;
    	private FibonacciThird 	fibThird;
    	private Thread 			fibThreadThird;
    	
    	public static final int 		ACTION_PROGRESS = 1;
    	public static final int 		ACTION_FINISHED = 2;
    	public static final int 		ACTION_FAILED = 3;
    	
    	/** Called when the activity is first created. */
    	@Override
    	public void onCreate(Bundle savedInstanceState)
    	{
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    
    		btnStartFibonacciOne = (Button) findViewById(R.id.btnStartFibonacciOne);
    		btnStartFibonacciSecond = (Button) findViewById(R.id.btnStartFibonacciSecond);
    		btnStop = (Button) findViewById(R.id.btnStop);
    
    		btnStartFibonacciOne.setOnClickListener(this);
    		btnStartFibonacciSecond.setOnClickListener(this);
    		btnStop.setOnClickListener(this);
    
    		tvCurrent = (TextView) findViewById(R.id.tvCurrent);
    		tvFinsih = (TextView) findViewById(R.id.tvFinish);
    		tvException = (TextView) findViewById(R.id.tvException);
    		
    		etNumber = (EditText)findViewById(R.id.etNumber);
    		
    		btnStartFibonacciThird = (Button) findViewById(R.id.btnStartFibonacciThird);
    		btnStartFibonacciThird.setOnClickListener(new OnClickListener() {
    			public void onClick(View v) {
    				runOnUiThread(new Runnable() {
    					public void run() {
    						tvFinsih.setText("running....");
    					}
    				});
    				int number = Integer.parseInt(etNumber.getText().toString());
    				fibThird = new FibonacciThird(handler, number);
    				fibThreadThird = new Thread (fibThird);
    				fibThreadThird.start();
    			}
    		});
    		
    		handler = new Handler(new Callback() {
    			public boolean handleMessage(Message msg) {
    				if (msg != null) {
    					switch (msg.what) {
    						case ACTION_PROGRESS:
    							progress(msg.arg1);
    							break;
    						case ACTION_FINISHED:
    							finished(msg.arg1);
    							break;
    						case ACTION_FAILED:
    							failed((Exception)msg.obj);
    							break;
    						default:
    							break;
    					}
    				}
    				return false;
    			}
    		});
    	}
    
    	public void onClick(View v)
    	{
    		switch (v.getId())
    		{
    			case R.id.btnStartFibonacciOne:
    				startFivonacciOne();
    				break;
    			case R.id.btnStartFibonacciSecond:
    				startFivonacciSecond();
    				break;
    			case R.id.btnStop:
    				stop();
    				break;
    			default:
    				break;
    		}
    	}
    
    	private void startFivonacciSecond()
    	{
    		tvFinsih.setText("running....");
    		int number = Integer.parseInt(etNumber.getText().toString());
    		
    		fibonacciSecond = new FibonacciSecond();
    		fibonacciSecond.setListener(this);
    		fibonacciSecond.execute(number);
    	}
    
    	private void stop()
    	{
    		if (fibonacciOne != null)
    		{
    			if (fibonacciOne.keepRunning == false)
    			{
    				fibonacciOne.keepRunning = true;
    			}
    		}
    		
    		if (fibonacciSecond != null)
    		{
    			if (fibonacciSecond.keepRunning == false)
    			{
    				fibonacciSecond.keepRunning = true;
    			}
    		}
    
    		if (fibThird != null)
    		{
    			if (fibThird.keepRunning == false)
    			{
    				fibThird.keepRunning = true;
    			}
    		}
    	}
    
    	private void startFivonacciOne()
    	{
    		tvFinsih.setText("running....");
    		int number = Integer.parseInt(etNumber.getText().toString());
    		fibonacciOne = new FibonacciOne(number);
    		fibonacciOne.setListener(this);
    //		fibonacciOne.run();
    		Thread thread = new Thread(fibonacciOne);
    		thread.start();
    	}
    
    	public void progress(final int value)
    	{
    //		tvCurrent.setText("Current: " + String.valueOf(value));
    		runOnUiThread(new Runnable() {
    			public void run() {
    				tvCurrent.setText("Current: " + String.valueOf(value));
    			}
    		});
    	}
    
    	public void finished(final int value)
    	{
    //		tvFinsih.setText("Finish: " + String.valueOf(value));
    		runOnUiThread(new Runnable() {
    			public void run() {
    				tvFinsih.setText("Finish: " + String.valueOf(value));
    			}
    		});
    	}
    
    	public void failed(final Exception ex)
    	{
    //		tvException.setText("Exception: " + ex.toString());
    		runOnUiThread(new Runnable() {
    			public void run() {
    				tvException.setText("Exception: " + ex.toString());
    			}
    		});
    	}
    	
    }
    
     
    GENiALi bedankt sich.
  9. GENiALi, 06.06.2012 #9
    GENiALi

    GENiALi Threadstarter Erfahrener Benutzer

    Beiträge:
    248
    Erhaltene Danke:
    10
    Registriert seit:
    25.08.2011
    OK. Das werde ich mir erste nach der Arbeit antun können. Werde es aber ncoh einbauen. Als Fibonacci 3. :)
     
  10. Tom299, 06.06.2012 #10
    Tom299

    Tom299 Android-Experte

    Beiträge:
    602
    Erhaltene Danke:
    120
    Registriert seit:
    31.08.2011
    Ich würde aber anstatt Runnable zu implementieren von Thread ableiten:
    Code:
    public class FibonacciThird extends Thread
    dann mußt du nicht noch extra ein Thread erstellen, sondern kannst nach dem Konstruktor gleich start() aufrufen. ist aber geschmackssache.
     
  11. GENiALi, 06.06.2012 #11
    GENiALi

    GENiALi Threadstarter Erfahrener Benutzer

    Beiträge:
    248
    Erhaltene Danke:
    10
    Registriert seit:
    25.08.2011

Diese Seite empfehlen