Spanish [SOLUCINADO] Crear rutina comun para conexion JSON

Edu Portu

Member
Licensed User
Buenos dias,
Tengo una APP que hace bastantes consultas JSON (que devuelven un solo registro), por lo que para economizar codigo queria hacer una rutina que le pase un MAP con los parametros y me devuelva otro MAP con el resultado.
La rutina es la siguiente (la he creado siguiendo los tutoriales de Erel):

RUTINA JSON:
Sub GenerarConexionJSON1(MapaDatos As Map, Modulo as Activity) As ResumableSub
    Dim ResultadoMap As Map

    ResultadoMap.Initialize
    'Comprobamos si se puede escribir en la memoria externa
    If Not(File.ExternalWritable) Then
        ToastMessageShow("Compruebe conexión.", True)
    Else
        'Comprobamos si tenemos conexion a internet
        Dim server As ServerSocket 'Add a reference to the network library
        server.Initialize(0, "")
        If server.GetMyIP = "127.0.0.1" Then  'this is the local host address
            ToastMessageShow("Compruebe conexión.", True)
        Else
            'Accedemos a la base de datos para coger los datos
            Dim job As HttpJob
       
            Dim JSONGenerator2 As JSONGenerator
            JSONGenerator2.Initialize(MapaDatos)
               Dim JSONstring As String
            JSONstring = JSONGenerator2.ToString
         
            job.Initialize("", Modulo)
            job.PostString("http://www.xxxxx.com/json/yyyyyy.php", JSONstring)
               
            Wait For (job) JobDone(job As HttpJob)
           
            If job.Success Then
                DoEvents
                Dim Resultado As String = job.GetString
                If Resultado<>"" Then
                    Dim parser As JSONParser
                    parser.Initialize(Resultado)
                    Dim DATOS As List
                    DATOS = parser.NextArray 'returns a list with maps
                    If DATOS.Size = 1 Then 'Solo tiene que haber un registro con esa ID
                        ResultadoMap = DATOS.Get(0)
                    End If
                End If
            Else
                ToastMessageShow("Error: " & job.ErrorMessage, True)
            End If
            job.Release
        End If
    End If
    Return ResultadoMap
End Sub
Pues bien la rutina funciona perfectamente mientras estaba en el mismo modulo que se usaba, pero a la hora de meterla en un MODULO DE CODIGO, para poder llamarla desde todos los modulos me da error en la linea 26:

ERROR LINEA 26:
Error al compilar el programa.
Descripción del error: Los módulos estáticos no pueden manejar eventos.
Ha ocurrido un error en la línea: 83
Wait For (job) JobDone(job As HttpJob)
Word: wait for
Alguna manera de poder reutilizar esta rutina sin tener que copiarla en todos los modulos del programa? Muchas gracias.
 

Edu Portu

Member
Licensed User
Muchas gracias por la rapida respuesta, he probado a ponerla en el modulo starter y ya no da error esa linea, pero da error en la llamada

ERROR AL LLAMAR A LA RUTINA GENERARCONEXIONJSON1:
Error al compilar el programa.
Descripción del error: Miembro desconocido: generarconexionjson1
Ha ocurrido un error en la línea: 278
Dim rs As ResumableSub = Starter.GenerarConexionJSON1(PeticionMap)
Word: generarconexionjson1
Entiendo que quizas en los MODULOS DE CODIGO todas las rutinas son globales y se pueden llamar desde cualquier sitio y en cambio en este modulo habra que hacer algo para que se pueda acceder a ella?
 

JordiCP

Well-Known Member
Licensed User
Para llamar un Sub de un módulo de servicio desde una Activity, se requiere un CallSub (en este caso, un CallSub2). Sin embargo, a partir de B4A 9.80 también se puede llamar directamente a los Subs del módulo Starter (no lo he probado, tengo 9.50 y todavía no he actualizado a la última).

Lo que desconozco es si (sea con CallSub o llamando directamente a 'Starter.GenerarConexionJSON1(..)' si tienes B4A 9.80) funcionará el Wait For, por el hecho de estarlo llamando desde otro módulo.
 

JordiCP

Well-Known Member
Licensed User
Los modulos de código no pueden manejar eventos. La rutina 'GenerarConexionJSON1' incluye eventos ("Complete"). Por tanto se debe poner en un servicio o Activity, y el único que es visible desde todos los módulos es el Starter.

B4X:
    Wait for (CallSub2(Starter, "GenerarConexionJSON1", PeticionMap)) Complete(res As Map)
o también, a partir de B4A 9.8 (supongo)
B4X:
    Wait for ( Starter.GenerarConexionJSON1(PeticionMap) ) Complete(res As Map)
 
Last edited:

JordiCP

Well-Known Member
Licensed User
De hecho sí, aunque se necesitaría declarar e inicializar una instancia de la clase allí donde se use. Pero funcionar, funciona sin problemas :)
La ventaja del módulo Starter es que es visible desde todos sitios, y ya está 'instanciado' al arrancar la app, por lo que se puede acceder sin declarar nada adicional.
 

drgottjr

Active Member
Licensed User
creo que todavía hay que anteponer "starter." delante de cualquier referencia a variantes o subs que residen en starter. al igual que con un módulo de código.
no obstante la "globalidad" de ellos. son accesibles, sí, pero son primos, no hermanos. ¿qué decís?
 

Edu Portu

Member
Licensed User
Lo primero muchas gracias a todos

He probado con la siguiente llamada:

MODULO INFOBUZON SUB CREATE:
    Wait for (CallSub3(Starter, "GenerarConexionJSON1", PeticionMap, Activity)) Complete(ResultadoMap As Map)
Y ya el programa se ejecuta sin errores de compilacion, pero si que da un error en ejecucion en la rutina GenerarConexionJSON1

ERROR:
** Activity (infobuzon) Create, isFirst = true **
** Activity (infobuzon) Resume **
java.lang.RuntimeException: java.lang.ClassCastException: anywheresoftware.b4a.BALayout cannot be cast to java.lang.String
    at anywheresoftware.b4a.keywords.Common$11.run(Common.java:1157)
    at anywheresoftware.b4a.keywords.Common$12.run(Common.java:1168)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:201)
    at android.app.ActivityThread.main(ActivityThread.java:6810)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:547)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:873)
Caused by: java.lang.ClassCastException: anywheresoftware.b4a.BALayout cannot be cast to java.lang.String
    at anywheresoftware.b4a.keywords.Common.getComponentBA(Common.java:1194)
    at anywheresoftware.b4a.keywords.Common$11.run(Common.java:1102)
    ... 8 more
al tracear he visto que cuando se hace en la rutina el :

B4X:
            Wait For (job) JobDone(job As HttpJob)
el proceso vuelve al modulo donde se hizo la llamada "infobuzon -> Activity_Resume" (en el log se ve que entra justo antes del error) sin esperar a que terminen los WAIT FOR y ahi da el error (ese sub esta vacio)

MODULO INFOBUZON:
Sub Activity_Resume

End Sub
 
Last edited:

JordiCP

Well-Known Member
Licensed User
Cuando se hace un Wait For, se devuelve la ejecución al proceso principal, en este caso el Activity. Es decir, el proceso que lo ha llamado esperará que acabe, pero la Activity sigue. Y si lo has llamado desde el Activity_Create, es normal que siga por aquí.

El error viene después. ¿Qué hace tu rutina con el segundo parámetro que le pasas (Activity)?
 

Edu Portu

Member
Licensed User
Lo paso porque hace falta para inicializar el HttpJob?

RUTINA JSON:
Sub GenerarConexionJSON1(MapaDatos As Map, Modulo as Activity) As ResumableSub

....
            Dim job As HttpJob
      
....
        
            job.Initialize("", Modulo)
            job.PostString("http://www.xxxxx.com/json/yyyyyy.php", JSONstring)
              
            Wait For (job) JobDone(job As HttpJob)
          
....
    Return ResultadoMap
End Sub
 

Edu Portu

Member
Licensed User
Claro, cuando estaba la rutina en todos los modulos tenia logica que fuese ese modulo (job.Initialize("", Me)) , pero al estar en STARTER igual no tiene logica que sea el modulo en el que se llama a la rutina sino el propio modulo de la rutina (STARTER):

En el manual ese segundo parametro TARGETMODULO pone que es la actividad o servicio que va a gestionar el evento JOBDONE

Si pongo job.Initialize("", Activity) da error
Si pongo job.Initialize("", Me) da error
Si pongo job.Initialize("", Sender) NO da error pero se queda en el Wait For (job) JobDone(job As HttpJob) y nunca muestra los datos...
 
Last edited:

Edu Portu

Member
Licensed User
Pongo aqui (por si alguien quiere aprovecharlo) como queda la llamada y la rutina:

LLAMADA:
Wait for (CallSub2(Starter, "GenerarConexionJSON1", PeticionMap)) Complete(ResultadoMap As Map)
RUTINA JSON:
Sub GenerarConexionJSON1(MapaDatos As Map) As ResumableSub
    Dim ResultadoMap As Map

    ResultadoMap.Initialize
    'Comprobamos si se puede escribir en la memoria externa
    If Not(File.ExternalWritable) Then
        ToastMessageShow("Compruebe conexión.", True)
    Else
        'Comprobamos si tenemos conexion a internet
        Dim server As ServerSocket 'Add a reference to the network library
        server.Initialize(0, "")
        If server.GetMyIP = "127.0.0.1" Then  'this is the local host address
            ToastMessageShow("Compruebe conexión.", True)
        Else
            'Accedemos a la base de datos para coger los datos
            Dim job As HttpJob
    
            Dim JSONGenerator2 As JSONGenerator
            JSONGenerator2.Initialize(MapaDatos)
               Dim JSONstring As String
            JSONstring = JSONGenerator2.ToString
      
            job.Initialize("", "Starter")
            job.PostString("http://www.xxxxx.com/json/yyyyyy.php", JSONstring)
            
            Wait For (job) JobDone(job As HttpJob)
        
            If job.Success Then
                DoEvents
                Dim Resultado As String = job.GetString
                If Resultado<>"" Then
                    Dim parser As JSONParser
                    parser.Initialize(Resultado)
                    Dim DATOS As List
                    DATOS = parser.NextArray 'returns a list with maps
                    If DATOS.Size = 1 Then 'Solo tiene que haber un registro con esa ID
                        ResultadoMap = DATOS.Get(0)
                    End If
                End If
            Else
                ToastMessageShow("Error: " & job.ErrorMessage, True)
            End If
            job.Release
        End If
    End If
    Return ResultadoMap
End Sub
Muchas gracias a todos/as, y ya puestos, como seria si quisiesemos crear una clase con esta rutina? Quizas quedaria mas limpio y reutilizable?
 
Top