As cross-platform developers, we all know that maintaining speed in a complex codebase is of paramount importance. When you’re adding layers of abstraction to your code in hopes of being able to share large portions of it across disparate platforms, the little steps you have to take to synchronize your common code with the underlying platform specific code can quickly add up to a massive slowdown that leaves you with an application that performs and feels no better than a mobile web application plopped into a WebView.
The Kroll Bridge
Acknowledging the Kroll cost
I’ve prepared a couple of minimal test cases, with built-in timestamping and simple profiling code, so feel free to test these cases on your own devices! For each of these tests, we’ll run some operation 1000 times and take the difference in milliseconds between the start of the operations and the end. Note that this testbed only takes a look at how long different operations tie up the Titanium thread. Many of these tests tie up the UI thread for a substantial amount of time as well, and will cause the app to become unresponsive. The UI thread performance of these tests are outside of the scope of this blog post, I’d like to circle back in a few weeks and take a closer look at the UI impact of these tests as well.
I ran all of my tests on a Nexus 5 running Android 6.0 Marshmallow and an iPhone 5 running iOS 9.1. I ran six trials of each of these tests, and averaged the results. I’ve published the test code on github. Take a look, give it a clone, and follow along on your own devices.
Creation arguments vs Create then set
Titanium provides factory methods to create proxy objects, which allow you to interact with native objects that you may need multiple instances of. Optionally, these methods accept a creation dictionary describing its initial state. Of course, you can just make the proxy and then configure it later, what’s the difference?
On iOS, this behaves largely as expected. Creation with arguments returns a little faster than the creation followed by sets. On Android, however, in addition to being substantially slower, the creation dictionary actually slowed the creation process down!
Sequential updates vs applyProperties
Similarly, Titanium provides both individual property set APIs as well as a bulk application API. Take the following examples:
Oddly enough, we observe the opposite behavior here from view creation! Android performs as expected, with the bulk API yielding better performance. iOS on the other hand runs quite slowly, and the bulk API is slower than the sequential sets.
The table structures in Titanium also provide bulk or individual modification APIs. Consider the following examples:
Finally, an expected result! The bulk population API is massively more performant than the individual population API. Android is still a little slower than iOS, but that is mostly expected.
When flushing the views within a hierarchy, you can either loop over the children array or call removeAllChildren.
Another API that performs differently on iOS vs Android. On iOS, the call to removeAllChildren is almost immediate, whereas on Android the call takes even longer than looping over the entire child list and removing them individually.
Another substantial difference. Backbone events perform consistently (and impressively well!) on both platforms, whereas Titanium events are much slower on iOS, and are a little slower on Android.
The clearest take-aways from these tests are that one needs to be much more careful while modifying the view hierarchy on android, that Ti.App events are quite slow, and that there isn’t a one-size-fits-all performance solution for Titanium. There’s no magic bullet that you can adopt in your codebase and not have to worry about platform-specific performance issues. Android’s slowness when handing creation dictionaries and iOS’s aversion to applyProperties makes it more difficult to write platform agnostic performant code. That being said, in the general case, applyProperties is still worth using, because of the small performance hit we took on iOS and the performance bump we get on Android (which is usually the issue from a performance perspective).
At the end of the day, there’s no substitute for profiling your application and making use of the platform-specific Alloy preprocessor directives (OS_ANDROID, OS_IOS) to deal with platform-specific performance issues. And now that we’re armed with concrete data, we’re ever so slightly better equipped to do that!
I recently took the time to checkout out Appcelerator’s Labs page where they allow users try out pre-release software. There are some interesting projects here, but I spent most of my time experimenting with Hyperloop, which could be the best new feature in Titanium.
I think Hyperloop will be one of the best additions to Appcelerator’s arsenal, but there are some improvements I would like to see before its final release.
In a typical project using Hyperloop, I might write something like this if I needed to require some native Android API’s:
A more complicated example that uses a lot of native API’s could look like this:
This looks a little messy. I would like to see ES6 style destructuring and object matching. That could make the code above look something like this:
This could make the code much more readable as classes from the same package will be grouped together, and var’s with matching names will be created automatically.
I, personally, cannot wait to start using Hyperloop in my daily development here at Shockoe. I think it will not only let me make more powerful applications that harness more native API’s, but it will also save me time when using third party libraries. Fewer native modules means less code to maintain down the line when operating systems and SDK’s are updated.
I think Appcelerator has a great product in development, and with a few improvements, it will be invaluable to the Appcelerator developer community.
I’ve been working with web technologies for over a decade and never had to touch Digest Access Authentication. All of the services I had worked with had other solutions for authentication. A few months back that all changed. I was finishing up a mobile project, to be used for internal purposes for the client, when suddenly the API requirements changed. They had implemented Digest Access Authentication with MD5. Uh-oh.
What is Digest Authentication?
Let’s start small; I certainly had to. Digest Access Authentication is a method for validating a user and granting them some access over HTTP. It involves making a request, being presented with a challenge, answering that challenge with another request, and finally getting the resource originally requested. It works like this:
- Client sends request to the server
- Server responds with a nonce (number to be used once) with a status of 401
- Client makes another request to the server providing identification information
- Server evaluates whether the user is valid and if they are who they say are
- Server responds with desired resource
How do we do this in Titanium?
The idea is to send out requests like normal, check for a 401 status code, and respond to the presented challenge if applicable. That is simple enough:
When I was originally tackling this problem I found a very helpful example on github by rollsroyc3: https://gist.github.com/rollsroyc3/6869880 The majority of the following code is from that example, but I made a few changes.
Before adding any code to actually handle the challenge, let’s take a step back. Assuming you have multiple HTTPClients, like I did, every HTTPClient would need to be rewritten. Instead, let’s encourage code reuse and turn this into a commonJS lib that wraps the HTTPClient. Then we can have one HTTPClient that does Digest Authentication when it encounters a 401 status code and acts normally with all others:
(I use underscore.js a bit here, and you should too)
We can then require this in and use it:
This is just one example of how it can be accomplished. Your needs may be different. There are many more options available to the HTTPClient that are not exposed (like headers). This was a quick and dirty solution to a last minute problem.
If I needed the library today for a new project I would modify it to make use of Backbone events. Imagine different components being notified when a new request has been made, when that request has been met with Digest Access Authentication, and when the response is finally successful! Evented networks are both a blessing and a curse. Allowing various controllers to listen for updates to data is amazing, but if you’re not careful with cleanup then you’re begging for memory leaks.