How To Optimize The Performance Of PhoneGap Apps

Despite the increasing processing power of mobile devices, most cross-platform apps cannot fully satisfy the constantly growing demands as for the visual richness combined with good performance on both iOS and Android. How can we resolve the issue? The most obvious answer lies in understanding the specifics of the platform, the wrapper on which the application code is running (mobile WebView), and the corresponding processes.

This article was inspired by our diverse expertise in mobile application development. Here you’ll find problems of code execution, solutions and helpful tips on optimizing the performance of cross-platform apps based on PhoneGap (a.k.a. Apache Cordova).

Let’s start with the runtime environment in which the application code is running:

#1. Runtime Environment

The whole program code in PhoneGap apps or other types of JS/HTML apps runs in mobile webView, which is a browser built into your application. WebView serves as a wrapper or a virtual runtime environment for the execution of your functional code. Internal limitations and specificity of app execution fully depend on the architecture of the built-in browser. There is no official documentation about browser internals. Most modern browsers work according to the following diagram:
The whole program code in PhoneGap apps or other types of JS/HTML apps runs in mobile webView, which is a browser built into your application. WebView serves as a wrapper or a virtual runtime environment for the execution of your functional code. Internal limitations and specificity of app execution fully depend on the architecture of the built-in browser. There is no official documentation about browser internals. Most modern browsers work according to the following diagram:

It clearly shows 3 stages of the main thread: loading, reflow, repaint. Each of these stages has a great impact on app performance.

– Loading includes loading the HTML and CSS with the content and design, as well as parsing of the downloaded data.
– Repaint – actually paint and display.
– Reflow – the most heavyweight part of the main thread, related to processes such as loading and repaint. Includes the construction of a document object model, witch contains all the document data. Creating design rules, applying them to the DOM, creating the render tree, layouting (dividing the render tree into independent and dependent layers for rendering, and their optimization). There is also a Garbage Collector which affects all three stages of the main flow.

All of these stages are crucial, therefore we will give a few practical tips. After showing WHAT should you do, we’ll show WHY you should do it, so we will refer to the theory and browser internals.

#2. Control The Size Of Your DOM

As you could see from the previous scheme, DOM is actually a separate functional mechanism in the browser. Data processing / manipulation takes time because DOM is stored in memory all at once.

 

The more data you have (the bigger DOM is), the more time it requires for manipulation.

Is there a solution to this situation? Yes, it’s trying to manipulate a more lightweight DOM. There are 4 ways of achieving it:

1. Make your application markup as simple as possible.

Pros: faster and easier development.

Cons: it doesn’t solve the problem completely: the more data there is, the more time it requires for the manipulation, and it isn’t always possible as for functionality and design.

2. Disconnect the unnecessary (currently invisible) branches of the app from the DOM main tree, which repaints.

Pros: it increases the speed of rendering and makes it easier to manipulate the DOM main branch. The DOM-branches under manipulation are not deleted; that’s why they are not recreated while they are being attached or detached. JavaScript code already contains links to DOM branches.

Cons: requires JavaScript implementation.

3. Use classic patterns to represent a large number of “heavy” visual information, for example, you can use Flyweight + factory (adapter) – as it is done in existing mobile platforms.

Pros: it is the best solution to the issue of performance.

Cons: very costly development concerning time resources. Modern UI frameworks implement only the visual design, sometimes including mobile GPU acceleration for faster animation. So we started to develop our own solutions to implement complex UI-patterns for mobile devices (e.g. infinite scroll, view pager or tile set) that are able to work with real data. A good example is our developer toolkit RAD.js.

4. Create page content using templates each time you navigate to another app page.

Pros: it almost solves the problem.

Cons: it adds a delay in template rendering during every transition to another page; because the more complex it is, the longer it takes.
Each of these approaches has the right to exist. But each one has pros and cons, so consider them and select according to the requirements in each particular case.

In most cases we choose the 2nd option – the one with the implementation of detachment and attachment of DOM branches. It is easy and you can see it from the code sample. This.el is a DOM pointer we work with.

 

There’s no magic – you just use append child and remove child, that’s it. The DOM element is not released by Garbage Collector because there is an external link to it in JavaScript. That is why frequent connection and disconnection of the branches to the DOM doesn’t take much time. This is very important – it reduces required time at subsequent showing and hiding of elements.

Now a question: how can we optimize an already existing project?

The following illustrations show two variants of using attaching/detaching approach. On the left you can see loading content to an empty container; on the right – when you already have loaded DOM. You can detach the invisible from the user DOM. A common mistake is frequent use of custom events in DOM.

 

#3. Custom DOM Events

A few words about custom events in DOM. They are used in many polyfills for mobile devices – they are heavyweight, so try not to use them too often. During the event distribution in the DOM tree, the browser has to use 2 mechanisms (JavaScript and DOM) together, which takes additional time to synchronize them. It leads to delays.

Also you should note that frequent use of custom DOM events may activate the Garbage Collector which stops all the processes in the browser.

If you need to transfer data between the JavaScript modules of the application regularly or frequently – use the object pool pattern and transmission of JavaScript objects rather than a custom DOM event.

 

#4. Cache Everything

You should save references to the DOM nodes you constantly work with in JavaScript variables, so to not waste time on querying.

#5. Keep It Simple, Keep It Flat (Rendering)

The simpler the visual style of the app is, the better performance and responsiveness it has. Maybe this is the main reason of the latest trends in design, for example, flat style, or material design by Google. These approaches hardly use rich design with shadows or half-opacity. It simplifies the rasterization algorithm of visualization and reduces the load on the mobile processor. The problem is when the app tries to render a part of shadow or opacity, it has to know the fill color and the color of the background, then the overlay algorithm — it all leads to excessive calculations.

#6. GPU Animation (Rendering)

Most modern mobile devices have Graphic Processor Units (GPUs). Their presence allows you to speed up animations of the apps’ visual parts, because it translates your DOM-branches into textures in GPU.

 

Above you can see several typical approaches to transferring element rendering into GPU. Typically, we use the first two. Unfortunately, their efficiency depends on the mobile platform. For example, the second approach does not work on iOS 6. Sad but true.

 

Here you can encounter the following issues:

  1. In CPU we operate DOM objects, in GPU – textures. It means that you can’t manipulate the last ones. You need additional time to transfer changed DOM structures to GPU textures.
  2. It takes time because it requires additional rendering in the device memory.
  3. Thus memory consumption grows.
  4. Try to avoid accidental transition of the layer to GPU. It can happen when a layer untransferred to GPU overlaps with a transferred one, so you get lags. Meanwhile the first layer will be automatically transferred to the GPU before the animation and brought back afterwards.
  5. It is impossible to speed up everything: for example, items which contain rapidly changing content, instead of acceleration lead to lags due to the 2nd and 3rd reasons.

So be careful and make sure that:

  • There’s no changing data during the CSS animation in accelerated layers because it requires additional rendering.
  • There are no layers not accelerated in the GPU, which partially overlap the accelerated ones — it will cause lags.

#7. Keep Smart CSS Layers (Rendering)

Tip: markup your app so that the frequently changed data is divided into separate smaller layers.
You may also place static content elements on the page (for example, header and footer – it will not affect the result).

You should consider huge amount of DOM methods lead to reflow. And sometimes it happens with the recalculation of the whole page. But in some cases (for example, when you use position: absolute or fixed) it leads to recalculation of small separate layers only. The rest of the page will not be touched. This mechanism is important for optimization, so let’s consider it in detail.

How exactly does the reflow happen? There are 4 steps:

At the same time, if you use a ”float’ in your CSS for shifting from calculation thread, it takes more time then ”position: absolute or fixed” cases – because the main thread also should be recalculated in the first place. Only afterwards the float element will be reflowed.

So if your application page elements will be animated or have frequently changing content, the most effective way is to put this functionality in a separate CSS layer by assigning to them position: absolute.

#8. Image Scaling (Rendering)

Don’t scale images! It’s better to make an image in several sizes beforehand.

Sometimes developers simply forget about it. But every change means a recalculation at the expense of processing power and time, because most browsers perform all operations in a single thread. We can’t waste our time on additional calculations when we develop apps for mobile devices.

For example, on iOS 7 at 1.3-x, scaling of an image with 500px width takes almost 10 milliseconds on the simulator (on a real device, of course, less time – but it is still a indicator). This is more than half of the animation frame budget. For example, if you have a list of pictures, you won’t be able to get a smooth animation owing to this.

#9. Give It Some Rest (Browser Execution Flow)

If you have a huge undivided flow part, try to use fragmentation of code into smaller operations. Then run them through the setTimeout () or requestAnimationFrame (). This will allow the browser, which executes the code in your app, to perform critical service operations such as the recalculation in the intervals. In addition, it will smoothen the user experience of your app.

Here is an example of delayed animation. Here we use the left attribute of element BUT the animation is smooth.

Notice that it is recommended to use requestAnimationFrame in cases with animation, because this method is specified for 60 fps. Same for setTimeout, in case you want to split the execution flow. setTimeout has an approximate 4-millisecond delay.

#10. Garbage Collector (Save Memory Patterns)

Try not to create JavaScript objects frequently (in the loop or while returning the results from frequently used functions). The user can see the result as casual small delays in your app — unpredictable and extremely irritable for the user.

Delays happen due to Garbage Collector. It stops all operations in the browser. On mobile devices it works more aggressively than on desktops. So you should consider the specifics of the memory in JavaScript.

 

In real life it looks like this:

Look at the picture. Red examples create garbage during execution. In the top case – by the returned object. In the bottom case – by anonymous functions in every iteration.

Green examples work without unnecessary garbage. In the first example we exchange the creation of a returned object for a permanent object. In the second example we do not use anonymous functions, we use direct references by array index.

In addition we can recommend pool object pattern discussed earlier.

These solutions will help you solve the problem with unpredictable small delays in your apps.

#11. Use The Correct Wrapper! PhoneGap vs. CrossWalk

As you can see from everything mentioned above, the runtime enviroment is very important. That is why you can and you should choose a wrapper for your JavaScript application. At the moment, for example, for Android we have several options. The most famous are PhoneGap by Adobe and CrossWalk by Intel.

How to speed up PhoneGap apps:

  • Simplify your DOM
  • Manipulate the DOM tree
  • Understand the markup layers
  • Simplify your CSS (by not using shadows and gradients)
  • Use hardware acceleration
  • Cache everything
  • Monitor the work of Garbage Collector

A bonus for you: our PhoneGap app development case study.

Want to get in touch?

contact us
Insights

Tips on How We Went Remote With Our Software Development Company

Insights
10 AI And Machine Learning Trends To Impact Business In 2020

10 AI and Machine Learning Trends To Impact Business in 2020

Insights
AI-Based Visual Inspection For Defect Detection1

AI-Based Visual Inspection For Defect Detection