Ver la versión completa : Duda programacion Android
crossmax
20/09/2013, 12:37
Buenas compas!
Una vez mas (soy mas cansino que la leche!), acudo a vosotros a ver si alguien me puede aclarar el cacao mental que tengo con las activitys, intents, bundles, fragments y la madre del cordero.
Yo soy de ansi C y esto me esta superando.
El caso es que hay tanta info que estoy saturado. A ver si con un par de cosas que me digais puedo empezar a aclararme y saber por donde tirar.
Estoy haciendo una apli que por un socket va pidiendo info para mostrarla en pantalla. Pues no se cómo hacer que una clase Conection.java que gestiona el socket (logica pura) vaya cambiando los elementos del MainActivity. Algo tan simple como tener un circulo rojo o verde en la pantalla y que cambie de color segun la clase Contection.java detecte si el socket esta abierto/activo.
¿Que es, de todo lo que he mencionado (excluyendo la madre del cordero), lo que necesito para hacer esto de una manera sencilla?
Gracias!
AsyncTasks
1.- Las conexiones de datos no se pueden hacer en el hilo principal de la interfaz gráfica: se tienen que hacer en un hilo separado o podrías bloquear la interfaz gráfica.
2.- Desde los hilos separados no puedes modificar los elementos de la interfaz gráfica
3.- Solución: utilizar AsyncTasks, que tienen algunos métodos que se ejecutan en un hilo separado y otros que se ejecutan en el hilo de la interfaz: http://developer.android.com/reference/android/os/AsyncTask.html
Por cierto, esto no es cosa de Android, pasa en todos los sistemas gráficos: Windows, Linux, Wiz...
crossmax
20/09/2013, 15:05
Gracias!
Mis entornos gráficos han sido tan rudimentarios que estas cosas son nuevas para mi.
El AsyncTask pensaba que era una forma equivalente de extender una clase a Runnable para lanzar un hilo con run(). Por eso lo hice de esta segunda forma que me resultaba "un poquito" más conocida. Pero si por lo que me dices es mucho más que eso, tiraré a mirar por ahí a ver si voy avanzando.
Muchas gracias
^MiSaTo^
20/09/2013, 15:34
Gracias!
Mis entornos gráficos han sido tan rudimentarios que estas cosas son nuevas para mi.
El AsyncTask pensaba que era una forma equivalente de extender una clase a Runnable para lanzar un hilo con run(). Por eso lo hice de esta segunda forma que me resultaba "un poquito" más conocida. Pero si por lo que me dices es mucho más que eso, tiraré a mirar por ahí a ver si voy avanzando.
Muchas gracias
La UI siempre va en foreground y tienes que evitar bloquearla porque un usuario no quiere ver como la app se queda congelada mientras haces X (a no ser que sea requisito indispensable, y en ese caso deberías al menos mostrar un spinner o algo indicando que debes esperar).
AsyncTask es una clase runnable (aka un thread) que se ejecuta en background, por lo que es ideal para ese tipo de cosas. Cualquier operación "pesada" o que va a tardar debe ir en background.
Por experiencia también te recomiendo que no metas más de 3 threads (contando con la UI) porque Android se vuelve loco con ello.
crossmax
24/09/2013, 11:01
Os agradezco la ayuda. Ya me he puesto apelearme con el AsyncTask a ver si me voy apañando.
Una pregunta facilita sobre el socket. El metodo isConnected() deberia devolverme true solo si hay alguien escuchando al otro lado, no?
Es que si creo el socket con sck = new Socket(IP,PORT) y hago un sck.isConnected() siempre me da true, aunque no haya abierto el otro extremo.
Si esto es así, ¿alguien me podría decir porque mi logica me dice lo contrario??
Muchas gracias!
EDIT:
Tengo otro problemilla de concepto :(
Si mi aplicacion tengo que "dibujarla" en el mainactivity, y dependo de lo que vaya recibiendo del socket que ahora esta en el Asynctask, debo crear un bucle "infinito" que vaya tratando los envios/recepciones, pero no se donde crearlo para que dependiendo de estas rx/tx vaya dibujando la pantalla...
Mas perdido que un pulpo en un garaje :loco:
Si al otro lado ni siquiera está abierto, no deberías ni llegar al isConnected(), en el constructor ya salta una excepción "connection refused". ¿Quizá puedas poner el código por aquí y vemos dónde falla?
Sobre el bucle infinito, haces algo así en el AsyncTask:
public Object doInBackground(Object... params){
// Esto se ejecuta en un hilo aparte
Socket s = new Socket(......);
while(true) { // bucle "infinito"
int numBytes = s.receive(data); // recibimos datos
publishProgress("Recibidos " + numBytes); // publicamos el progreso
if (numBytes == 0) { break; }
.....
}
return "Ya está"; // acabamos el método
}
public void onProgressUpdate(Object status){
// esto se ejecuta cuando encuentre un publishProgress() dentro de doInBackground(), en el hilo de la interfaz
TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(status.toString());
}
public void onPostExecute(Object status){
// Esto se ejecuta cuando acabe el doInBackground(), en el hilo de la interfaz
TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(status.toString());
}
El findViewById() es el del Activity, así que tiene que ser accesible por el AsyncTask. Tú verás cómo: le pasas el activity al constructor del AsyncTask, haces el AsyncTask interno al Activity...
^MiSaTo^
24/09/2013, 15:55
Os agradezco la ayuda. Ya me he puesto apelearme con el AsyncTask a ver si me voy apañando.
Una pregunta facilita sobre el socket. El metodo isConnected() deberia devolverme true solo si hay alguien escuchando al otro lado, no?
Es que si creo el socket con sck = new Socket(IP,PORT) y hago un sck.isConnected() siempre me da true, aunque no haya abierto el otro extremo.
Si esto es así, ¿alguien me podría decir porque mi logica me dice lo contrario??
Muchas gracias!
EDIT:
Tengo otro problemilla de concepto :(
Si mi aplicacion tengo que "dibujarla" en el mainactivity, y dependo de lo que vaya recibiendo del socket que ahora esta en el Asynctask, debo crear un bucle "infinito" que vaya tratando los envios/recepciones, pero no se donde crearlo para que dependiendo de estas rx/tx vaya dibujando la pantalla...
Mas perdido que un pulpo en un garaje :loco:
Ahora mismo aquí no tengo mis apps de Android, pero precisamente, el droidChat (que tengo publicada una alpha) es un cliente de IRC que usa sockets y es precisamente lo que tú quieres hacer. Puedo echarle mañana un ojo porque no recuerdo el código. Lo que sí recuerdo, es que el método isConnected me pasaba como a ti (es posible que sea un bug o que haya que mirar algo más, o no se, no recuerdo cómo lo solucioné)
Y posiblemente necesites un Handler, para tratar todas las respuestas del socket. Hoy no voy a estar en casa, pero mañana si quieres le echo un ojo a todo el código y te digo :)
Si al otro lado ni siquiera está abierto, no deberías ni llegar al isConnected(), en el constructor ya salta una excepción "connection refused". ¿Quizá puedas poner el código por aquí y vemos dónde falla?
Sobre el bucle infinito, haces algo así en el AsyncTask:
public Object doInBackground(Object... params){
// Esto se ejecuta en un hilo aparte
Socket s = new Socket(......);
while(true) { // bucle "infinito"
int numBytes = s.receive(data); // recibimos datos
publishProgress("Recibidos " + numBytes); // publicamos el progreso
if (numBytes == 0) { break; }
.....
}
return "Ya está"; // acabamos el método
}
public void onProgressUpdate(Object status){
// esto se ejecuta cuando encuentre un publishProgress() dentro de doInBackground(), en el hilo de la interfaz
TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(status.toString());
}
public void onPostExecute(Object status){
// Esto se ejecuta cuando acabe el doInBackground(), en el hilo de la interfaz
TextView tv = (TextView) findViewById(R.id.textView1);
tv.setText(status.toString());
}
El findViewById() es el del Activity, así que tiene que ser accesible por el AsyncTask. Tú verás cómo: le pasas el activity al constructor del AsyncTask, haces el AsyncTask interno al Activity...
No necesitas devolver nada en el doInBackground. Puedes declarar el AsynTask como <Void, Void, Void> ;)
Aparte, NO pongas nunca nunca nunca nunca tildes en el código. Ni siquiera en comentarios. En serio, a veces Java se le va la olla y te genera un bytecode corrupto. Lo he visto muuuchas veces con código android de gente española. No, no es buena práctica y sí da problemas (al menos en Android)
Yo soy partidaria de tener variables privadas para usar las vistas después porque el findViewById consume bastantes recursos así que es mejor no usarlo más que una vez. (Posiblemente te interese en el onResume más que en el onCreate).
Sobre el AsyncTask, yo no le pasaría la vista al constructor, ya que es buena práctica no hacer un constructor con parámetros (aunque por poderse se puede, pero Google no lo recomienda tampoco). Lo puedes pasar en el execute que para eso admite parámetros ;) O bien crear una clase privada del activity (aunque dependerá mucho de lo que vaya a hacer, a mi me gusta separar lo más posible las clases por legibilidad).
crossmax
24/09/2013, 20:15
Esta claro que cada uno tiene sus metodos. Y muchas veces tampoco hay que complicarse para hecer eficiente una mierdilla de aplicacion.
El caso es que a mi tambien me gusta tener el codigo repartido en clases, no sé si por ser ordenado o por ser novato, pero eso de ver clases dentro de otras me comlican un poco el ver el codigo claro. Ademas, como estoy empezando con la aplicacion, prefiero empezar ordenando que ya habra tiempo para desordenarlo.
Sobre pasar activitys al hilo, si me soluciona el problema podría tirar por alli, porque como digo estoy un poco cansado de no avanzar casi :(
Supongo que la solucion a esto, y segun dice Misato, es tener en la clase del hilo variables privadas y despues en el activity ir consultarlas con sus get() para ir pintando según éstos.
Lo que si me gustaria es saber si me tengo que meter con Handlers o no. Basicamente la aplicacion va a ser un interfaz grafico de una maquina industrial (un simulador en realidad) a la que se conectará por el socket para ir enviando y recibiendo informacion, y con ella construir los elementos en la pantalla. Por ello, de momento, tengo la clase Activity y una clase Conexion que extiende de AsyncTask que es la encargada de crear el socket y ir enviando y recibiendo comandos. Todavia no le he metido el while(true), pero precisamente por ser infinito ¿como voy pasandole al activity lo que va recibiendo si AsyncTask no devuelve los datos hasta que termina?
Supongo que me estoy liando de mala manera, y que es dificil seguidme los post, pero de verdad os agradezco el tiempo que os habeis tomado para intentar entenderme, escribir el codigo, buscarlo...
Muchas gracias
¿como voy pasandole al activity lo que va recibiendo si AsyncTask no devuelve los datos hasta que termina?
¿Hola? ¿Me tienes en ignorados? :D
Con una llamada a publishProgress() dentro del bucle que está en doInBackground. Esta llamada avisa al AsyncTask para que llame a onProgressUpdate() dentro del hilo de interfaz y allí podrás cambiar lo que quieras. El follón es que para cambiar lo que quieras, tendrás que tener una referencia al Activity dentro del AsynTask. Ahí es donde demostrarás tu arte, aunque lo más sencillo para empezar es meter el AsynTask como clase interna del Activity. Te he puesto antes un ejemplillo que muestra cuántos bytes se ha descargado en cada momento, obviamente dentro del bucle infinito.
(aunque un buen programador debería tener en cuanto todo lo que dice Misato)
crossmax
25/09/2013, 19:21
¿Hola? ¿Me tienes en ignorados? :D
Con una llamada a publishProgress() dentro del bucle que está en doInBackground. Esta llamada avisa al AsyncTask para que llame a onProgressUpdate() dentro del hilo de interfaz y allí podrás cambiar lo que quieras. El follón es que para cambiar lo que quieras, tendrás que tener una referencia al Activity dentro del AsynTask. Ahí es donde demostrarás tu arte, aunque lo más sencillo para empezar es meter el AsynTask como clase interna del Activity. Te he puesto antes un ejemplillo que muestra cuántos bytes se ha descargado en cada momento, obviamente dentro del bucle infinito.
(aunque un buen programador debería tener en cuanto todo lo que dice Misato)
Jejeje, no te ignoro, es que con tanto por donde mirar estaba un poco perdido.
Para ir probando, y no sentirme tan inutil, he pasado la activity como parametro del execute y por fin veo como cambian mis imageviews y textviews si existe conexion!!! :) Por lo que me comentais es la forma "mas correcta" de hacerlo si no quiero juntar clases en el mismo .java
En lo que sigo dudando es si tirar ya con Handlers o me vale con AsyncTask. Misato decía que lo mismo necesitaba Handler. Con lo que he contado sobre la aplicacion que quiero crear, ¿tu como lo ves?
Una vez mas, muchas gracias por tu paciencia y tu tiempo
Si te je entendido bien, en una aplicación sencilla no necesitarás handlers, con el asynctask tendrás suficiente. De todas maneras te recomendaría empezar con el asynctask, y luego si ves que se complica, separar en bucle principal en un handler. Pero en principio no lo veo necesario.
^MiSaTo^
25/09/2013, 19:38
Aquí tengo el código (muy antiguo y muy mierdoso por lo que veo xDD) de una primera versión de mi cliente de IRC. Uso 3 clases:
* Chat que es mi Activity principal y la que instancia
* ChatSocket que es un socket normal y corriente de Java. Recuerdo que usé eso porque el AsyncTask está pensado para hacer X cosa en background pero es mejor usar un socket normal si es algo persistente (Como lo es en mi caso, que el socket estará siempre abierto).
* ChatHandler que es la que se encarga de conectar la UI y el socket. Es decir, si recibo algo por el socket, el handler lo envía a la UI y viceversa, cuando escribo algo en la UI el handler lo manda al socket.
Si te soy sincera veo el código y me parece una mierder xD Esto tiene ya como 2-3 años y no se si habría una manera mejor de hacerlo (seguramente sí).
Pero lo mismo te sirve de algo porque como te digo es exactamente lo que tú quieres hacer (solo que lo mío es un cliente de IRC y tú usarás el socket para otra cosa).
Espero que te ayude, te lo adjunto al mensaje y perdona si el código está echo una pena. Si tienes dudas pregunta!
PD: a las asyncTask, si es lo que vas a usar, mejor no pasarle el activity. En java no hay punteros, por lo que estás creando una copia de TODA tu Activity cuando se la pasas y eso es pesado. Lo mismo os da igual el rendimiento, pero en móviles hay que ser cuidadoso con esas cosas :)
PD: a las asyncTask, si es lo que vas a usar, mejor no pasarle el activity. En java no hay punteros, por lo que estás creando una copia de TODA tu Activity cuando se la pasas y eso es pesado. Lo mismo os da igual el rendimiento, pero en móviles hay que ser cuidadoso con esas cosas :)
Hace un clone la AsyncTask? Insinuas que al pasar un objeto como parametro a un metodo java se esta creando una copia de todo el objeto? O me he perdido algo? xD
crossmax
30/09/2013, 13:51
Hola!
Muchas gracias Misato por tu codigo, me he hecho otro proyecto en eclipse con tu handler y estoy tirando por ahí, la verdad es que la logica del codigo es la misma y me basta con darle a run :lamer:
El caso es que tambien tengo que hacer el lado del servidor en linux y ambas partes no acaban de entenderse. Parece como que el socket se queda abierto, sin terminar de enviar, por lo que ni uno acaba de enviar ni el otro de recibir.
He realizado pruebas por separado:
- Server: mi codigo C en linux -> Cliente:socket workbench y funciona OK
- Server: nc desde consola linux -> Cliente: mi app android y funciona OK
- Server: socket workbench -> Cliente: mi app android y NO funciona
- Server: mi codigo C en linux -> Cliente: mi app android y NO funciona
La app android envia el primer comando, y el servidor lo recibe en cualquiera de mis pruebas. Despues el servidor envia la respuesta, pero no llega a la app android, que se queda indefinidamente en el readline(). Si cierro el servidor justo despues de esto, parece como que se libera el socket y la app si que acaba recibiendo la respuesta del servidor.
El problema es que no sé que cambiar, si ambas partes funcionan por separado.
Os pego el codigo de cada cosa para que me ayudéis si podeis.
Server:
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
//accept connection from an incoming client
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
puts("Connection accepted");
//Receive a message from client
while( kbhit()==0 && (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
{
//Send the message back to client
//write(client_sock , client_message , strlen(client_message));
if(memcmp(client_message,"HELO",4)==0) {
printf("Respondo ACK\n");
lng=5;
memcpy(client_message,"ACK/r",lng);
}
else if(memcmp(client_message,"INIT",4)==0) {
printf("Respondo con la info\n");
lng=39;
memcpy(client_message,"INIT:1:00001:001:01:0001:0030:00200:0\n",lng);
}
if(kbhit()!=0) {
puts("Cerrando socket");
lng=6;
memcpy(client_message,"QUIT/r",lng);
read_size=0;
}
write(client_sock , client_message , lng);
}
if(read_size == 0 || kbhit()!=0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
return 0;
Y el cliente android:
while (socket.isConnected()){
String reply = "";
if (input.ready()) {
reply = input.readline();
Log.d(HMI.LOG_TAG,"[SERVER]"+ reply);
if(lastMsgSent.equals("HELO") && reply.startsWith("ACK", 0) == false)
sendCommand(lastMsgSent+EOF); //Volvemos a intentar conexion
else if (reply.startsWith("ACK")){
//El servidor nos esta escuchando
lastMsgSent = "INIT";
sendCommand(lastMsgSent+EOF); //Pedimos datos servicio
}
else {
sendMessageToHandler(reply); //Pasamos el mensaje recibido al manejador
}
}
}
Siempre un flush() después de un write() cuando estés escribiendo un protocolo que espere respuesta :)
Si hay un buffer por medio (y en las conexiones TCP suele haberlo) los datos no se van a enviar cuando tú quieras sino cuando el sistema operativo lo considere necesario. Por ejemplo, (1) cuando se llene el buffer o (2) cuando hayan pasado un tiempo prudente o (3) cuando se cierra la conexión, con lo que se aprovecha para vaciar el buffer o (4) cuando el programador ha dicho "déjate de historias y envíalo ya, cojones" con un flush()
Así que flush() después del write(), tanto en Java como en C, para asegurarte de que los datos se envían ya esté como esté el buffer.
^MiSaTo^
30/09/2013, 14:07
Venía a decir lo mismo que juan, que eso es tema del buffer fijo y que hicieras un flush xD
Me alegro que te haya servido mi código. Como digo tiene 2-3 años y está echo una mierda
crossmax
30/09/2013, 18:27
Llmarme paquete, que lo soy incluso antes de este mensaje, pero por mucho flush y mucho TCP_NODELAY que le meto al servidor en linux no hay manera.
Como visteis en el codigo, el cliente android si tiene el flush (Misato lo sabrá xD). Ahora le he metido un fflush al codigo C e incluso unas prioridades y retardos que he leido por internet al buscar "flush en linux". Quitando lo que sigue igual, me ha quedado:
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
FILE *f = fdopen(socket_desc, "w+");
....
int flag = 1;
setsockopt(client_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
send(client_sock , client_message , lng , flag);
fflush(f);
Y ni con esas.
Lo cierto es que he leido que el flush en linux no se debe hacer. De ahí que se tenga que hacer el apaño del puntero al descriptor del socket. Lo que me extraña es que los 2 esteis tan seguros del flush :loco:
El flush() en Linux claro que se puede hacer, cuando se necesite y en tu caso lo necesitas :) No vas a esperar que se llene una ventana TCP, necesitas enviar los 39 bytes de tu paquete ya pero ya. flush(), que está para precisamente eso. Otra cosa es que no se llame flush() reconozco que hace mucho que no programo rutinas de red en C así que puede que se haga como has puesto, abriendo un fichero con el socket y fflush() al fichero, o puede que se haga con el TCP_NODELAY.
Confieso que no recuerdo así que no sé bien qué te está pasando... Probemos, palos de ciego. Mira justo en este código:
else if(memcmp(client_message,"INIT",4)==0) {
printf("Respondo con la info\n");
lng=39;
memcpy(client_message,"INIT:1:00001:001:01:0001:0030:00200:0\n",lng);
}
¿Dónde declaras o reservas espacio para client_message? ¿Seguro que puede meterle 39 bytes sin problemas, o te estás cargando tu propio programa?
crossmax
03/10/2013, 09:48
Buenas de nuevo!
Tengo una buena noticia!! Mas para mi que para vosotros :D
Por fin parece que todo funciona como deberia!!! El problema era el final de linea que mandaba desde el servidor.
Al ver que mi server en linux funcionaba con Socket Workbrench me empecé a liar con mil cosas y no acabé de probar todas las opciones de final de linea/fichero, en linux, en windows... y todas esas cacufas.
Asique ya me he metido de lleno a mostrar pantallitas y demas historias que es lo que anima!!
Mil gracias una vez mas!!! No se que haria sin vosotros!
Por cierto, especifico la solucion, por si alguien cae por aqui con lo mismo.
Le he metido al final de la cadena en el envio desde el servidor hecho en C y en linux los caracteres "\n\r". De esa forma, el BufferReader de mi cliente en android no se queda esperando indefinidamenet al leer la linea con bufferreader.readline()
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions Inc. All rights reserved.