Running mobile apps on an application server
From the mobile device, programs can be started remotely on an application server, and displayed on the device.
Purpose of remote application execution for mobile devices
Server applications can only be started through the Genero Application Server (GAS), by using the UA protocol available since version 3.00. You must set up and configure the GAS for the programs you want to start remotely. See the GAS documentation for more details.
Implementing the embedded mobile app
Create a small application to be deployed on the mobile device, which then starts the application(s) on an GAS server.
The server application is started from the embedded application through the runOnServer
front call. The
embedded mobile application can be a very simple MAIN / END MAIN
program, only
performing the "runOnServer
" front call.
MAIN
CALL ui.interface.frontcall("mobile","runOnServer",
["http://myappserver:6394/ua/r/myapp"],[])
END MAIN
When the remote application starts, the graphical user interface displays on the mobile device.
The runOnServer
front call returns when the called application ends,
control goes back to the initial application executing on the mobile device.
runOnServer
front call. Because starting remote GAS applications is
done with a front call, this configuration mimics an embedded starter app running on
the device. Using the runOnServer front call
The application executed on the server-side is identified by the first parameter of
the runOnServer
front call. This application must be delivered by the Genero
Application Server. The parameter must contain an "ua/r
" URL syntax (the UA
protocol introduced with the GAS 3.00).
For example: http://myappserver:6394/ua/r/myapp
The URL may contain a query string, with parameters for the application to be executed by the GAS.
If needed, you can add a second argument to define a timeout as a number of seconds. The embedded application will wait for the remote application to start, until the timeout expired. If no timeout parameter is specified, or when zero is passed, the timeout is infinite.
TRY/CATCH
block to check if the execution
the server application was successful:
MAIN
TRY
CALL ui.interface.frontcall("mobile","runOnServer",
["http://myappserver:6394/ua/r/myapp"],[])
CATCH
ERROR err_get(STATUS)
END TRY
END MAIN
Subsequent server-side application runs are allowed; the last active application will
display on the device. However, it is not possible to navigate between started applications.
Therefore, an application started with the runOnServer
front call must only use the
RUN
instruction to start sub-programs.
RUN WITHOUT WAITING
is not supported.
Passing parameters to the server application
?Arg=value1&Arg=value1&...
notation:DEFINE params, base, complete_url STRING
LET params = "Arg=verbose&Arg=5677"
LET url = "http://myappserver:6394/ua/r/myapp"
LET complete_url = base || "?" || params
The remote program can retrieve the parameters with the arg_val()
built-in function.
runOnServer
front call.See the GAS documentation (AllowUrlParameters
attribute) about
passing parameters in the application URL.
IMPORT util
MAIN
DEFINE arr DYNAMIC ARRAY OF STRING, x INT
MENU "test"
COMMAND "runOnServer"
CALL arr.clear()
LET arr[1] = "first argument"
LET arr[2] = "second argument"
LET x = do_run("http://10.0.40.29:6394/ua/r/test1", 10, arr)
COMMAND "exit"
EXIT MENU
END MENU
END MAIN
FUNCTION do_run(url,timeout,params)
DEFINE url STRING,
timeout SMALLINT,
params DYNAMIC ARRAY OF STRING
DEFINE i, r INTEGER, tmp STRING
LET r = 0
LET tmp = url
FOR i=1 TO params.getLength()
LET tmp = tmp || IIF(i==1,"?","&") || "Arg=" || params[i]
END FOR
TRY
CALL ui.interface.frontcall("mobile","runOnServer",[tmp,timeout],[])
CATCH
ERROR err_get(STATUS)
LET r = -1
END TRY
RETURN r
END FUNCTION
MAIN
MENU "Prog1"
COMMAND "arg1" MESSAGE "Arg 1 = ", arg_val(1)
COMMAND "arg2" MESSAGE "Arg 2 = ", arg_val(2)
COMMAND "arg3" MESSAGE "Arg 3 = ", arg_val(3)
COMMAND "Quit" EXIT MENU
END MENU
END MAIN
Sharing files between embedded and server app
If files need to be shared between the embedded application and the server
application, the application running on the GAS can only access the
data-directory directory, in the sandbox of the embedded
application that executes the "runOnServer
" front call.
This matters when using file handling APIs such as fgl_putfile()
and fgl_getfile()
or front calls like takePhoto
and launchURL
.
The data-directory on the mobile device can be found with the
feInfo/dataDirectory
front call. In both the
embedded app and the app running on the server, this front call will return the same
directory.
- Before starting the server application with a
runOnServer
front call, the embedded app must copy files to the data-directory. - While executing, the server application can retrieve files from the
data-directory with
fgl_getfile()
, and send its own files to the data-directory, withfgl_putfile()
. - When the server application terminates, the embedded app can read files the server application left in the data-directory.
RUN
instruction, make sure to not overwrite files
written by other server programs.In order to write code for the embedded app, that can be executed in development mode
(running on a server) and on the mobile device, you can adapt to the execution
context: Make a simple file copy when executing on the mobile device, or do an
fgl_putfile()
call, when running on the development server.
Check the execution context with the base.Application.isMobile()
method.
IMPORT os
...
CALL mobile_copy_to_data_dir("myfile.txt")
...
FUNCTION mobile_copy_to_data_dir(fn)
DEFINE fn, dd, dst STRING, r INT
CALL ui.interface.frontcall("standard","feInfo",["dataDirectory"],[dd])
-- Always use / as path sep for Android/iOS dirs.
LET dst = dd || "/" || os.Path.basename(fn)
IF base.Application.isMobile() THEN
-- Executing on device: make a simple copy to data-dir
LET r = os.Path.copy(fn, dst)
MESSAGE SFMT("COPY status = %1", r)
ELSE
-- Executing on dev server: make a file transfer to data-dir
CALL fgl_putfile(fn, dst)
END IF
END FUNCTION
os.Path.join()
method here because it would add
the path separator for the operating system where the application is executed. This
would not be a problem when executing on the mobile device or Unix-like platforms.
However, when running on a Windows® platform, the os.Path.join()
method would join
the directory and the file name with a backslash, and the resulting path would not
fit Android™ or iOS
directory path specification for the data-directory.fgl_getfile()
function, to
transfer a file from the mobile device data-directory to the
local server
disk:IMPORT os
...
CALL server_get_from_data_dir("myfile.txt", "/tmp/server_file.txt")
...
FUNCTION server_get_from_data_dir(fn, dst)
DEFINE fn, dst, dd, src STRING
CALL ui.interface.frontcall("standard","feInfo",["dataDirectory"],[dd])
-- Use / as path sep for Android/iOS dirs!
LET src = dd || "/" || fn
CALL fgl_getfile(src, dst)
END FUNCTION
fgl_putfile()
function, to copy a file from the server application to the
data-directory of the embedded
app:IMPORT os
...
CALL server_put_to_data_dir("/tmp/server_file.txt", "myfile.txt")
...
FUNCTION server_put_to_data_dir(src, fn)
DEFINE src, fn, dd, dst STRING
CALL ui.interface.frontcall("standard","feInfo",["dataDirectory"],[dd])
-- Use / as path sep for Android/iOS dirs!
LET dst = dd || "/" || fn
CALL fgl_putfile(src, dst)
END FUNCTION