• por admin - 19 de diciembre de 2005 - Etiquetado como , ,
    11 comentarios »

    Recientemente nos hemos visto sumergidos en el desarrollo de aplicaciones para dispositivos móviles con J2ME, y tras algunos dolores de cabeza hemos salido a flote. Y digo esto porque no esperábamos la cantidad de problemas con los que nos íbamos a encontrar y la escasa documentación de los infinitos bugs que existe en Internet. Como no quiero que al siguiente le pase lo mismo que a mí, que a veces parecía que estaba jugando al http://www.googlewhack.com/, pienso compartir parte de las cosas que he descubierto en algunos posts en el blog. Y digo parte porque me debo a la confidencialidad con el cliente. Y la primera en la frente. PORQUE EL con.openInputStream COMO APARECE EN TODOS LOS LIBROS Y TUTORIALES NO FUNCIONA Pues eso, que te compras un libro sobre j2me, lo estudias y tan panchito te pones a crearte tu primer midlet con conexiones a servidor web. Para ello y como aparece en Toooodos los libros y tutoriales te decides a poner:

    HttpConnection con = (HttpConnection) Connector.open(url, Connector.READ, true);
    con.setRequestMethod(HttpConnection.GET);
    InputStream in = con.openInputStream();
    Bonito verdad? Pues bien, te vas a encontrar que en GRAN cantidad de móviles NO FUNCIONA. Así de claro y de alto. Y diría que no es simplemente un problema de bugs, sino de la propia especificación MIDP2.0, y me explico: Lo primero que debes saber es que en algunos libros y/o tutoriales te recomiendan que resulta bonito crear un thread de peticiones por red, para que la pantalla siga respondiendo a los comandos del usuario. Bien, no es que sea bonito, es que es TOTAL Y ABSOLUTAMENTE NECESARIO. La mayoría de los teléfonos suelen pedir permiso al usuario para usar el “airtime” (o tiempo de conexión por red, y menos mal que lo hacen dadas las tarifas de los operadores ;-)); eso suele suceder cuando realizamos la llamada Connector.open(), osea cuando ya hemos empezado a realizar la petición, o sea el control del programa está en esa llamada NO ATENDIENDO a la pantalla. Con lo cual cuando te pregunte, si no has creado un thread paralelo al thread que atiende los comandos, para las peticiones de red, tu usuario se quedará mirando esta pantalla hasta que se aburra, sin posibilidad de hacer nada más. ¿No es muy buena “experiencia de usuario”, no? ;-)

    Usar otro thread para las peticiones de red no es una opción, sino una obligación

    Bien pues aprendido esto, cambiamos nuestro código, y las cosas “parece” que empiezan a funcionar ;-):

    // En algun lugar de tu midlet NetRequest request = new NetRequest(); request.start(); ...

    public class NetRequest extends Thread{ .... HttpConnection con = (HttpConnection) Connector.open(url, Connector.READ, true); con.setRequestMethod(HttpConnection.GET); InputStream in = con.openInputStream();

    Y digo “parece”, porque efectivamente (si usas un emulador, benditos sean que siguen viviendo en el país del Edén) abres tu conexión, y obtienes el InputStream que querías, y lo puedes usar en que te de la gana. A no ser que seas un blade runner y te decidas a hacer los tests que en Linking Paths hicimos. En un momento dado, desde el emulador (el WTK de Sun), desconectábamos el equipo de la red. Alguno pensará, ¿para qué? Hombre pues teniendo en cuenta la cobertura de los móviles se trata de una situación bastante probable ¿no?; pero es que además como veremos en algunos teléfonos, existen algunos divertidos bugs en su implementación de openInputStream().

    Bueno, pues si acertábamos en el momento clave (indicado abajo), EL THREAD DE RED SE QUEDABA BLOQUEADO EN EL MÉTODO openInputStream(), sin saltar ningún tipo de Excepción (ni la IOEXception, ni la OyesQueMeHeQuedadoSinConexionException):

    HttpConnection con = (HttpConnection) Connector.open(url, Connector.READ, true);
    con.setRequestMethod(HttpConnection.GET);

    // Aqui la conexión de red se va a hacer gargaras InputStream in = con.openInputStream();

    Glups!, Se quedaba bloqueado para siempre, jamás de los jamases. ¿Pero y los temporizadores de red que a su bien tuvieron enseñarme en la Escuela de Ingenieros? ¿No saltan? Pues no, NO SALTA NINGÚN TIPO DE TEMPORIZACIÓN DE RED, y eso aunque pongas Connector.open(url, Connector.READ, true). Para ver realmente donde está el problema debemos ir a la especificación MIDP2.0, que en su API, respecto a Connector:

    “An optional third parameter is a boolean flag that indicates if the calling code can handle timeout exceptions. If this flag is set, the protocol implementation may throw an InterruptedIOException when it detects a timeout condition. This flag is only a hint to the protocol handler, and it does not guarantee that such exceptions will actually be thrown. If this parameter is not set, no timeout exceptions will be thrown.”

    Por resumir: si le dices a un fabricante de móviles que puede hacerlo o no, y que no está obligado, te apuesto lo que quieras a que no lo hará. Y en todos los móviles que lo hemos probado (unos 10) ocurre que NO EXISTEN TEMPORIZADORES DE RED PARA HTTP. Y aunque la especificación no diga nada sobre este método opnInputStream(), también ocurre lo mismo con inputStream.read(), y si miramos la especificación: “This method blocks until input data is available, the end of the stream is detected, or an exception is thrown.” Con lo cual sospechamos que esa actitud blockeante es inherente a los InputStream. Con lo cual tenemos una paradoja muy interesante:

    Si se cierra la conexión al abrir o al leer de un InputStream, por cualquier causa, no se lanza ninguna excepción, y como no existen temporizadores, el bloqueo del thread es permanente

    Y no es que ocurra con algún móvil arcaico, ocurre en todas las gamas modernas, incluso en el emulador de móviles MIDP2 de Sun (el WTK). Podéis comprobarlo, dando por ejemplo un sleep de varios segundos para que os de tiempo a cerrar la conexión en el momento justo. Ahh, se me olvidaba, si alguien pretende solucionar el bloqueo en el read() llamando antes al método avalaible(), que se olvide. Es un método que siempre devuelve cero ;-) (¿Verdad que te empiezan a gustar los estándares?;-))

    Para nuestro cliente hemos encontrado ciertos “work-arounds” para que la aplicación no se quedase parada en ese thread, pero lamentablemente es confidencial. Sólamente comentaré varias cosas:

  • Como el método stop() de los threads ya está deprecado (y no es para nada recomendable), sólamente un thread se puede matar a si mismo. Eso en la práctica se consigue con algún tipo de booleano en el run, que se pone a false cuando se quiere parar la ejecución. Si el thread está en un bucle, podemos parar su ejecución controlando el valor de esa variable. Lamentablemente cuando estamos en plena llamada openInputStream (que en algunos móviles puede tardar varios segundos), no estamos en un bucle, y si se produce el problema ahí, esta técnica no funciona
  • Existen algunos móviles (como el Samsung SGH-D500) que simplemente se bloquean cuando intentas hacer una conexión a url distintas: referencia , Verdad que es divertido? Para esos casos, hemos descubierto que intentando de nuevo las conexiones, el bloqueo del primer thread desaparece, obteniendo ahora dos conexiones, la antiguamente bloqueada y la nueva
  • Llamar a connection.close() desde otro thread paralelo, a veces funciona (haciendo que salte una IOException), y a veces no (Simplemente no pasando nada, y quedando el thread bloqueado)
  • Muchos teléfonos tienen un límite máximo en el número de conexiones y de threads que pueden existir en un momento determinado, así que el problema puede ser aún mayor
  • La verdadera solución vendría porque en la próxima especificación de MIDP, se obligara realmente a los fabricantes a crear temporizadores de red (como es lógico), o que los métodos openInputStream o read realmente recibieran IOEXception, que no lo reciben. Pero la verdad no sé quien está metido en esa especificación, porque este tipo de errores nos dejan perplejos. Pues nada, a disfrutar de j2me, y a rezar porque ningún móvil pierda la conexión de red o tengáis que hacer un midlet de red para los Samsung. Eso o contratar a LP para dar solución ;-)

11 comentarios para “J2ME: Lo que no cuentan los libros ni los tutoriales o Cosas que nunca te dije”

  1. xelha dice:

    Hola, primero gracias por tu excelente aportación!!! tengo un apregunta no se si puedas ayudarme!!

    Estoy atorado con pos permisos para escribir y leer archivos con el fileConnection, creo que es por que tengo que firmar el midlet!! sabes de algun texto en el cual pudiera guiarme???

    gracias

  2. javier dice:

    hola amigos estoy en busca de codigo uente para efectuar una llamada desde un movil con el j2me por favor se l0os agradeceria si me lo pasan de eso depende mi proyecto

  3. Windzor dice:

    Hola, queria acerles una consulta no se si pueden mandarme algun ejemplo de conexion con un servidor web que funcione ya que he probado con lo de los libros y no funciona, practicamente lo que quiero es que desde una aplicacion en el clular pueda mandar informacion hacia una pagina web donde pueda procesar dicha informacion

    gracias espero me ayuden Saludos :)

  4. Alfonso dice:

    Buenas

    llevo trabajando profesionalmente con j2me y con windows mobile unos 3 años, y elaborando algún paper para nokia y colaborando con varias casas hace tiempo, de telefonía claro; Lo que dices es totalmente cierto, yo de hecho podría escribir varios libros sobre ‘lo que no se cuenta’ al implementar aplicaciones en dispositivos embebidos….un ejemplo más claro que las comunicaciones….the java heap , eso si que es complicado de explicar como el tema basado en el jsr-82…..mi consejo : no leáis libros, sólo os orientarán y en la práctica sirven de muy poco creedme; yo me he chupado muchos proyectos j2me y para hacer algunas cosas, ni en google, ni en ningún libro. Saludos

  5. Pedro dice:

    Ahora que ya ha pasado un tiempo desde este post. ¿podrías comentar como conseguisteis que la aplicación no se quedase parada en ese thread?

  6. Ger dice:

    Tienes toooda la razon llevo unos cuantos dias tratando de darle solucion al bloqueo y nomas no, voy a probar la solucion que posteaste pero independiente de eso seria bueno que pudieras postear un ejemplo basico de conexion midlet con un servidor web.

    Saludos

  7. Angeles dice:

    Me he sentido terroríficamente identificada con cada uno de los problemas que habéis descrito. He tenido tan mala suerte que he pasado por cada uno de ellos, y no solo respecto a Http connections sino también en cuanto a StreamConnection.

    No sé cómo lograríais solucionarlo, pero mis soluciones han pasado por parches y soluciones muy de andar por casa.

    Desde luego, cada vez que daba con un bug así, me costaba asumir que no fuera culpa mia. Parece increible.

  8. Jesus Chuda Contreras dice:

    saludos e leido su articulo de J2ME, en estos momentos me encuentro realizando un proyecto de envio y recepcion de sms via moden gsm y megustaria saber si tienen algo en lo que puedan ayudarme, gracias de antemano por la atencion prestada, agradeceria cualquier ayuda.

  9. Cesar Diaz dice:

    Hola eh estado leyendo el foro y me parece muy interesante, apenas comienzo con Java y ahora tengo la tearea de consumir un web service desde una BlackBerry, estoy utilizando el plugin de elicpse para BlackBerry y por mas que hago ejemplos encontrado en la web no consigo nada, eh leido bastante informacion pero solo logro confundirme. En una pagina me decia que lo primero que se deberia hacer para consumir un WEbService era crear un Stub con J2Me Wirless Toolkit, y despues de ello realizar el codigo necesario, pero que es un Stub. otras me decian que solo bastaba con abrir el link donde se muestra el WSLD del WebSer… ya sea con HttpConnection o streamConnection, y enseguida solo tenias que mandar llamar los metodos contenidos o descritos en dicho WSDL pero, no eh podido.

    Mi duda es en primera lugar como conectarme al ServicioWeb, enseguido como le puedo mandar los datos que quiero que se procesen y como recupero la respuesta del WS. Por el moemnto tengo un webService creado en JDeveloper 10 g de Oracle, este WS esta en localhost, y por mas que intento no logro conectarme con el, alaguien tendra algún ejemplo o un buen tutorial o link donde pueda guiarme, se los agradecere bastante. Mi correo es cesarloc@hotmail.com

    De antemando gracias por la ayuda que púdieran brindarme y hasta pronto.

  10. jesus dice:

    Hola!!!

    Soy nuevo en esto y quiero consumir una web service, ya tengo el codigo y al ejecutarlo me sale lo que se menciono arriba lo de “airtime” que puedo hacer??

  11. [...] articulo se titula J2ME: Lo que no cuentan los libros ni los tutoriales o Cosas que nunca te dije y es una buena referencia de lo que se van a encontrar cuando intenten comenzar a programar en [...]

Linked, el blog de Linking Paths