Monday Jan 27, 2014

Picking up the threads in ADF Mobile

There is a huge difference between the actual performance of an application and the user's perception of that performance. Typically, developers will try to improve the latter by delegating time-consuming tasks to background threads; in other words: asynchronous processing makes it possible to keep the user interface responsive at all times. This is especially important in mobile applications, where network bandwidth and latency can fluctuate wildly in a short time frame. Users are not necessarily aware of changes in network conditions, and thus will readily ascribe any slowdown to the application itself. Consequently, multithreaded programming is an essential part of the mobile developer's tool set.

ADF Mobile applications run on a Java virtual machine. Therefore, they can start threads that will exist in the context of the JVM process. In the current release, the ADF Mobile JVM follows the JavaME CDC specification, which is based on Java 1.4. This means that, unfortunately, the improvements brought by JSR 166 (java.util.concurrent)  are not available. On the other hand, threads are well integrated in the ADF Mobile framework. They can invoke AdfmfJavaUtilities.invokeDataControlMethod or AdfmfJavaUtilities.setELValue, for example. This makes it possible for you to update the user interface or refresh a bound collection in memory from a thread among other things.

The Apple iOS and Google Android operating systems manage application-related resources themselves. In iOS, when you switch to another application, the current application is suspended. On the other hand, Android's behavior in the same scenario will vary depending on the free memory available on the device. Typically, the processes belonging to an application will continue to run in the background after the switch; when memory is scarce, the operating system may force-kill the process. What happens when you switch away from an ADF Mobile application is thus dependent on the underlying OS. Any threads started by the application process will behave in the same way as the process itself.  By default, threads will suspend and resume by themselves on iOS; they will still run in the background on Android. 

If you want to implement multithreading in your application, my recommendation is to always manage the state of your threads explicitly and to interrupt them when the application is deactivated or suspended. This will ensure the integrity of your data and will make the application behave the same way independently of the operating system. Interrupting a thread is done by calling the interrupt() method of the Thread class and by checking the return values of the interrupted() or isInterrupted() methods inside the run() method of the thread or of the runnable.  The proper location for the call to interrupt() is a listener class implementing the oracle.adfmf.application.LifeCycleListener interface; such listeners must be registered in  adfmf-application.xml. The activate() and deactivate() methods it specifies will be invoked even if the application is killed through the Android task manager. Typically, in addition to interrupt the threads, the application will need to do the following in order to ensure a proper deactivation:

  • Write any restorable state to an appropriate store
  • Close database cursors and connections
  • Defer pending web service requests
  • Release resources such as files

These tasks can be performed by the threads themselves, by their associated java.lang.Runnable instances, or somewhere else. Be careful, though, since activate() and deactivate()will not be called if the application is terminated. It is also possible to implement listeners at the feature level if more granularity is needed. Such listeners implement the oracle.adfmf.feature.LifeCycleListener interface instead. Please note calls to activate() and deactivate() are blocking; you will need to be careful to ensure the application doesn't look unresponsive to the user.

Resource contention is without a doubt one of the greatest challenges any multithreaded application must solve. In ADF Mobile, each local database corresponds to a single file; the SQLite database engine thus implements a complex but reliable locking system.  Fortunately, ADF Mobile encapsulates all the complexity. If two threads - each possessing its own JDBC connection to the database - try to write at the same time, no exception will be thrown. One of the threads will own the write lock and will be able to proceed, while the other will wait. In other words: there can be only one database connection in write mode at any given time. All other connections will be in read-only mode until they can acquire the write lock. This will influence the design of your application. For example: if you have to insert a sizable number of records in a background thread, you will perform the operation in smaller batches in order to yield the lock to other threads of higher priority. 

Writing a good application is not easy, nor is writing a good performing one.  Multithreading can help with the latter, but you must be careful not to waste resources when the application is not in the foreground. After all, performance is not the only component in the user's perception of your application; battery life counts as well... 

Thursday Jun 20, 2013

Saved by the local database!

In my last post, I told you about my latest ADF Insider Essentials recording on the local database, and pointed you to the companion code sample. I had lots of feedback about both. I am glad to see I have so many viewers and readers!

Among all the questions I got, one was asked very frequently: « How can I transparently retrieve data from the database when a web service call fails? » In other words: how can the local database save my life if the web service doesn't respond? This is a bit different from what I had implemented initially. Thus, I built a new version of the sample application which does exactly that. In the original sample, the code simply detected the presence or absence of a network connection; an available connection meant the web service call was assumed to succeed. Otherwise, an exception was raised and displayed to the user. Thus, the key change to obtain the desired behavior is simply to catch the exception. Then, it is easy to invoke the method that retrieves data from the database instead.

Here is the relevant method in the sample application.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    private CountryBO[] getCountriesFromWS() {
        try {
            GenericType genericReturnValue =
                (GenericType)AdfmfJavaUtilities.invokeDataControlMethod("HR_WS", null, "findCountry", new ArrayList(),
                                                                        new ArrayList(), new ArrayList());
            CountryBO[] returnValue =
                (CountryBO[])GenericTypeBeanSerializationHelper.fromGenericType(CountryBO[].class, genericReturnValue,
                                                                                "result");

            Arrays.sort(returnValue);
            return returnValue;

        } catch (AdfInvocationException aie) {
            if (AdfInvocationException.CATEGORY_WEBSERVICE.compareTo(aie.getErrorCategory()) == 0) {
                AdfmfContainerUtilities.invokeContainerJavaScriptFunction("oracle.adfinsider.localdb.countries",
                                                                          "navigator.notification.alert",
                                                                          new Object[] { "The web service is unavailable. \n\n Data has been retrieved from the local cache.",
                                                                                         "null", "Warning", "Ok" });

                return getCountriesFromDB();
            } else {
                throw new RuntimeException(aie);
            }
        } catch (Exception ex) {
            Utility.ApplicationLogger.severe(ex.getMessage());
            throw new RuntimeException(ex);
        }
    }

Another nice thing this code snippet demonstrates is how to call JavaScript code from Java business logic. At line 15, I invoke a method which is part of the Apache Cordova library to display a warning message to the user. Cordova is an integral component of ADF Mobile, but your AMX pages must be properly configured in order to use it. I added the proper references to the countriesList.amx page like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  <amx:panelPage id="pp1">
    <amx:verbatim id="v1">
        <script type="text/javascript">if (!window.adf) window.adf = {}; adf.wwwPath = "../../../../www/";</script> 
        <script type="text/javascript" src="../../../../www/js/base.js"></script>
        <script type="text/javascript" src="../../../../www/js/cordova-2.2.0.js"></script>
    </amx:verbatim>
    <amx:facet name="header">
      <amx:outputText value="#{viewcontrollerBundle.COUNTRIES}" id="ot1"/>
    </amx:facet>
...
  </amx:panelPage>

I placed the script references (lines 3 to 5) inside a verbatim tag, which ensures that they will be rendered as is in the page.

While I was at it, I fixed a few other issues with the sample. In the original version, the database connection was closed inside the stop() method of the LifeCycleListenerImpl class. The stop() method is usually called when the use exits the application; there is no guarantee, however. Thus, the connection wouldn't be closed properly in some corner cases. To fix this, I moved the code to the deactivate() method, which doesn't suffer from the same drawback and will be called each time the user switches to another application. This is much better, as the connection will be properly closed even if the device crashes while the application is inactive.

You can download the refreshed sample application here

Friday May 17, 2013

See the ADF Mobile local database in action

ADF Insider recordings are probable one of the best parts of my job. They are a lot of work, sure. But they are lots of fun to do and join so many members of the ADF Community... 

My latest recording is on the ADF Mobile local database. In it, I explore the various aspect of the feature and devote a healthy chunk of time to the management of the database file. The slides contain a few selected code snippets, but I thought it would be better to build a sample application to fully illustrate the concepts. In particular, I wanted to show how it is possible to retrieve data from either a web service or the local database while binding the UI to a POJO Data Control. 

My sample application  is made is made of two distinct components: 

   - A simple SOAP web service (SDO view object) built on the top of the HR database schema. 

   - The ADF Mobile application itself, that demonstrates local database techniques and calls the web service when a network is available. Data is fetched from the local database when their is no connectivity.

I contributed the application to the ADF Enterprise Methodology Group samples repository. It is not listed on the web pages right now, but you can download it from the following location:

https://svn.java.net/svn/smuenchadf~samples/ADFMobileLocalDatabase.zip

My recording is available on our YouTube channel here: http://www.youtube.com/watch?v=-XzE1n_j5Nc

About

Frédéric Desbiens

The musings of a member of the ADF Product Management team.

I focus here on my favorite development framework but also have a strong interest in Mobile Development, Oracle WebCenter and Oracle SOA Suite.

Attentive readers will even find posts about IT Strategy from time to time, an interest of mine since I completed my MBA in 2006.

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today