AIR application invocation and termination
This section discusses the ways in which an installed Adobe® AIR® application can be invoked, as well as options and considerations for closing a running application.
Note: The NativeApplication, InvokeEvent, and BrowserInvokeEvent objects are only available to SWF content running in the AIR application sandbox. SWF content running in the Flash Player runtime, within the browser or the standalone player (projector), or in an AIR application outside the application sandbox, cannot access these classes.
For a quick explanation and code examples of invoking and terminating AIR applications, see the following quick start articles on the Adobe Developer Connection:
Application invocation
An AIR application is invoked when the user (or the operating system):
Launches the application from the desktop shell.
Uses the application as a command on a command line shell.
Opens a type of file for which the application is the default opening application.
(Mac OS X) clicks the application icon in the dock taskbar (whether or not the application is currently running).
Chooses to launch the application from the installer (either at the end of a new installation process, or after double-clicking the AIR file for an already installed application).
Begins an update of an AIR application when the installed version has signaled that it is handling application updates itself (by including a
<customUpdateUI>true</customUpdateUI>
declaration in the application descriptor file).(iOS) Receives a notification from the Apple Push Notification service (APNs).
Invokes the application via a URL.
Visits a web page hosting a Flash badge or application that calls
com.adobe.air.AIR launchApplication()
method specifying the identifying information for the AIR application. (The application descriptor must also include a<allowBrowserInvocation>true</allowBrowserInvocation>
declaration for browser invocation to succeed.)
Whenever an AIR application is invoked, AIR dispatches an InvokeEvent object of
type invoke
through the singleton NativeApplication object. To allow an
application time to initialize itself and register an event listener, invoke
events are queued instead of discarded. As soon as a listener is registered, all
the queued events are delivered.
Note: When an application is invoked using the browser invocation feature, the
NativeApplication object only dispatches an invoke
event if the application is
not already running.
To receive invoke
events, call the addEventListener()
method of the
NativeApplication object ( NativeApplication.nativeApplication)
. When an event
listener registers for an invoke
event, it also receives all invoke
events
that occurred before the registration. Queued invoke
events are dispatched one
at a time on a short interval after the call to addEventListener()
returns. If
a new invoke
event occurs during this process, it may be dispatched before one
or more of the queued events. This event queuing allows you to handle any
invoke
events that have occurred before your initialization code executes.
Keep in mind that if you add an event listener later in execution (after
application initialization), it will still receive all invoke
events that have
occurred since the application started.
Only one instance of an AIR application is started. When an already running
application is invoked again, AIR dispatches a new invoke
event to the running
instance. It is the responsibility of an AIR application to respond to an
invoke
event and take the appropriate action (such as opening a new document
window).
An InvokeEvent
object contains any arguments passed to the application, as
well as the directory from which the application has been invoked. If the
application was invoked because of a file-type association, then the full path
to the file is included in the command line arguments. Likewise, if the
application was invoked because of an application update, the full path to the
update AIR file is provided.
When multiple files are opened in one operation a single InvokeEvent object is
dispatched on Mac OS X. Each file is included in the arguments
array. On
Windows and Linux, a separate InvokeEvent object is dispatched for each file.
Your application can handle invoke
events by registering a listener with its
NativeApplication object:
NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvokeEvent);
And defining an event listener:
var arguments:Array;
var currentDir:File;
public function onInvokeEvent(invocation:InvokeEvent):void {
arguments = invocation.arguments;
currentDir = invocation.currentDirectory;
}
Capturing command line arguments
The command line arguments associated with the invocation of an AIR application
are delivered in the InvokeEvent object dispatched by the NativeApplication
object. The InvokeEvent arguments
property contains an array of the arguments
passed by the operating system when an AIR application is invoked. If the
arguments contain relative file paths, you can typically resolve the paths using
the currentDirectory
property.
The arguments passed to an AIR program are treated as white-space delimited strings, unless enclosed in double quotes:
Arguments | Array |
tick tock | {tick,tock} |
tick "tick tock" | {tick,tick tock} |
"tick" "tock" | {tick,tock} |
\tick\ \tock\ | {"tick","tock"} |
The currentDirectory
property of an InvokeEvent object contains a File object
representing the directory from which the application was launched.
When an application is invoked because a file of a type registered by the application is opened, the native path to the file is included in the command line arguments as a string. (Your application is responsible for opening or performing the intended operation on the file.) Likewise, when an application is programmed to update itself (rather than relying on the standard AIR update user interface), the native path to the AIR file is included when a user double-clicks an AIR file containing an application with a matching application ID.
You can access the file using the resolve()
method of the currentDirectory
File object:
if((invokeEvent.currentDirectory != null)&&(invokeEvent.arguments.length > 0)){
dir = invokeEvent.currentDirectory;
fileToOpen = dir.resolvePath(invokeEvent.arguments[0]);
}
You should also validate that an argument is indeed a path to a file.
Example: Invocation event log
The following example demonstrates how to register listeners for and handle the
invoke
event. The example logs all the invocation events received and displays
the current directory and command line arguments.
ActionScript example
package
{
import flash.display.Sprite;
import flash.events.InvokeEvent;
import flash.desktop.NativeApplication;
import flash.text.TextField;
public class InvokeEventLogExample extends Sprite
{
public var log:TextField;
public function InvokeEventLogExample()
{
log = new TextField();
log.x = 15;
log.y = 15;
log.width = 520;
log.height = 370;
log.background = true;
addChild(log);
NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke);
}
public function onInvoke(invokeEvent:InvokeEvent):void
{
var now:String = new Date().toTimeString();
logEvent("Invoke event received: " + now);
if (invokeEvent.currentDirectory != null)
{
logEvent("Current directory=" + invokeEvent.currentDirectory.nativePath);
}
else
{
logEvent("--no directory information available--");
}
if (invokeEvent.arguments.length > 0)
{
logEvent("Arguments: " + invokeEvent.arguments.toString());
}
else
{
logEvent("--no arguments--");
}
}
public function logEvent(entry:String):void
{
log.appendText(entry + "\n");
trace(entry);
}
}
}
Flex example
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="https://www.adobe.com/2006/mxml" layout="vertical"
invoke="onInvoke(event)" title="Invocation Event Log">
<mx:Script>
<![CDATA[
import flash.events.InvokeEvent;
import flash.desktop.NativeApplication;
public function onInvoke(invokeEvent:InvokeEvent):void {
var now:String = new Date().toTimeString();
logEvent("Invoke event received: " + now);
if (invokeEvent.currentDirectory != null){
logEvent("Current directory=" + invokeEvent.currentDirectory.nativePath);
} else {
logEvent("--no directory information available--");
}
if (invokeEvent.arguments.length > 0){
logEvent("Arguments: " + invokeEvent.arguments.toString());
} else {
logEvent("--no arguments--");
}
}
public function logEvent(entry:String):void {
log.text += entry + "\n";
trace(entry);
}
]]>
</mx:Script>
<mx:TextArea id="log" width="100%" height="100%" editable="false"
valueCommit="log.verticalScrollPosition=log.textHeight;"/>
</mx:WindowedApplication>
Invoking an AIR application on user login
An AIR application can be set to launch automatically when the current user logs
in by setting the NativeApplication startAtLogin
property to true
. Once set,
the application automatically starts whenever the user logs in. It continues to
launch at login until the setting is changed to false
, the user manually
changes the setting through the operating system, or the application is
uninstalled. Launching at login is a run-time setting. The setting only applies
to the current user. The application must be installed to successfully set the
startAtLogin
property to true
. An error is thrown if the property is set
when an application is not installed (such as when it is launched with ADL).
Note: The application does not launch when the computer system starts. It launches when the user logs in.
To determine whether an application has launched automatically or as a result of
a user action, you can examine the reason
property of the InvokeEvent object.
If the property is equal to InvokeEventReason.LOGIN
, then the application
started automatically. For other invocation paths, the reason
property is set
as follows:
InvokeEventReason.NOTIFICATION
(iOS only) - The application was invoked through APNs. For more information on APNs, see Use push notifications.InvokeEventReason.OPEN_URL
- The application was invoked by another application or by the system.InvokeEventReason.Standard
- All other cases.
To access the reason
property, your application must target AIR 1.5.1 or
higher (by setting the correct namespace value in the application descriptor
file).
The following, simplified application uses the InvokeEvent reason property to decide how to behave when an invoke event occurs. If the reason property is "login", then the application remains in the background. Otherwise, it makes the main application visible. An application using this pattern typically starts at login so that it can carry out background processing or event monitoring and opens a window in response to a user-triggered invoke event.
package
{
import flash.desktop.InvokeEventReason;
import flash.desktop.NativeApplication;
import flash.display.Sprite;
import flash.events.InvokeEvent;
public class StartAtLogin extends Sprite
{
public function StartAtLogin()
{
try
{
NativeApplication.nativeApplication.startAtLogin = true;
}
catch ( e:Error )
{
trace( "Cannot set startAtLogin:" + e.message );
}
NativeApplication.nativeApplication.addEventListener( InvokeEvent.INVOKE, onInvoke );
}
private function onInvoke( event:InvokeEvent ):void
{
if( event.reason == InvokeEventReason.LOGIN )
{
//do background processing...
trace( "Running in background..." );
}
else
{
this.stage.nativeWindow.activate();
}
}
}
}
Note: To see the difference in behavior, package and install the application.
The startAtLogin
property can only be set for installed applications.
Invoking an AIR application from the browser
Using the browser invocation feature, a web site can launch an installed AIR
application to be launched from the browser. Browser invocation is only
permitted if the application descriptor file sets allowBrowserInvocation
to
true
:
<allowBrowserInvocation>true</allowBrowserInvocation>
When the application is invoked via the browser, the application's NativeApplication object dispatches a BrowserInvokeEvent object.
To receive BrowserInvokeEvent events, call the addEventListener()
method of
the NativeApplication object ( NativeApplication.nativeApplication
) in the AIR
application. When an event listener registers for a BrowserInvokeEvent event, it
also receives all BrowserInvokeEvent events that occurred before the
registration. These events are dispatched after the call to addEventListener()
returns, but not necessarily before other BrowserInvokeEvent events that might
be received after registration. This allows you to handle BrowserInvokeEvent
events that have occurred before your initialization code executes (such as when
the application was initially invoked from the browser). Keep in mind that if
you add an event listener later in execution (after application initialization)
it still receives all BrowserInvokeEvent events that have occurred since the
application started.
The BrowserInvokeEvent object includes the following properties:
Property | Description |
---|---|
arguments | An array of arguments (strings) to pass to the application. |
isHTTPS | Whether the content in the browser uses the https URL scheme (true) or not ( false). |
isUserEvent | Whether the browser invocation resulted in a user event (such as a mouse click). In AIR 1.0, this is always set to true; AIR requires a user event to the browser invocation feature. |
sandboxType | The sandbox type for the content in the browser. Valid values are defined the same as those that can be used in the Security.sandboxType property, and can be one of the following:
|
securityDomain | The security domain for the content in the browser, such as "www.adobe.com" or "www.example.org". This property is only set for content in the remote security sandbox (for content from a network domain). It is not set for content in a local or application security sandbox. |
If you use the browser invocation feature, be sure to consider security
implications. When a web site launches an AIR application, it can send data via
the arguments
property of the BrowserInvokeEvent object. Be careful using this
data in any sensitive operations, such as file or code loading APIs. The level
of risk depends on what the application is doing with the data. If you expect
only a specific web site to invoke the application, the application should check
the securityDomain
property of the BrowserInvokeEvent object. You can also
require the web site invoking the application to use HTTPs, which you can verify
by checking the isHTTPS
property of the BrowserInvokeEvent object.
The application should validate the data passed in. For example, if an application expects to be passed URLs to a specific domain, it should validate that the URLs really do point to that domain. This can prevent an attacker from tricking the application into sending it sensitive data.
No application should use BrowserInvokeEvent arguments that might point to local
resources. For example, an application should not create File objects based on a
path passed from the browser. If remote paths are expected to be passed from the
browser, the application should ensure that the paths do not use the file://
protocol instead of a remote protocol.
Application termination
The quickest way to terminate an application is to call the NativeApplication
exit() method. This works fine when your application has no data to save or
external resources to clean up. Calling exit()
closes all windows and then
terminates the application. However, to allow windows or other components of
your application to interrupt the termination process, perhaps to save vital
data, dispatch the proper warning events before calling exit()
.
Another consideration in gracefully shutting down an application is providing a single execution path, no matter how the shut-down process starts. The user (or operating system) can trigger application termination in the following ways:
By closing the last application window when
NativeApplication.nativeApplication.autoExit
istrue
.By selecting the application exit command from the operating system; for example, when the user chooses the exit application command from the default menu. (This only happens on Mac OS; Windows and Linux do not provide an application exit command through system chrome.)
By shutting down the computer.
When an exit command is mediated through the operating system by one of these
routes, the NativeApplication dispatches an exiting
event. If no listeners
cancel the exiting
event, any open windows are closed. Each window dispatches
a closing
and then a close
event. If any of the windows cancel the closing
event, the shut-down process stops.
If the order of window closure is an issue for your application, listen for the
exiting
event from the NativeApplication and close the windows in the proper
order yourself. You might need to do this, for example, if you have a document
window with tool palettes. It could be inconvenient, or worse, if the system
closed the palettes, but the user decided to cancel the exit command to save
some data. On Windows, the only time you will get the exiting
event is after
closing the last window (when the autoExit
property of the NativeApplication
object is set to true
).
To provide consistent behavior on all platforms, whether the exit sequence is initiated via operating system chrome, menu commands, or application logic, observe the following good practices for exiting the application:
Always dispatch an
exiting
event through the NativeApplication object before callingexit()
in application code and check that another component of your application doesn't cancel the event.public function applicationExit():void {
var exitingEvent:Event = new Event(Event.EXITING, false, true);
NativeApplication.nativeApplication.dispatchEvent(exitingEvent);
if (!exitingEvent.isDefaultPrevented()) {
NativeApplication.nativeApplication.exit();
}
}Listen for the application
exiting
event from theNativeApplication.nativeApplication
object and, in the handler, close any windows (dispatching aclosing
event first). Perform any needed clean-up tasks, such as saving application data or deleting temporary files, after all windows have been closed. Only use synchronous methods during cleanup to ensure that they finish before the application quits.If the order in which your windows are closed doesn't matter, then you can loop through the
NativeApplication.nativeApplication.openedWindows
array and close each window in turn. If order does matter, provide a means of closing the windows in the correct sequence.private function onExiting(exitingEvent:Event):void {
var winClosingEvent:Event;
for each (var win:NativeWindow in NativeApplication.nativeApplication.openedWindows) {
winClosingEvent = new Event(Event.CLOSING,false,true);
win.dispatchEvent(winClosingEvent);
if (!winClosingEvent.isDefaultPrevented()) {
win.close();
} else {
exitingEvent.preventDefault();
}
}
if (!exitingEvent.isDefaultPrevented()) {
//perform cleanup
}
}Windows should always handle their own clean up by listening for their own
closing
events.Only use one
exiting
listener in your application since handlers called earlier cannot know whether subsequent handlers will cancel theexiting
event (and it would be unwise to rely on the order of execution).
More Help topics
flash.desktop.NativeApplication
flash.events.BrowserInvokeEvent
Launching an installed AIR application from the browser