Network and UI

  • 5 Antworten
  • Letztes Antwortdatum
D

DeJe

Dauer-User
437
Ich stecke fest. ;)

Ich versuche eine Real-Time Anzeige für ein Gerät zu realisieren. Konkret geht es um eine Portalfräse mit LinuxCNC. Diese kann ich per IP kontrollieren/abfragen. Zunächst möchte ich die Positionsabfrage realisieren. Die Kommunikation erfolgt über ein Telnet-Interface.
Ich schicke ein Komando z.B. "get rel_act_pos" und die Anlage reagiert mit "GET REL_ACT_POS xxx.xxxx yyy.yyyy zzz.zzzz nnn.nnnn ...".

Soweit, so gut. Das funktioniert und ich bekomme die Daten rein. Mein Problem ist, das sich Netzwerk-Aktivitäten und UI-Aktivitäten unter Android spinnefeind sind. :( Da das Update aber durchaus mit vernünftiger FPS erfolgen soll, weiß ich einfach nicht weiter, wie ich das am besten löse...

Hier mal mein Code der bis auf das Update der UI super funktioniert.

Code:
package com.example.navigationdrawer;

import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TimerTask;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
 
public class FragmentOne extends Fragment {
 
    static FragmentOne f = null;
    
    static Button pos[] = { null, null, null };
    
    public static Fragment newInstance(Context context) {
    	f = new FragmentOne();
 
        return f;
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
        ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_one, null);
        
		pos[0] = (Button)root.findViewById(R.id.posX);
		pos[1] = (Button)root.findViewById(R.id.posY);
		pos[2] = (Button)root.findViewById(R.id.posZ);
		
        (new readRelPos()).execute("","","");
        
        Timer timerDRO = new Timer();
        final int FPS = 30;
        TimerTask updateDRO = new UpdateDROTask();
        timerDRO.scheduleAtFixedRate(updateDRO, 0, 30000/*1000/FPS*/);
        
        return root;
    }
 
    static class UpdateDROTask extends TimerTask {
	   public void run() {
		   MainActivity.mOut.println("get rel_act_pos");
	   }
    }

	static class readRelPos extends AsyncTask<String, String, String> {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... params) {

			String mh = "";
			
            try {
            	while( MainActivity.mIsConnected ) {
            		mh = MainActivity.mIn.readLine();
            		if( mh.startsWith("REL_ACT_POS") ) {
            			int i = 0;
            			StringTokenizer st = new StringTokenizer(mh);            			
            			st.nextToken();
            			while (st.hasMoreElements()) {
            				if( i > 2 ) break;
            				mh = st.nextToken();
//? that one doesn't work            				pos[i].setText(st.nextToken());
            				i++;
            			}
            		}
            	}
            } catch (IOException e) {
                e.printStackTrace();
            }
            return mh;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
        }
    }
}

Zur Erklärung: Der TimerTask sendet die Abfrage. Der AsyncTask soll eigentlich permanent auf dem InputStream horchen und wenn etwas da ist die UI updaten.
Das Problem: Entweder darf ich keine Netzwerkaktivitäten (insbesondere Lesen) ausführen oder keine UI-Aktivitäten! :( Andererseits möchte ich aus Performance Gründen nicht dauernd Threads/Tasks/Prozesse starten und beenden. Oh, noch ein Problem: readLine blockiert wenn keine Daten da sind. Das ist also ebenfalls gefährlich. ;)

Vielleicht hat jemand den zündenden Einfall...wie das einfach zu lösen wäre.

Danke,
DeJe
 
Das Hauptproblem dürfte bei dir

Code:
protected String doInBackground(String... params) {
  ...
  pos[i].setText(st.nextToken());
  ...
}

sein. doInBackground wird im separaten Thread ausgeführt, daher darfst du da keine UI-Dinge machen. Aber genau dafür gibts beim AsyncTask die Methode onProgressUpdate, die wird im UI-Thread ausgeführt und darin darfst du dann die UI-Elemente verändern. Als Beispiel könnte das dann etwa so aussehen:

Code:
protected String doInBackground(String... params) {
  ...
  publishProgress(st.nextToken());
  ...
}

protected void onProgressUpdate(String... params) {
  pos[i].setText(params[0]);
}

Ein AsyncTask ist aber eigenetlich eher für kürzere Operationen gedacht. Als Alternative könntest du normale Threads zusammen mit einem Handler verwenden. Die Netzwerk-Dinge werden in der run-Methode des Threads gemacht und der Handler wird dann verwendet, um eine Message (mit den gelesenen Daten) an den UI-Thread zu senden.
 
  • Danke
Reaktionen: DeJe
Ich habe es mittlerweile selbst gefunden... ;)
Deinen Vorschlag (onProgressUpdate) habe ich im Moment in etwa so implementiert und es funktioniert. Meine irrtümliche Annahme war, das ich dafür Min/Max Werte brauche. Manchmal sieht man den Wald vor lauter Bäumen nicht bzw. muß mal eine Nacht drüber schlafen. Mit 15FPS läuft das Ganze brauchbar.

Die Handler muß ich mir anschauen. Ist zu erwarten, das das performanter läuft?
Ich kann übrigens durchaus von der Annahme ausgehen, das das Gerät ausschließlich für diese App genutzt wird.

Wie auch immer, vielen dank für die Hinweise. Das wird in das ProfeOfConcept einfließen. :)
 
Hallo, das ganze sieht recht interessant aus. Aus reiner Neugier, wie schnell leert sich dabei dein Aku?
 
markus.tullius schrieb:
Aus reiner Neugier, wie schnell leert sich dabei dein Aku?
Keine Ahnung. :smile: Derzeit ist das wirklich nur ein Profe of Concept. Fakt ist, das Display muß permanent an sein, WiFi und 15 mal in der Sekunde ein AsyncTask... Ich gehe also davon aus, das das Gerät nicht allzu lange durchhält ohne Strippe.

Hier übrigens mal ein erstes Video:
Aber es funktioniert
 
Hi DeJe,

ich arbeite häufig an ähnlichen Projekten.
Aktuell visualisiere ich Verfahrenstechnische Abläufe einer SPS über ein Fließbild.

Meiner Erfahrung nach bietet sich ein TimerTask in zusammenspiel mit einem Handler als beste Lösung.
Zum Beispiel in deiner onResume() Methode:

Code:
TimerTask mTimer = new TimerTask();
Handler mHandler= new Handler();

myTimer.schedule(new TimerTask() {  
		
		@Override    
		public void run() {
			
			//**Hier die Daten übers Netzwerk lesen*//*				

			}
			
			myHandler.post(new Runnable() {            
				@Override            
				public void run() {                
						
				//**Hier anhand der gelesenen Daten dein Layout Updaten*//*					

				}       
			});    
		};
	}, 0L, 1L * 1000);

Wie du schon richtig erkannt hast, wird das bei stetiger Verwendung, deinen Akku vernaschen wie Süßigkeiten... Es wird ihn leerschlürfen wie einen Milchshake usw usw....

Die Entwicklung solcher Projekte führt aktuell noch in eine Sackgasse. Die Hersteller müssen zuersteinmal auf die *Notwendigkeit solcher Systeme reagieren und entsprechende Geräte zur Verfügung stellen.
*Es ist doch häufig nur eine Nice-To-Have Spielerei.

Panasonic macht hier schon einen Schritt. Mit ihren Toughbooks und Tough-Tablets versuchen sie hier auf dem Markt present zu sein. Bessere Akkus, Dockingstation usw. Aber auch eine Dockingstation macht es NICHT besser. Da du auch hier ständig Ladezyklen des Akkus hast. Solltest du dir mal ansehen wenns dich interessiert.

Anyway, schönes Projekt hast du da, und weiterhin viel Erfolg damit!
:thumbup:

Gruß Flocke
 
Zurück
Oben Unten