Introduction
This primer provides an overview of mobile application development using Flex 4.5. It includes some information specific to Android.
Note: Since this primer focuses on using Flex/Spark to develop mobile apps much of its content will not apply to ActionScript-only mobile projects
While this primer includes links to numerous resources (blog posts, docs, books) one Adobe doc stands out so I'll highlight it at the start. Developing Mobile Applications with Flex and Flash Builder is an in-depth developer guide and is probably the definitive resource on the topic (link provides access to online and pdf versions). BTW, if you are not using Flash Builder you should give it a serious look, it has many features that simplify creation of Flex/AIR mobile apps.
Developer guides are great but we always need ASDoc, so here here is the doc you'll need for mobile dev.
Overview of Mobile Application Development using Flex 4.5 sdk
This section summarizes key Flex 4.5 mobile features. Later sections drill into more detail.
Design for mobile
When creating mobile apps the traditional application developer must (to coin a phrase) think different. Some differences are obvious, some less so.
The most obvious difference between "traditional" Flex/AIR apps and mobile apps is real estate — not just screen size in pixels (Droid X is just 854x480, and that's more pixels than your average Android phone) but also physical screen size and the resultant pixel density. The skins for mobile apps account for mobile's greater pixel density by "sizing up". For example, the mobile skin for a Flex Button sets a button's height at about 70 pixels. While this is large on most PC screens (because they pack in fewer pixels per inch) it's just about right for most smartphones. But this affects screen design: when buttons are 70px then 854x480 starts looking even smaller.
Beyond sizing issues you also have some fundamental functionality differences. Different input methods is one: your desktop app that makes use of mouseOver won't work so well on a phone. And then there are the fun layout challenges that arise from re-orientation: most desktop apps never had to worry about users frequently flipping their screens between portrait and landscape. And then there's touch: it's a swipe-to-scroll world, so just kiss those old scrollBars good-bye...
Devnet has several excellent posts on this topic so let me mention a few here:
You might find Nacisso Jaramillo's 360Flex 2011 presentation Building Multi-Density and Multi-Platform UIs with Flex interesting.
Finally, Adobe doc also covers some mobile-specific design issues — for example, see Chapter 1 of Developing Mobile Applications with Flex and Flash Builder.
Suffice to say that your UI design and app architecture for mobile will differ (sometimes significantly) from that of your traditional Flex apps. And this is where the new Flex sdk help you out — it includes many features that support/encourage good mobile design.
Flex 4.5
The 4.5 sdk provides functionality to support design patterns common in the mobile world. A lot of this involves data persistence and navigation. Flex 4.5 provides a memory-efficient mechanism for navigating between views (View is another new class, for now just think of it as a full-screen container). Flex 4.5 also provides data persistence features that help you maintain state for individual views and even your entire application.
The root class of your Flex/AIR Mobile app will be Spark's new ViewNavigatorApplication or TabbedViewNavigatorApplication classes, not Application or WindowedApplication.
The TabbedViewNavigatorApplication class lets you create more complex applications that have multiple sections, each with its own ViewNavigator, and buttons at the bottom of the screen that let you switch between these sections. I won't cover TabbedViewNavigatorApplication in this version of the primer but will add a section on it shortly.
When you create a mobile app (File/New/Flex Mobile Project) Flash Builder 4.5 creates both your mobile application's root class (e.g., an instance of Spark's ViewNavigatorApplication class) and a default home view (an instance of Spark's View class). You define your UI in the views, not in your root class (though you can put code in the root class).
During development you create the views you need (File/New/MXML Component) and within those view containers you define your UI. Then at runtime you navigate between views using features of the new ViewNavigator class. ViewNavigatorApplication creates an instance of ViewNavigator for you and it is available to all views through their navigator property. The ViewNavigator itself creates an ActionBar instance that is shared by all views. This actionBar is basically a container that can appear docked above your view and is commonly used for title or message text and option controls.
Here's a crude representation of the relationship between ViewNavigatorApplication, ViewNavigator, ActionBar, and View:
|
As for defining your UI, for the preview release Adobe recommends use of a limited Spark component set (currently only a subset of Spark components have been given mobile skins). These mobile-ready controls are listed in "Developing Mobile Applications with ADOBE Flex and Flash Builder" (see here or page 5 of the pdf).
As for using MX controls in your UI, in general you should not use any mx controls in a ViewNavigatorApplication project. In fact, by default the mx controls are not even in the path (no xmlns for mx). However, Adobe does say that if you want to include charts in your mobile app you can use the mx datavisualization controls. Again, for details see here or page 4 of the PDF).
A mobile app's navigation between views is similar to that of mx:ViewStack, where you navigate between child containers with a single container active (visible) at a time. One big difference is persistence: a ViewStacks's children persist once they've been created, but ViewNavigator keeps only one view in memory at a time. That is, garbage collection for views is essentially immediate — by default as soon as you move off a view it's a candidate for garbage collection. If you navigate back to that view you'll get a completely new instance. This helps minimize your app's memory footprint.
When you are coding a mobile app think memory, memory, memory. The threshold for encountering memory problems is much lower on a phone than on a PC. Remember, most phones have very limited RAM (Droid X = 512 MB and many phones have less). If you use too much memory Android may kill your app (see my post AIR for Android memory issue with large images on this). If your app is in background and Android needs more memory it can also kill your app (another reason you should maintain view and app state).
Ok, that's some basics, here are some resources, and then we'll drill down a level.
Resources: Intro to Flex 4.5 Mobile dev
A good intro into Flex mobile dev is the new Tour de Mobile Flex. Install it from the Android marketplace and see this stuff in action, with source code provided.
Here are some links to Flex 4.5 mobile dev overview pages and videos
- Mobile development using Adobe Flex 4.5 SDK and Flash Builder 4.5
- Adobe provides some sample mobile apps here.
- http://tv.adobe.com/watch/adc-presents/build-your-first-mobile-application-in-flash-builder-45/
- http://tv.adobe.com/watch/adc-presents/mobile-device-debugging-using-usb/
- http://tv.adobe.com/watch/max-2010-develop/introduction-to-adobe-air-for-mobile/
ViewNavigator, ActionBar, and Views
Every ViewNavigatorApplication has a ViewNavigator instance that provides much of your app's mobile-specific functionality. In addition to creating views and providing view navigation the navigator provides an optional and configurable ActionBar that can appear atop your view and can contain UI controls.
To interactively explore the parts of the Flex mobile app I recommend installing Tour de Flex Mobile on your phone and then using its "Mobile App Anatomy" option. |
Below is a screencap that shows the layout of a basic mobile app.
← Action bar is created/managed by ViewNavigator. It is optional and customizable. You can have a global actionBar that is modified by individual views
← This view's content area is completely filled with a StageWebView instance, but as with any container you can populate it with whatever components you need. View is a subclass of SkinnableContainer. |
View anatomy (Tour de Flex Mobile "StageWebView" example)
An ActionBar has three parts: a titleContent area, a navigationContent area (left of title area) and an actionContent area (right of title area). You have total control over what appears in these areas through properties of the View class. For example, View.title sets the text in the titleContent area. But you can also put your own components in this area using the titleContent property. See Define navigation, title, and action controls in a mobile application for details and several usage examples. Remember, you have total control over what displays in the actionBar (or whether the actionBar displays at all).
As noted above, a view is basically a container where you place your user interface controls and the code that drives them. What makes this container different from other Spark containers like Group and SkinnableContainer is that View has properties and methods specific to the Flex mobile architecture. In essence it's a "ViewNavigator-aware" container.
While a View is a container, in some ways it's like a very specific type of container, an ItemRenderer. In fact each view has a data property just like an item renderer. In the case of a view, it's up to you to populate that data property. We'll cover this in a minute, but first let's take a moment to consider the lifecycle of a view.
Here is a nice summary of View from the Adobe View+ViewNavigator design doc : "A view can be thought of as a standard Flex 4 ItemRenderer with an integrated data model and additional functionality that allows a view to provide contextual information about itself to external components (such as ActionBar). A view will have a data property that can be used to represent the content and the state of the view. This object is automatically serialized, persisted and restored by the view's navigator as the view is activated and deactivated"
To keep memory use down ViewNavigator keeps only the currently displaying view in memory. In other words, when you navigate away from a view it is immediately eligible for garbage collection. So, if you navigate from ViewA to ViewB, by the time ViewB is displayed it no longer has ViewA available for reference (presuming you have no pointers to ViewA that inhibit its garbage collection). An obvious implication of this design is that time you can't directly set event listeners from one view to another; or have one view directly read another's data. Note that this auto-destruct behavior for inactive views is default. You can override it using View's destructionPolicy property but in general you shouldn't — remember: memory, memory, memory!
Although views are destroyed, their data persists — that is, Flex stores/restores the contents of each view's data property each time it's destroyed/instantiated. So, the contract here is that when you return to a view the previous contents of its data property will be available to you; your code can count on the data property being populated early in the instantiation process (before the ADD event fires). How might you use this view data persistence mechanism? Well, you could use it to restore your view to a previous state. More on this in the next section.
The View class data property
The View class defines a property named data which is of type Object. The very first time a view is instantiated its data property has a value of null (well, unless you've implemented application-level persistence, but we won't cover that here). So, your app will generally include a check to see if your view's data property is null and, if it is, creates the data structure you want to work with. Something like this:
if (data==null) { data = new Object() ; data.someText = "howdy!" ; data.someNumber = 1234 ; } else { // do something with contents of data property, // e.g., initialize your view with data values }
You can populate a view's data property with whatever data you want. The thing about the data property is that once it's populated it stays that way — ViewNavigator will persist the contents of a view's data property whenever the view is destroyed (e.g, when you navigate to a different view) and it will restore those contents if your view is reinstantiated.
Because a view's data persists during an app's session (and potentially across sessions) one use of a view's data property is to maintain state. How? That's up to you, the framework just provides a persistence mechanism. But clearly this isn't rocket science. Just fill data with whatever you need to restore state, and to complement this you include instantiation-time code that uses the contents of data to initialize fields to previous values, maybe makes use of Flex 4 states to restore appearance, etc.
It's worth emphasizing that saving and restoring state is an important part of mobile app design. Remember, you're on a phone, and users sometimes navigate away from your app. If they're gone long enough your app may be killed by the OS. When mobile users eventually return to an app they generally expect to pick up right where they left off. It's your job to make this happen. Of course, in some apps saving state isn't important. In that case don't use the data property for this purpose. Again, how you use the data property is up to you.
So far I've addressed using the data property to save state. But it can also function like the data property of a Flex ItemRenderer. That is, you can use it to pass in data you want your view to render. We'll cover this in a moment, but before that we need to consider navigation between views in a bit more detail. First, here are some resources:
Resources: MobileApp Views and ViewNavigator
For a diagram of a view's lifecycle including the firing order for events like VIEW_ACTIVATE and VIEW_DEACTIVATE see the section "The life cycle of the Spark ViewNavigator and View containers" in "Developing Mobile Applications with Flex and Flash Builder". It's also covered in the View design doc mentioned next.
Here is Adobe's design doc for View (and ViewNavigator), really interesting reading, though it's just a design doc so actual implementation sometimes varies from the doc details.
Also check out the ATV videos from Max 2010 (Develop), there are several related to mobile development.
And Mihai Corlan has many thoughtful posts on Flex, here's one on ViewNavigator and Views.
View navigation
To navigate between views you can use the ViewNavigator class. ViewNavigatorApplication creates a ViewNavigator instance at startup and makes it available to all views though their navigator property.
It can be useful to think of mobile view navigation as similar to mx:ViewStack, where you have multiple containers, one container is initially displayed, the containers can be displayed in an arbitrary order, but only one container is active (visible) at a time. However, a big difference between ViewStack and ViewNavigator is persistence: with ViewStack your child containers are created when you first navigate to them and after that they persist; with ViewNavigator each view is created every time you navigate to it and destroyed every time you navigate away from it (this is default behavior, you can modify it, for example if you have a view that takes a lot of time to create).
View navigation is done via stack push and pop operations. When your app starts up the Flex framework will display your home view. This is the view you've assigned to your ViewNavigatorApplication's firstView property. When you create a ViewNavigatorApplication in Flash Builder 4.5 (File / New / Flex Mobile Project) an initial view is created for you in a views package. By default it will be named [your-project-name]+"Home". From this home view you can display another view through a push operation (navigator.pushView(MyOtherView);
). More views can be added to the stack with more push operations.
The navigation stack operations available are: pushView(), popView(), popToFirstView(), popAll(). To these methods you pass the view you wish to display. Note that you pass the view class name, not an instance of the view class. This is because the ViewNavigator is responsible for the instantiation of views (via a class factory) so you don't instantiate them yourself (so, you do not do: var myViewInstance:MyView = new MyView();
).
See ViewNavigator's ASDoc entry for details.
Let's consider a simple example where a home view uses another view to display detail information (image from Developing Mobile Applications with Flex and Flash Builder).
views.ListView views.DetailsView
A scenario for the above could be:
- Your ViewNavigatorApplication has the assignment
firstView="views.ListView"
so at application startup an instance of the ListView class is created and displayed - The view ListView is basically just a selection list. It responds to a user selection by navigating to DetailsView, which lives in the default views package. So, your call might be:
navigator.pushView( views.DetailsView )
- When DetailsView is pushed onto the stack it is instantiated by the ViewNavigator and added to the display list. At the same time, our ListView view has been removed from the display list and is now eligible for garbage collection (but the contents of its data property have been saved away, ready for recall in case we re-instantiate ListView)
- DetailsView can either display yet another view by executing its own navigator.pushView OR (as shown in the diagram) it can return to ListView with a call to navigator.popView().
- When the popView executes ViewNavigator just refers to its stack to get the name of the last displayed view class, then it instantiates that view (note that we get a completely new instance) and then it populates the view's data property with the data saved away in step 3.
Of course, the example above is incomplete since it doesn't consider how the detail view knew whose details to display (in this case, info on Lauren Fisher). We'll consider passing data between views in the next section.
Note that in Flex mobile apps the Android back button takes you back to the previous view (i.e., it executes a popView()). This behavior is implemented by the ViewNavigator class — you do not need to do anything to enable this native Android behavior. I'll mention that in Hero, the prerelease of Flex 4.5, you couldn't inhibit this native behavior by listening for an Android back button press and then simply stopping the popView through preventDefault() — didn't work in Hero, and I haven't had time to test htis in 4.5.
Resources: View Navigation
- I mentioned Mihai Corlan's post already but it's so good on this topic I'll mention it again: Understanding Flex mobile views and viewnavigator/
- Wiki on flexsdk and View/ViewNavigator
- ViewNavigator source code (obviously also available in FB)
- Wiki on flexsdk and Views and Transitions
- http://www.flex-blog.com/view-navigation-in-a-mobile-flex-application/
Passing Data between views
As your app navigates between views you will, of course, need to pass information between your views. As with many things in Flex, there are several ways to skin this cat. Mostly we'll focus here on the built-in mechanisms provided by Flex.
During view navigation you can pass data "forward" (i.e., when you do a pushView) and "backward" (e.g., when you do a popView) through features of ViewNavigator. How you do this varies depending on whether you are pushing or popping a view.
pushView
To pass data into a view you can use pushView's second parameter to specify a data object that will be passed to the new view. The new view will receive this through its data property. Here are some examples.
Example 1 - passing a property value of a UI component, in this case a list's selected item:
navigator.pushView(views.DetailView,myList.selectedItem) ;
Example 2 — creating/passing an object:
var myObj:Object = { user:username , numSelections:2 , msgText:"Choose 2 values" }; navigator.pushView(views.SelectionList, myObj) ;
Example 3 - passing the view's data property:
data.listMessageText = "Select a customer" ; data.listDataProvider = model.customerIDs ; navigator.pushView(views.SelectionList,data) ;
When you pass data into a view it is assigned to the view's data property. In that way a view is a bit like a Flex ItemRenderer. That is, upon instantiation its data property is populated with the data to be rendered and you use this to populate your view components (e.g., msgline.text=data.msgText
). Note that Flex 4.5 also includes "real" mobile-optimized renderers like IconItemRenderer but I won't be covering them in this version of the primer.
popView
When you want to return data on a popView() the mechanism for passing data is different than with pushView — there is no popView parameter for passing data. However, you can return data to the previous view through the Navigator class poppedViewReturnedObject property. You do this by overriding the createReturnObject method and, in this override, creating the data object you want to pass back. Then in the popped-to view you retrieve the returned data from the navigator.poppedViewReturnedObject property. So, a 2-step process:
1. The view that will return data defines an override of its createReturnObject method. This method is used to create an object that will be passed back to the previous view. It executes whenever the view is popped. Here's an example:
override public function createReturnObject():Object { var listViewReturnObject:Object = new Object(); if (myList.selectedItem) { // pass back selected value, here we just return the item's label // text in a property arbitrarily named lastSelectedItemValue listViewReturnObject.lastSelectedItemValue = myList.selectedItem[myList.labelField] } return listViewReturnObject; }
2. In the view you've popped to you include a creation-time check of the navigator.poppedViewReturnedObject property. If it's not null then the assumption is that your current view was "popped to", and the "popper" has passed you some data. That data is stored in the object property of navigator.poppedViewReturnedObject. This is shown in the example below. BTW, to ensure against RTEs you may want to use hasOwnProperty() to ensure that the returned object actually has a property before you try to access that property.
if (navigator.poppedViewReturnedObject != null) { // process the data object that was passed back... msgline.text="You selected " + navigator.poppedViewReturnedObject.object.lastSelectedItemValue; }
As an alternative to using the returnedObject property you could share data between views through a persistent data structure that's available to both views. One view can modify the data and then when your app navigates to another view the data is still there for it to read. One way to do this is through a view's data property, since it's persistent. Something like:
- ViewA creates some data structure (e.g., Object) and assigns it to its data property
- ViewA then calls ViewB, and it passes along a reference to its data property
- ViewB does whatever it does. This includes updating/adding values in the the data object it was passed
- Eventually ViewB calls popView, thus returning control to ViewA
- ViewA can now retrieve the updated/added data from its own data property
As mentioned above, this works because the view's data property is persistent.
Using a model
Of course, there are other (and arguably better) ways to persist data across views. You might want to consider using a model to store your data (model as in Model-View-Controller). Since mobile apps are by nature fairly simple you may even want to use a Singleton ModelLocator (basically, the same approach used by Cairngorm and PureMVC). This lets you centralize data so it's available to all views. Just instantiate it in your app's root class (the one with the ViewNavigatorApplication root tag) and it will persist even as your individual views come and go. A downside of this is that each view becomes tightly coupled to that ModelLocator, since they must must "know about" (have dependency on) that ModelLocator to read and write this data.
Resources: Passing Data between views- see "Developing Mobile Applications with Flex and Flash Builder" section Define views in a mobile application
- Adobe cookbook's post on Passing data between Views
- http://devgirl.org/2011/05/18/flex-4-5-mobile-data-handling/
- remotesynthesis.com post on passing data across views in flex mobile
Conclusion
Developing mobile apps is a big topic and this post barely scratches the surface (hey, where's info on touch and gesture support? IconItemRenderer? using geolocation?). Still, hopefully some of this has been useful to get you started with developing mobie apps in Flex 4.5. When I have time I'l write a post with general tips and traps, for now I'll just tack on a few sample tips below:
- Since memory is critical on mobile you need to keep an eye on it. How? If you have Flash Builder Premium then just use the profiler. If you don't then you can check your app's memory use through System.privateMemory.
- Flex mobile apps let you interface with native OS apps (e.g., invoking the Droid's camera, cameraRoll, the dialer, etc). When you invoke one of these apps your app goes into background mode and a DEACTIVATE event will fire. If control returns to your app an ACTIVATE event will fire. For example, if you invoke CameraRoll then when the user selects an image an ACTIVATE event will fire as your app returns to foreground. Keep this behavior in mind if you are thinking of using those events to trigger view initialize and cleanup activities.
- Flex ViewNavigator uses nice view transitions but they can get flaky if you do other processing while the transition is executing. You can always kill transitions with ViewNavigator's transitionsEnabled property. Or you can just wait until the transition has completed by listening for the transitionEnd event. Bottom line: avoid doing any work during transitions.
- If you don't like the title text on the ActionBar you can easily replace it or style it. Here's a replacement that drops down the font size so the text can better be used as a messageline:
<s:titleContent> <s:Label id="msgText" text="howdy" color="yellow" fontSize="24" verticalAlign="middle" width="100%"/> </s:titleContent>
More Flex/AIR Mobile Resources
O'Reilly has just published a book on developing AIR apps for Android, "Developing Android Applications with Adobe AIR". It's an excellent intro to the topic.
Adobe TV has several Max 2010 videos on Mobile development. Here's your start point: http://tv.adobe.com/show/max-2010-develop/
If you are a Lynda subscriber there's a video course titled Flash Builder 4.5 and Flex 4.5 New Features (they also have one for Flex "Hero" and Flash Builder "Burrito" Beta Preview).
Some links that didn't fit in above:
- here is Adobe Devnet's main page for Mobile and Devices
- Wiki on flexsdk and View+Transitions
- Flex mobile view transition direction/
- Adobe Edge January 2011 articles index
- unitedmindset.com (Jon Campos Flex 4.5 mobile development post burrito/
- devnet article on multitouch gestures
- http://www.riagora.com/2010/12/tips-for-flex-mobile-apps/
- unitedmindset.com on Flash player gestures/
- www.unitedmindset.com AIR for Android text messages emails and phone calls/
- remotesynthesis.com AIR for Android and Flex mobile resources
Update: I now have a Primer on Flex/AIR Multiscreen Development which covers many mobile issues, like handling different screen sizes and pixel densities, CSS for styling to OS conventions, etc. I also have added an AIR mobile dev Tips post.