Table of Contents
Introduction
My first article introducing ThinkAlike framework was post to CodeProject on March, 2014. In short, it illustrated a story of swiftly developing a HearthStone game card reference in JavaFX firstly, according to the principles of MVVM, and then efficiently extending it to its Android equivalent with only native View layer replaced.
One year passed by, many things evolved (see Background section), and joyfully I still found the philosophy of ThinkAlike useful and would like to share a new user story about the framework.
The new story originates from the "Airpocalypse" which is puzzling Chinese, and contains three parts:
- ThinkAlike which had been forged to an independent library and become more handy (see Framework section)
- asynchronous mechanism poached from Android's source to be launched from generic module (see Service section)
- and a pair of widgets which could respectively report Air conditions from outdoors and in-the-office (see the screenshots above and UI section).
Background
During the year after my previous post, quite a few events occurred.
- Community: CodeProject held an Android Tutorial Contest
Although encouragingly attracted 20,000+ reads, my first ThinkAlike article has not received enough public comments yet. This was probably due to the difficulties of having to learn both JavaFX and Android at the same time, espeically for Android newcomers. Fortunately, CodeProject held a contest of Android tutorial writing last year. A great number of new articles and authors have emerged and I hope my MVVM cross-platform solution will appeal to more potential participants.
- JavaFX: community introduced JavaFXPorts
JavaFXPorts might have kick off very close to when ThinkAlike did (Dec,2013). It was motivated by the possibility of translating Java byte code into mobile OSs' and propelled by support from the JavaFX team, whereas ThinkAlike was based on experience in Android, MVVM and SWT (In fact I thought of taking SWT as Desktop UI, which is employed by Eclipse). Being a part-time project, JavaFXPorts also suffers lack of resource but Johan Vos (author) works really hard on it. I believe if succeeded it will bring great convenience to JavaFX developers.
- Android: ART replaced Dalvik
Google introduced a new compiler called ART (Andriod Runtime) and explained its difference with Dalvik. As an application layer solution, however, ThinkAlike needn't worry about such underlying modifications.
- ThinkAlike: Library + HearthStone Card Reference filtering function accomplished
First of all cross-platform related parts were split from the original all-in-one HearthStone card reference source. A HSCardRef project separated out and linked with the thereafter isolated ThinkAlike library project. And by now, functionally, HSCardRef has been upgraded to Func#3 which can filter cards by Mana/Atk/Hp as well as Ability traits! (And have to mention it's mainly fulfilled by my colleagues who have studied my tutorial articles!)
Day 1 -- Framework: Hello, ThinkAlike Library!
Challenges prior to an "Airpocalypse" expedition is to build up a start-up project for both JavaFX and Android platforms. Here we go!
For 1st Day tutorial, there will be no need to read back through my previous "HearthStone card reference" article.
Challenge 1. Environment Setup (2-4hours)
My development tools inventory remains the same:
The sample projects I provided are configured in e(fx)clipse. You may use other IDEs like Android Studio, NetBeans, but then you will be required to setup relationships among projects by yourself (mentioned below).
In addition, if you were a novice at either Android or JavaFX programming, or especially at using the Eclipse IDE, I recommend you run through at least one example project in your new environment. (Estimated 2-4 hours. Otherwise if any compiler/setting problems defeated you, remember to come back for a self-aid revival :))
Challenge 2. Project Settings (15-min for Eclipse users)
Settled down in the new environment, apprentices may unzip the package named HelloThinkAlike and find 6 folders deployed like below.
In the middle of the bottom generic module of ThinkAlike project can be located, flanked by Android module and JavaFX module. Accompanied by any one of the platform-specific modules, the platform-independent module can "awaken"(build) a relevant version of ThinkAlike library. Eclipse apprentices can implement this by importing the modules as "Existing Projects", with linkage well pre-configured.
In case you are not using Eclipse, please try to configure the linkage with the following hints in mind:
- ThinkAlike Android is an Android library project which references(links) to the source of ThinkAlike generic.
- ThinkAlike JavaFX is a JavaFX library project which also references(links) to the source of ThinkAlike generic.
- Although for Eclipse IDE, different kinds of references (source-level vs project-level) are used (mainly for a constraint of e(fx)clipse plugin), it ain't necessarily so.
Then move on to the 3 modules above. Application there should be replaced by the true name of your host application which employs the ThinkAlike library/architecture for cross-platform extensibility. For our tutorial package, it is named as simple as HelloThinkAlike.
For non-Eclipse apprentices:
- Application Android is an Android application project, with references both to Application generic for sharable sources and asset files, and to ThinkAlike Android as a library project(*1).
*1 Current version of Android SDK doesn't allow distributing a library as a JAR file. - Application JavaFX is a JavaFX application project, which references to the Application generic as well, and employs the correspoding ThinkAlike JavaFX in form of a JAR library (namely ThinkAlike_jfx.jar, in its libs subfolder).
Challenge 3. Building, Running, and Distributing (1-2hours for Eclipse users)
Through the perspective of the Eclipse, 6 modules would be reflected flawlessly like the following (excepts several suppressed warnings).
Our next objective is to activate the HelloThinkAlike_jfx application on our laptops and the HelloThinkAlike_android on our fingertips.
If any of the 6 reflections were flawed, that may be ascribed to Java Build Path or Android Project Build Target setting problems. Eclipse apprentices can amend them by openning the Project Properties of the untuned projects. After tunning, for double-check, you may try to build the projects in the following order (optional): ThinkAlike_generic, ThinkAlike_jfx, ThinkAlike_android, HelloThinkAlike_generic, HelloThinkAlike_jfx, and HelloThinkAlike_android.
In fact, most of e(fx)clipse apprentices could have directly launched HelloThinkAlike_jfx project from their IDE. ([Context Menu] Run As > Java Application, be aware when being asked to select an Application class, choose "HelloThinkAlikeApp")
A JavaFX GUI will rise up, with an round-cornered outline and alpha transparent background. It's merely a tutorial example, so simply "Click here", and you will unveil the secret.
Similar things would happen to the HelloThinkAlike_android project, except that your Android device should have been connected to and identified by your development OS and IDE. (USB driver problems could baffle you, as they had done to me. Using virtual device? overwhelmingly cumbersome..) For Eclipse apprentices, [Context Menu] Run As > Android Application, after transferring a zipped APK to your mobile and deploying on it, a similar GUI will appear.
Once being launched, rather than being merely compiled, Android project will encapsulate an .apk file in its bin subfolder, which can be submit to most Android App Stores.
By contrast, JavaFX project has extra learning curve on distributing stand-alone packages.
- For details (and for e(fx)clipse apprentices only), there is a must-read named JavaFX 2 Tutorial Part VII - Deployment With E(fx)clipse for Windows as well as MacOS.
- For skimmers, you may go directly to HelloThinkAlike_jfx\build\build.xml for a glimpse of the Ant script, which helps you directly jumping to Step 4 of the above tutorial. Then, if you're Windows user, download an .exe installer utility called Inno Setup, and remember to add its installation path to your Path environment variable. (For MacOS, no installer utility is required) Finally, run the build.xml by [Context Menu] Run As > Ant Build, the building will take a while. If succeeded, there will be an .exe installer in the folder \build\deploy\bundles.
Challenge 4. Customizing (1-10hours)
The warm-up, or start-up project, ends up here? Not until the true force be with you, force of the Code.
Overlook the code
HelloThinkAlike projects can be used as template for new development. The table below illustrates to which extent each source file having been customized for the HelloThinkAlike demo. Essential sources/assets are all marked in bold and will be explained later.
Source | Codeline | Level of Customization |
--- HelloThinkAlike_generic --- |
Config.java | 18 | low. log level, assets file root |
Constant.java | 13 | low. app title, assets file path, propertyNames |
HelloViewModel.java | 30 | high. ViewModel, main logic |
logo_*.png | 0 | high. UI, assets |
--- HelloThinkAlike_jfx --- |
Assets.java | 3 | n/a. helper class |
Res.java | 23 | n/a. helper class |
Config.java | 3 | n/a |
Constant.java | 15 | n/a |
Factory.java | 21 | n/a. create platform-specific adaptor classes |
HelloThinkAlikeapp.java | 147 | medium. entrance, initialization |
MainController.java | 80 | high. 1.UI, View layer 2.employs ViewModel |
scene_main.fxml | 25 | high. UI, layout markup |
style_base.css | 8 | high. UI |
bundle.properties | 1 | high. UI, I18N, text |
ApplicationPreloader.java | 7 | n/a. optional |
--- HelloThinkAlike_android --- |
Config.java | 3 | n/a |
Constant.java | 3 | n/a |
Factory.java | 27 | n/a. create platform-specific adaptor classes |
HelloThinkAlikeApp.java | 105 | medium. entrance, initialization |
MainActivity.java | 91 | high. 1.UI, View layer 2.employs ViewModel |
activity_main.xml | 31 | high. UI, layout markup |
styles.xml | 26 | high. UI, I18N, font size |
strings.xml | 5 | high. UI, I18N, text |
Change basic settings
The entrance, of each platform-specific project (HelloThinkAlike_jfx or HelloThinkAlike_android), can be located at source file of the class com.{domain_name}.{platform}.{app_name}App
. Application level settings can be configured there.
Take HelloThinkAlike_jfx as an example. You may open com.helloworld.jfx.HelloThinkAlikeApp
, the Application class, and move to its entrance method start()
, to check which settings you need to modify. (Similarly, Android Application class has an entrance method called onCreate()
)
@Override
public void start(Stage primaryStage) {
Locale.setDefault(new Locale("en", "US"));
......
com.helloworld.jfx.common.Constant.initialize();
Config.STORAGE_BASEPATH = "C:\\Test\\";
LogTag.removeExclusionItem(LogTag.AssetThread);
Util.trace(getClass().getSimpleName(), "......");
......
}
As shown above, locale, storage base (asset files will be extracted to an external folder), log filters (e.g. LogTag.AssetThread) etc., can be customized there. You may also open up com.helloworld.generic.common
package of the HelloThinkAlike_generic module, and check domain-specific constants/configs like application titile, log levels, which are statically defined.
The Application class also implemented a Platform interface declared by ThinkAlike library (com.thinkalike.generic.Platform
) so as to provide required platform-specific data/method to generic modules. For instance, by calling getOsType()
, generic module can tell current platform type so as to determine which OS logo should be displayed.
Change resources, assets
If you want to animate your own favourites onto the screen, e.g. AOE Paladins like I've done before in HTML5, you will find it convenient to manage the images in a folder common to both your Android project and JavaFX project. HelloThinkAlike_generic\assets-{locale} will be such repositories.
In the world of ThinkAlike, there are 2 kinds of methods to render an image: ordinary way by feeding path or drawable info to native classes (e.g.android.widget.ImageView), or alternative approach by employing adaptive classes which wrap the native controls with generic interface and consume generic content data, as will be detailed below.
Change logic
UI logic of an Model-View-ViewModel(MVVM) project resides in ViewModel classes which abstract essence from real View classes. ThinkAlike takes advantage of the MVVM philosophy to make the user story oriented code generic to platform-specific projects and easy to maintain, like this:
import com.thinkalike.generic.viewmodel.ViewModelBase;
import com.thinkalike.generic.viewmodel.control.UIImageNode;
public class HelloViewModel extends ViewModelBase{
private static String _helloMessage = "Hello, ThinkAlike!";
private static UIImageNode _helloImage;
private void updateHelloMessage(){
this.firePropertyChange(Constant.PropertyName.HelloMessage, null, _helloMessage);
if(_helloImage==null){
OsType osType = Loader.getInstance().getPlatform().getOsType();
ImageNode node = new ImageNode((osType==OsType.Android) ? Constant.PATH_LOGO_ANDROID : Constant.PATH_LOGO_JAVAFX);
_helloImage = new UIImageNode(null, node, false);
}
this.firePropertyChange(Constant.PropertyName.HelloImage, null, _helloImage);
}
public void onRefresh(){
updateHelloMessage();
}
}
Logic of HelloThinkAlike is extremely simple: handling a Click by displaying Message+Logo. Details of accepting user input and rendering contents can be accomplished by platform-specific View layer, whereas ViewModel layer encapsulates UI related data, logic, and communicates with the View through 2 kinds of interface:
- ICommand
User commands (not the intermediate ui operations) will be interpreted as ICommand events which will be dispatched from the View and handled by the ViewModel. onRefresh()
is a simple example. For time consuming transaction, you may also employ asynchronized mechanism for handling the command.
- IPropertyChange
UI update feedback will be interpreted as IProperty(Change)
events which will be fired from the ViewModel and consumed by the Views which have registered as listeners. There are 2 kinds of properties to be announced changed: _helloMessage and _helloImage. The former represents primitive data types/objects which can be easily rendered by ordinary native UI controls (e.g. android.widget.TextView), while the latter represents ComponentModel (e.g.UIImageNode) which can be consumed by platform-independent "adaptive" component classes (e.g. com.thinkalike.android.control.ImageNodeView). The ComponentModel, like a component-level ViewModel, can initialize the data required by a specific kind of component across different platform SDKs. You may modify the source to respond with weekday images, View layer will honestly render them.
Remember that the generic ViewModel doesn't know platform-specific View classes. It's View class who is responsible for the instantiation of corresponding ViewModel(s) and subscribes itself to their IPropertyChange
events.
Change UI for specific platform
Last month, HearthStone released its Android phone version (lower left). More compact layout, more cascading UI design which will lower user experience but maintain readability, makes it even different with its tablet ancestor (lower right).
vs.
There are several key factors to consider before deciding how many sets of UI need to design, e.g. available screen size or DIP (device independent pixel), available interactive methods/devices (e.g. glasses, watches). Under some circumstances, concerns about performance, compatibilities, support on new features, legacy sources etc., will leads to adopting platform-dependent development. ThinkAlike, with its MVVM design, however, provides cross-platform possibilities to such cases. Developers may employ adaptive UI components to suppress diversity among platforms, at the same time they may also keep enjoying platform-specific benefits as long as modeling ICommand/IPropertyChanged
stuffs into generic ViewModel classes.
For Android UI modification, as documented officially, resources under the res subfolder (e.g.activity_main.xml), and Activity classes should be checked out. On the other hand, for JavaFX, there is no strict conventions on the organization of resources, and currently they're managed under the com.{domain_name}.jfx.res package. A controller class named MainController interprets the native view-level logic of the layout scene_main.fxml ("Scene" equals "Activity" in JavaFX).
The following document collection will be helpful to novices while tunning the UI of HelloThinkAlike:
Change project name
This step may be moved to the first place as well.
When you determine to set off for your own project, you may replace {app_name}and {domain_name} throughout the HelloThinkAlike_* projects, which may appear at folder names, package names, or source file contents.
- In {app_name}_generic project,
- folders, packages: assets-{locale}\{app_name}, src\com\{domain_name}
- sources: globally replace string patterns like "com.{domain_name}", "{app_name}" throughout all source files, and .project setting file.
- In {app_name}_jfx project,
- folders, packages: src\com\{domain_name}
- sources: .classpath, .project, all source files, log4j.properties, build.xml.
- In {app_name}_android project,
- folders, packages: src\com\{domain_name}
- sources: .classpath, .project, project.properties, all source files, AndroidManifest.xml.
Summary
Day 1 is actually a simplification of my first article on ThinkAlike. Library has been separated out, leaving out implementation details of the ThinkAlike. And application logic has been simplified, transitions between MVVM layers become clearer. I hope that even novices at either Android or JavaFX programming could pass through all 4 challenges with least energy cost, and get prepared for further expeditions.
As planned, Day 2 will introduce AsyncTask enhancement made to the framework (com.thinkalike.generic.concurrent.*
). Day 3 will embrace AppWidget UI, which highly depends on interaction with platform, and will prove the adaptability of ThinkAlike.
Thank you for your concern about Android/Desktop cross-platform discussion. I will try my best to provide reader-friendly introduction, and will definitely appreciate any interaction. :)
TO BE CONTINUED...
History
05-06-2015: 1st edition
Tiancheng Hu is a senior tech consultant working at an ISV company (major products including scanner-based ECM, Asset Management), and holds Bachelor(SJTU@China) and Master(KIT@Japan) degrees in Computer Science. He showed favorites at Enterprise/Personal Knowledge Management and Efficiency Enhancement stuff (like MindMap/GTD/FastReading), and luckily had made prototype/proposal on an Enterprise Blog webapp(June,2000) and a portable PKM app(May,2010), before the public appearance of Blog or EverNote (in Chinese market).
When he is not working at designing/prototyping/consulting, he enjoys Chinese traditional spiritual practice/zen as well as fantasy novels/strategy games/scribble.