Exploring a complex mobile app

 
 

 
Explanation

This sample project is for a Flex mobile application that connects to server-side data to retrieve, update, add, and delete records in a database. To look at the code, right-click the dummy SWF file above in the browser and select View Source or download the sample files and view the source files in a text editor or follow the instructions to import the Flash Builder FXP. Multiple application versions are provided for PHP, Java, and ColdFusion servers.
 
This sample application is the same application built in the Flex Test Drive for Mobile. To walk through the creation of this application step by step or to build and deploy it yourself, follow the step by step instructions in those tutorials and/or videos.
 
Note: This sample retrieves data dynamically from a server at runtime. To add additional code to enable offline use, see the Creating a desktop application sample application, which syncs the remote data with a local database on the client and monitors connectivity.
 

 
Introducing Flex mobile applications

Beginning with Flash Builder 4.5, you can now build and deploy Flex applications on mobile devices. The version 4.5 release supports creating applications for the Google Android platform. The version 4.5.1 update will additionally support creating applications for the Blackberry Tablet OS and Apple iOS platforms.
 
Flex mobile applications created for the Google Android and Blackberry Tablet platforms are AIR applications that are packaged as APK and BAR files. To run the application on the mobile device, the device must support and have installed the required version of the Adobe AIR runtime, the cross-operating system runtime used to install, manage, and run AIR applications. The AIR runtime may be preinstalled on the device or it can be downloaded from the Android Market or the Blackberry App World.
 
In contrast, mobile applications created for the Apple iOS platform, must be compiled into native iOS applications; Flash Player and the AIR runtime cannot be installed on the devices because Apple forbids Just-In-Time compilation on their devices. Flash Builder 4.5.1 will add the ability to package applications as native iOS IPA application files, converting ActionScript applications into Objective-C bytecode.
 

 
Creating a mobile project

To create a mobile application with Flash Builder 4.5 or later, you use the New Flex Mobile Project dialog box to create a blank, a view-based application, or a tabbed application (see Figure 1).
 
Figure 1. Application templates for a Flex Mobile Project.
Figure 1. Application templates for a Flex Mobile Project.
Most mobile applications (especially those on mobile phones) are comprised of multiple screens, so you typically create a view-based or tabbed application. These application templates use the new ViewNavigatorApplication and TabbedViewNavigatorApplication containers that provide functionality and an API for easily creating and navigating between multiple views. As a user navigates through the application, the application switches to and from different views.
 
This sample application is a view-based application. When a view-based mobile project is created, three files are initially created: a main application MXML file, an MXML component, and an application descriptor file. The root tag of the main application file is a ViewNavigatorApplication tag:
 
Most mobile applications (especially those on mobile phones) are comprised of multiple screens, so you typically create a view-based or tabbed application. These application templates use the new ViewNavigatorApplication and TabbedViewNavigatorApplication containers that provide functionality and an API for easily creating and navigating between multiple views. As a user navigates through the application, the application switches to and from different views.
 
This sample application is a view-based application. When a view-based mobile project is created, three files are initially created: a main application MXML file, an MXML component, and an application descriptor file. The root tag of the main application file is a ViewNavigatorApplication tag:
 
<s:ViewNavigatorApplication xmlns:fx=http://ns.adobe.com/mxml/2009 xmlns:s="library://ns.adobe.com/flex/spark" firstView="views.FlexMobileTestDriveHomeView" ...> </s:ViewNavigatorApplication>
It has a property firstView , which references the MXML component that will be the first view (or screen) displayed in the application, in this case, a component called FlexMobileTestDriveHomeView located in the views package. FlexMobileTestDriveHomeView.mxml extends the View class:
 
<s:View xmlns:fx=http://ns.adobe.com/mxml/2009 xmlns:s="library://ns.adobe.com/flex/spark" title="HomeView"> </s:View>
You typically do not add content to the main application file, but instead add content to the views. The Flex 4.5 framework includes components and component skins that are optimized for mobile use and in some cases, enhanced to enable touch and throw scrolling. It also contains several new components specifically designed for mobile development. When you create a mobile project, the Flash Builder Component view displays a subset of Flex components reccomended for mobile use. For a list of the components that you can use in a mobile application, see Restrictions on using Spark and MX components in a mobile application
 
The third file is an application descriptor file, an XML file that contains information about the application including what the container operating system window should look like and how it should behave, what icon should be used for the application on the client device, what permissions the application should have, and more. This XML file is used when packaging the application for a specific platform. For Android and Blackberry Tablet applications, it is also used by the AIR runtime when it launches the application SWF contained in the application package on the device.
 

 
Connecting to data

The first view, FlexMobileTestDriveHomeView, displays a list of employees retrieved from a database on a server. To review the different options for connecting to data on a server and using Flash Remoting, read the code explanation for the Day 10 complex sample application. This mobile sample application also uses Flash Remoting to connect to server-side data but instead of using the RemoteObject class directly, it uses a data service created by Flash Builder, EmployeeService located in the services.employeeservice package.
 
The generated data service, EmployeeService, is a client-side proxy for the server-side service. It extends the generated _SuperEmployeeService class, which uses the RemoteObject class. EmployeeService has public methods corresponding to each of the methods of the server-side class, so you get code-hinting and compile-time error checking when using the service, which you don't get when using RemoteObject directly.
 
public function _Super_EmployeeService() { _serviceControl = new mx.rpc.remoting.RemoteObject(); valueObjects.Employee._initRemoteClassAlias(); var operations:Object = new Object(); var operation:mx.rpc.remoting.Operation; operation = new mx.rpc.remoting.Operation(null, "getEmployeeSummary"); operations["getEmployeesSummary"] = operation; // more code _serviceControl.endpoint = "http://localhost:8400/testdrive/messagebroker/amf"; // more code } public function getEmployeesSummary() : mx.rpc.AsyncToken { // code }
Note that in the sample files the endpoint is set to a URL for a local server. This works when developing and simulating the application on a device. To actually run the application on a device, however, the endpoint must be set to a URL on a publicly available web server.
 
The Flash Builder data service wizard can also be used to create client-side value objects for the different types of data returned or manipulated by the server-side methods. In this sample, the Employee value object was created in the valueObjects package.
 
To create a data service in Flash Builder, you select Data > Connect to Data/Service and then select XML, HTTP, WebService, PHP, ColdFusion, or BlazeDS or LCDS for Java. The data you enter or select depends upon the type of service you are creating. The different versions of this sample application were created using PHP, BlazeDS, and ColdFusion services.
 
In FlexTestDriveMobileHomeView, an instance of the EmployeeService data service called employeeService is defined inside the Declarations block:
 
<fx:Declarations> <employeeservice:EmployeeService id="employeeService"/> </fx:Declarations>
When the List's creationComplete event is broadcast (after the List has been created and all of its properties are set, including its size and position), the empList_creationCompleteHandler() function is called and an event object is passed to it.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)" .../>
The empList_creationCompleteHandler() function inside the Script block calls the getEmployeesSummary() method of the data service.
 
protected function empList_creationCompleteHandler(event:FlexEvent):void { getEmployeesSummaryResult.token = employeeService.getEmployeesSummary(); }
When this code is executed, the application makes a call to the server. This happens asynchronously in the background; the user can still interact with the application.
 
To handle successful results, a CallResponder object is defined in the Declarations block:
 
protected function empList_creationCompleteHandler(event:FlexEvent):void { getEmployeesSummaryResult.token = employeeService.getEmployeesSummary(); }
When a service call is initiated, an instance of the AsyncToken class is created. To associate the CallResponder object with the service call, you set the CallResponder's token property equal to the AsyncToken generated at the time the service call is made.
 
getEmployeesSummaryResult.token = employeeService.getEmployeesSummary();
Now when data is returned from the server, it is handled by the CallResponder object, which, in addition to getting its lastResult property set also has its result handler executed. The result handler removes a busy indicator component:
 
protected function getEmployeesSummaryResult_resultHandler(event:ResultEvent):void { this.removeElement(busyIndicator); }
... that was previously added to the application when it was instantiated.
 
<s:BusyIndicator id="busyIndicator" verticalCenter="0" horizontalCenter="0" symbolColor="red"/>
Finally, the dataProvider property of the List component is set equal to an instance of the AyncListView class whose list property is bound to the lastResult property of the CallResponder object. This means that whenever the value of getEmployeesSummeryResult.lastResult changes at runtime, the data associated with the List is updated and the List will repopulate itself with the new data. dataProvider is the default property of the List component so it does not have to be included as a child tag; when left out, it is automatically set equal to the AsyncListView instance.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)" labelField="lastname"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}" .../> </s:List>
The labelField property could be used to specify what property of the List data should be displayed for each list item (as shown in the code above). The sample application, though, does not simply display the last name of the user in the List. Instead, it uses an item renderer to display each employee's image, full name, and title.
 

 
Using item renderers

Many Flex components use item renderers to define how to display associated data in the component. You can define your own ItemRenderer class or use one of several included in the Flex framework. The Flex 4.5 framework includes IconItemRenderer, which been optimized for use on mobile devices. It displays four optional parts for each item in a list-based control: an icon on the left defined by the iconField or iconFunction property, a single-line text label next to the icon defined by the labelField or labelFunction property, a multiline message below the text label defined by the messageField or messageFunction property, and a decorator icon on the right defined by the decorator property (see Figure 2).
 
Figure 2. The parts of the IconItemRenderer class.
Figure 2. The parts of the IconItemRenderer class.
The List component has an itemRenderer property that you set equal to the ItemRenderer class to be used to render the data items. You can create the renderer as a separate ActionScript class that extends the IconItemRenderer class and set the List itemRenderer property equal to the fully qualified name of that class. For convenience, so you don't have to create and maintain a separate file, you can also use the Component compiler tag to define a new class inline. In the code below, a new class is defined that extends the IconItemRenderer class.
 
<s:List id="empList" left="0" right="0" top="0" bottom="0" creationComplete="empList_creationCompleteHandler(event)" change="empList_changeHandler(event)"> <s:AsyncListView list="{getEmployeesSummaryResult.lastResult}"/> <s:itemRenderer> <fx:Component> <s:IconItemRenderer iconHeight="50" iconWidth="50" iconFunction="getPhotoURL" labelFunction="getEmployeeFullName" messageField="title"> <fx:Script> <![CDATA[ private function getPhotoURL(item:Object):String { return "http://localhost:8400/testdrive/photos/" + item.photofile; } private function getEmployeeFullName(item:Object):String { return item.firstname + " " + item.lastname; } ]]> </fx:Script> </s:IconItemRenderer> </fx:Component> </s:itemRenderer> </s:List>
To customize the text to be displayed in the label part of the renderer (instead of just displaying the value of whatever property is set as the labelField ), you set the labelFunction property equal to a function to be called before an item is displayed in the control, in this case, getEmployeeFullName() .
 
To display an image next to the List label, you can set the IconItemRenderer iconField , iconFunction , iconHeight , and iconWidth properties. If the dataProvider for the List control had a property containing the full URL for the image to be displayed, you could just set the iconField property to the name of that property, but it doesn't; it has a property called photofile equal to the name of the associated employee image, for example, jdoe.jpg. Thus, the iconFunction property needs to be set instead of the iconField property, very similar to how the labelFunction property was used instead of a labelField .
 
In addition to retrieving and displaying all the employees, FlexMobileTestDriveHomeView also has functionality to search for employees by last name. Because all the employees have already been retrieved, the local data could have been searched (or filtered); in this example, however, another call has been made to the server to illustrate additional data calls.
 

 
Creating and navigating views

To create additional views for the application, you create MXML components that extend the View class. This sample application has three views: FlexMobileTestDriveHomeView, which displays a list of employees retrieved from a database on the server; DetailView, which displays the details for a selected employee; and AddEditView, which contains TextInput controls for adding or editing employees.
 
Note: All views use the EmployeeService data service to call additional methods of the server-side class. That code is very similar to the code explained in the previous Connecting to data section and will not be explained here.
 
To switch between views, you use the ViewNavigator class. The root container of the application, the ViewNavigatorApplication container, has a single child, a ViewNavigator. The ViewNavigator manages an ActionBar and a set of View instances using a stack-based history mechanism. The ActionBar component is displayed at the top of the application and appears above the views and consists of three areas: a navigator area, a title area, and an action area (see Figure 3).
 
Figure 3. The parts of the ActionBar container.
Typically, you place components that let the user navigate the content (like back or home buttons) in the navigator area, a string or other components in the title area, and components that let users take an action in the action area.
 
In FlexMobileTestDriveHomeView, the title area of the ActionBar contains a TextInput control and the action area contains Search and Add buttons.
 
<s:View title="HomeView" ...> <s:titleContent> <s:TextInput id="searchTxt" width="100%" prompt="Employee Name" enter="doSearch(event)"/> </s:titleContent> <s:actionContent> <s:Button id="searchBtn" click="doSearch(event)" .../> <s:Button id="addBtn" click="addBtn_clickHandler(event)".../> </s:actionContent> (...)
In DetailView, the title area of the ActionBar contains the title of the view (the name of the selected employee) and the action area contains Edit and Delete buttons.
 
<s:View title="{getEmployeesByIDResult.lastResult.firstname} {getEmployeesByIDResult.lastResult.lastName}" ...> <s:titleContent> <s:TextInput id="searchTxt" width="100%" prompt="Employee Name" enter="doSearch(event)"/> </s:titleContent> <s:actionContent> <s:Button id="editBtn" click="editBtn_clickHandler(event)" .../> <s:Button id="deleteBtn" click="deleteBtn_clickHandler(event)".../> </s:actionContent> (...)
In AddEditView, the title area of the ActionBar contains only the string "Add Employee" or "Edit Employee" depending upon which state is selected.
 
To switch between views, you use the ViewNavigator's pushView() , popView() , and replaceview() methods to "push," "pop," and "replace" views on and off the navigation view stack.
 
When an employee is selected in FlexMobiletestDriveHomeView, DetailView is pushed on the stack.
 
protected function empList_changeHandler(event:IndexChangeEvent):void { navigator.pushView(DetailView,empList.selectedItem.id); }
The first argument to pushView() is the view to display, DetailView. The second optional argument is any data you want to pass to the view. In this application, you want to pass the id of the selected employee, empList.selectedItem.id . When you pass a second argument to this method, the data property of the View class created (in this case DetailView) is populated with that value.
 
When the Add button is clicked, AddEditView pushed on the stack.
 
protected function addBtn_clickHandler(event:MouseEvent):void { navigator.pushView(AddEditView); }
Similarly in DetailView, when the Edit button is clicked, AddEditView is pushed on the stack and its data property set to the selected employee.
 
protected function editBtn_clickHandler(event:MouseEvent):void { navigator.pushView(AddEditView,getEmployeesByIDResult.lastResult); }
When the Delete button is clicked, however, no new views are added to the stack; instead, the existing view is popped off the stack and the user is returned to FlexMobileTestDriveHomeView.
 
protected function deleteEmployeeResult_resultHandler(event:ResultEvent):void { navigator.popView(); }
Similarly in AddEditView, when the Cancel button is clicked, AddEditView is popped off the stack and the user returned to the previous view, either FlexMobileTestDriveHomeView or DetailView.
 
protected function cancelBtn_clickHandler(event:MouseEvent):void { navigator.popView(); }
When the Create button is clicked, the existing view is popped off and the DetailView pushed on. 
 
protected function createEmployeeResult_resultHandler(event:ResultEvent):void { navigator.popView(); navigator.pushView(DetailView,event.result as int); }
When the Create button is clicked, the existing view is popped off and the DetailView pushed on.
 
navigator.replaceView(DetailView, event.result as int);

 
Creating pixel density aware applications

Applications built with Flex can target different mobile devices, each with different screen sizes, resolutions, and DPIs (dots or pixels per inch, also called the pixel density). Flex simplifies the process of producing resolution-independent applications by providing DPI-independent skins for mobile components and providing auto-scaling of your application (if you want) for different pixel density devices.
 
Flex classifies devices into three DPI categories: 160, 240, and 320. Actual devices come in a wide range of pixel densities, but for the purposes of layout it makes sense to group these into categories; for example, 238dpi or 249dpi displays are essentially equivalent to a 240dpi device. If you set a new applicationDPI property for the ViewNavigatorApplication tag (or any other Application or subclass tag), to one of these values, your layout is assumed to be tuned for that DPI and if it runs on a device of a different DPI classification, the entire application will automatically be scaled so that the user interface appears at the same approximate physical size on that device.
 
In the sample application, the applicationDPI property of the ViewNavigatorApplication has been set to 160. Note that in general it is better to scale upwards rather than downwards due to artifacts with vector rendering.
 
<s:ViewNavigatorApplication firstView="views.FlexMobileTestDriveHomeView" applicationDPI="160" ...>
Text scales well as its font size and not the text itself is scaled. Bitmaps, on the other hand, will not upscale well, so a new MultiDPIBitmapSource class has been added to the Flex 4.5 framework, which provides a way to specify different bitmaps to use on devices of different DPI classifications.
 
In the sample application, the graphics for all the buttons have been specified using the MultiDPIBitmapSource class. Here is the code for the search and add buttons in FlexMobileTestDriveHomeView:
 
<s:Button id="searchBtn" click="doSearch(event)"> <s:icon> <s:MultiDPIBitmapSource source160dpi="@Embed('assets/search160.png')" source240dpi="@Embed('assets/search240.png')" source320dpi="@Embed('assets/search320.png')"/> </s:icon> </s:Button> <s:Button id="addBtn" click="addBtn_clickHandler(event)"> <s:icon> <s:MultiDPIBitmapSource source160dpi="@Embed('assets/add160.png')" source240dpi="@Embed('assets/add240.png')" source320dpi="@Embed('assets/add320.png')"/> </s:icon> </s:Button>
To create applications that scale well, you should also try to avoid using constraints with absolute pixel values (or scale them appropriately using applicationDPI and runtimeDPI properties).
 

 
Integrating with the mobile device

The DetailView component contains functionality for integrating with the device, providing a way for users to send emails, send text messages, and make telephone calls from the application.
 
To email, text, and call, you use the navigateToURL() function in the flash.net package to make a request to the parent container, in this case, the device's operating system. In web and desktop applications, this function is typically used to open or replace browser windows (by passing a URL value). It can also be used to send emails (by passing a mailto link) in which case a mail program is opened to send an email. You can use the same syntax to send an email from a mobile application:
 
navigateToURL(new URLRequest("mailto:"+getEmployeesByIDResult.lastResult.email));
The device's default email program is launched (or you are prompted to select an appropriate application).
 
The navigateToURL() function has one required argument, an instance of the URLRequest class. The URLRequest constructor has one optional argument, a url string, which in this case is a mailto string with the selected employee's email.
 
Similarly to send a text message, you just change the protocol specified in the URL from mailto to sms :
 
navigateToURL(new URLRequest("sms:"+getEmployeesByIDResult.lastResult.cellphone));
Lastly, to make a telephone call, you change the protocol to tel :
 
navigateToURL(new URLRequest("tel:"+getEmployeesByIDResult.lastResult.cellphone));

 
Running the application in a simulator and on a device

To build the application, you write your code and run and debug your application just as you do for a web application. When you run the application though, Flash Builder launches a tool called the Air Debug Launcher (ADL), which simulates the application running on a specific device without you first having to install it on a device. You can use the ADL Device menu to simulate device actions (like pressing the Back button or rotating a device) and you can click and drag in a list to emulate scrolling.
 
To specify which mobile device is simulated, you create launch configurations in Flash Builder by selecting Run > Run Configurations (see Figure 4). For a particular configuration, you specify whether to launch the application on the desktop (simulating a specific device) or on a device connected to your computer via USB. Typically, you create multiple run configurations for devices with different pixel densities and simulate the application using each. Because your monitor is one particular pixel density, you will see this implemented in the simulator as different size windows with the application scaled to the appropriate size and using the appropriate multiresolution assets.
 
Figure 4. The Run Configurations dialog box.
Figure 4. The Run Configurations dialog box.
You can view common device configurations in Flash Builder by clicking the Configure button in the Run Configurations dialog box (see Figure 5).
 
Figure 5. Comon device configurations.
Figure 5. Comon device configurations.
To run the application on a device, you need to enable USB debugging on the mobile device and then attach it via a USB port on your computer. If you are using Windows, you will also need to follow the prompts to install a device driver. If you are accessing remote data as in this application, you need to make sure your services are installed on a publicly available web server, change the project's root URL to point to this server (see Figure 6) (this will modify the data service's generated endpoint), and update any other URL paths as needed (in this application, you would need to update the photo URLs).
 
Figure 6. The project's root URL set to a publicly available server.
Figure 6. The project's root URL set to a publicly available server.
At this point, you could create a run configuration to run on a device and a debug version of the application will be installed and launched on the attached device. Similarly, to debug the application on the device, you would create and run a debug configuration.
 

 
Customizing the file name and icon

If you packaged and installed the application at this point, the name of the application would appear in the operating system as the name of the MXML file and a default application icon would be used to represent the application.
 
You specify custom names and icons in the application descriptor file, FlexMobileTestDrive-app.xml.
 
<name>Employees</name> (...) <icon> <image16x16>assets/appicon16x16.png</image16x16> <image32x32>assets/appicon32x32.png</image32x32> <image36x36>assets/appicon36x36.png</image36x36> <image48x48>assets/appicon48x48.png</image48x48> <image72x72>assets/appicon72x72.png</image72x72> <image114x114>assets/appicon114x114.png</image114x114> <image128x128>assets/appicon128x128.png</image128x128> </icon>
You provide multiple PNG files of various sizes so the operating system can select the one that most closely matches the size it needs.
 

 
Packaging a mobile application

When you are ready to package and deploy the application, you select Project > Export Release Build just as for a web application, but in this case, Flash Builder launches the ADT (the AIR Development Tool) to create application packages for the selected target platforms. To package, you need a code signing certificate for each target platform.
 
To create APK packages for Google Android, you can self-sign your application or purchase an AIR Developer Certificate from ChosenSecurity, GlobalSign, Thawte, or VeriSign. To submit the application to the Android Market, you need to register as an Android developer.
 
To create BAR packages for Blackberry Tablet OS, you need to register for code-signing keys. To submit the application to the BlackBerry App World, you must also create a vendor account and submit the product for evaluation by RIM.
 
To create IPA packages for Apple iOS, you need join the iOS Developer Program and obtain a code-signing certificate, password, and provisioning profile from Apple. To submit the application to the Apple App Store, you also need a distribution provisioning profile.
 
Remember, in order for applications to run on the Google Android and Blackberry Tablet OS platforms, the device must have the appropriate version of the AIR runtime installed. If a device is attached via USB when you package the application, the application will be installed on the device and if necessary, the AIR runtime installed.
 
For more details on packaging mobile applications, refer to the following resources: