Appendix B. Extending Android development

In this appendix

  • Using WebViews
  • Working with JavaScript
  • Alternative programming languages

For consumers buying a smartphone, one of the appeals of Android is that it doesn’t take a one-size-fits-all approach. You can pick your screen size, form factor, and more. For developers, the story is much the same. Android provides a standard set of development tools: Android SDK/NDK, ADT, and so on. This is great both for getting new developers started, and for standardization on large development teams. But you’re not constrained to this standard set. The ability to do everything from the command line and the use of open build tools such as Ant allows other development environments like IntelliJ IDEA to be used for developing Android applications. You can go even further and eschew Java development completely by using web technologies, or by using alternative programming languages. Let’s take a look at each of these approaches, first by examining web technologies.

B.1. Using WebViews and JavaScript

Occasionally in this book, we’ve mentioned how many aspects of Android development are similar to aspects of web development. Some of these are simple things, such as using the findViewById API as a web developer would use JavaScript’s document. getElementById API. Others have been more substantial, such as the way visual styles are created and applied to UI elements. If your expertise is in web development and you’re making the move to Android development, many things will feel familiar. But if you want to leverage your web development skills to build Android apps, another interesting option is available: WebViews.

Android provides the android.webkit.WebView, a View that shows a web page. As the name implies, it uses the popular open source WebKit library to render web content. WebKit is a C++ library, and you can think of WebView and its related classes as Java wrappers to that library. WebView provides tremendous flexibility in using web content. It’s a View, so you can use make a WebView any size and put multiple WebViews on the screen at the same time. Or you can load a single WebView that uses the entire screen, turning your application into a wrapper around a web page.

The term web page is used loosely here. You could load a page from the Internet, or you could load a local file instead. You could even provide the WebView with a string of HTML to render directly. The flexibility of WebViews make them a powerful tool for Android developers, even those who think that writing JavaScript is for knuckle-draggers.

WebViews are a first-class citizen in Android, not some bolted-on technology used to fill in the gaps of the application framework. You can expose most of the APIs in Android to a web page running inside a WebView. Figure B.1 shows an example app that embeds a WebView.

Figure B.1. Embedded web application

This application allows the user to choose a contact from their address book and a picture from their phone that’s then displayed inside the WebView. It shows you some of the interaction that’s possible between the phone and a web page embedded within a native application using a WebView. The following listing shows how we achieved this kind of interactivity.

Listing B.1. Using a WebView in an Activity

In listing B.1, we start by enabling JavaScript in the WebView that’s embedded in our Activity. We then create a WebChromeClient for the WebView. This object will allow us to intercept certain events that occur in the WebView. In this case, we’ll intercept JavaScript alerts and console logs and route them to the standard Android log. Debugging embedded web pages can be challenging, because you don’t have a standard Java debugger as you do normally in Android development. So you’ll probably need to rely more heavily on logging than you’d like, and the easiest way to do that is to use the WebChromeClient. Next we create a WebViewClient. This is similar to a WebChromeClient, in that it also allows you to intercept other types of events that happen in the WebView—including lifecycle events such as onPageFinished and onReceivedError. In this case, we intercept a new URL being loaded. We could choose to override this. For example, if we wanted to launch an external browser instead of loading the page in the embedded WebView, you could do something like this:

startActivity(new Intent(Intent.ACTION_GET, Uri.parse(url)));

In this case, we’re logging the new URL that’s being loaded. Next comes one of the more interesting steps: we create a Java object and expose it to our JavaScript runtime . We give it the name android, so in JavaScript we’ll be able to refer to it by this name. We’ll look at this object and how it’s used shortly. Finally , we load the web page. In this case, we load a local web page saved in the /assets folder. We could load an external page, but in this case we didn’t want to go to a web server. We wanted to write our application code in HTML, CSS, and JavaScript. The following shows what this Java object does.

Listing B.2. Java object exposed to JavaScript

The idea behind listing B.2 is to show that there are few limits on what kind of Android functionality you can give your JavaScript access to. In this example, we provide access to the user’s name by using the AccountManager . We then also allow the user to select a contact to use for whatever reason . Note that this involves starting another Activity to select this, so it’s an asynchronous request. When we get the result back, we need to send it back to a callback in the JavaScript . To do this, we use (abuse?) the loadUrl method on WebView to execute JavaScript directly and invoke our callback method, passing in the data we got back from the external Activity. Note that we also have a similar method for passing in the file path to a picture, as well as more mundane methods that keep track of some local state in the Activity. Here’s the JavaScript that makes use of this object.

Listing B.3. JavaScript application

The code in listing B.3 shows an application that uses many native features of the Android platform, but does it all from JavaScript on a web page. It has a simple button that says “Select a Contact”; tapping it calls the selectContact method on the Java object in listing B.2. That will invoke the native contact chooser application on the Android device. When it finishes, the contactCallback function will pass the name of the contact back to it. Similarly, you can see functions for interacting with the other methods defined in the Java object shown in listing B.2.

A web application could do a lot of other things too. It could talk to a remote a server using an XMLHttpRequest object. It could use HTML 5 features such as Canvas, DOM storage, or geolocation. And all of the user interface could be done using HTML, CSS, and JavaScript. But on Android it’s also possible to write a little glue code and allow the web application to have access to everything that can be accessed in a native application.

B.2. Alternative programming languages

When Android was first announced, its use of the Java programming language brought with it some mixed reviews. The main alternative in embedded systems is native code—something that can be compiled to machine directly, such as C, C++, or Objective-C. Compared to those languages, Java is advanced. For example, it’s the only one that has garbage collection. (Objective-C 2.0 has a flavor of garbage collection, but it hasn’t yet found its way into the mobile space.) Many developers consider Java to be overly verbose and restrictive, and in general “long in the tooth.” There are many newer programming languages with more flexible and expressive syntax. Fortunately for aficionados of such languages, Android doesn’t require Java. It requires Java bytecode. Any language that can compile to Java bytecode can then be dexed to Dalvik bytecode that can then run on an Android device. Let’s look at figure B.2 to see how this works.

Figure B.2. Compilation and packaging on Android

This figure should look familiar; it’s figure 1.11 from the beginning of the book. The key here is the second rectangle. Here we have Java class files that are then dexed into a .dex file. So we can avoid writing Java code as long as whatever code we write can be converted into Java class files.

As it turns out, the Java runtime (the Java virtual machine) is a popular runtime to target for many newer programming languages. The modern JVM is high performance and provides excellent memory management, hence its popularity with modern programming languages such as Scala, Groovy, Clojure, Mirah, and Fantom. In addition to these young languages, the popular Ruby programming language has a Java-based runtime often referred to as JRuby. So you can add Ruby to the list of programming languages that can be compiled to Java bytecode and used to build Android applications.

As you might be able to guess from figure B.2, there’s usually one tricky point to using something other than Java: the build process. Often your application code needs things such as the R class that’s generated by Android. So that needs to be generated and compiled before your application code that depends on it can be compiled. Once you get this dependency managed, then you can plug into the build process visualized in figure B.2.

Now before you run off and abandon Java in favor of one of these sexier languages, there are some significant drawbacks to using an alternative language—three major drawbacks. The first is that in many cases these languages have constructs that must be translated into Java before being compiled into bytecode. This is true for languages with features that have no equivalent in Java, and often the reason why you want to use these languages is because of such features. With this extra layer of interpretation going on, these languages inevitably run slower than Java. In some cases, this isn’t a big deal. For example, if you have an app where most of the time you’re waiting for data from the network, a slower application probably won’t be noticeable. You should’ve already offloaded such work from the main UI thread.

The next major issue is that alternate languages often require more memory to be allocated than Java would. Again this tends to be more true when you use language features that don’t translate easily to Java. For example, many languages provide closures, anonymous functions that can be passed around to other functions/methods. These are useful in application programming such as in Android, where you must often handle events like taps and gestures. These are often referred to as first-class functions, and Java doesn’t have them. So for a language to provide them, it must usually perform some magic behind the scenes and create a Java object as a container for the function. Using a closure usually involves an object (a class) being defined, compiled, and then an instance of this object being created. That’s a lot of hidden memory being consumed. Even worse, memory used for classes (as opposed to instances of the classes) is much harder for the garbage collector to reclaim. So not only is a lot more memory being allocated, but some of it won’t become garbage that can be collected. Memory is one of the scarce resources in mobile development, so this can be a significant drawback.

The last issue to keep in mind is that alternate programming languages usually come with a runtime library. This is a standard library of classes and functions that you can always count on being there. They usually include common data structures and libraries for I/O and even networking. All of this standard library must be included with your application. This can lead to your application being much larger than you’d otherwise expect. The ProGuard tool described in appendix C can help with this. Furthermore, as Android devices become more advanced, application size becomes less of an issue as users become less likely to run out of space for apps (a common problem in the first year or so of Android). But there’s another frustrating corollary to this issue. The runtime library is most likely packaged as a JAR full of class files. All of these class files will have to be dexed every time you build your application. This can cause your build times to become drastically longer. This might sound like a minor issue, but it can be a significant drag on your development process.

Given all of these potential issues with using an alternate language for Android development, many of these languages have started to include specific support for Android to make things easier for the rapidly growing number of Android developers. For example, the JRuby project has a subproject known as Ruboto that aims to make it easier to use Ruby for building Android apps. Another popular alternative is Scala. The Scala website provides many tips for tweaking the Ant build process to work with Scala. Furthermore, Scala 2.9 includes a tweak to the Scala compiler to make it easier to define static final fields such as the CREATOR fields required by Android Parcelables. There are no static fields in Scala, but some language features are functionally equivalent. But prior to Scala 2.8, the bytecode wasn’t equivalent so there was a compatibility problem. The following listing shows what a Parcelable looks like in Scala.

Listing B.4. A Scala Parcelable
import android.os.{Parcelable, Parcel}

class Stock (val symbol:String, var maxPrice:Double, var minPrice:Double,
         val pricePaid:Double, var quantity:Int, var name:String,
         var currentPrice:Double) extends Parcelable{
    var id = 0

    def this(in:Parcel) = this(in.readString, in.readDouble, in.readDouble,
                   in.readDouble, in.readInt,in.readString,
                   in.readDouble)

    def describeContents = 0
    def writeToParcel(parcel:Parcel, flags:Int){
        parcel.writeString(symbol)
        parcel.writeDouble(maxPrice)
        parcel.writeDouble(minPrice)
        parcel.writeDouble(pricePaid)
        parcel.writeInt(quantity)
        parcel.writeDouble(currentPrice)
        parcel.writeString(name)
    }
}

object Stock{
    final val CREATOR = new Parcelable.Creator[Stock](){
        def createFromParcel(in:Parcel) = new Stock(in)
        def newArray(size:Int) = new Array[Stock](size)
    }
}

As you can see, Scala gets rid of a lot of the boilerplate you need to deal with in Java. But the lack of static members in Scala has its consequences. An object in Scala is a singleton. So its fields and methods can serve the same function as static fields and methods in Java (hence putting the CREATOR in the Stock object instead of the Stock class). As mentioned earlier, this still didn’t work prior to Scala 2.9, and Scala objects couldn’t be used for Parcelables. Fortunately the easy interoperability between Java and Scala meant that you could write your Parcelables in Java and everything else in Scala. But starting with Scala 2.9, the Scala compiler makes the Scala CREATOR bytecode equivalent to the Java static one, enabling you to use Scala for Parcelables.

This is only one example of how alternative languages can be used on Android and are constantly becoming easier to use on Android. These languages often provide different programming paradigms that you can take advantage of. It’s another example of how the openness of Android gives you so many options as a developer. You can use the tools that you want to use. You can use web technologies if you want to. You can choose your programming language.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.82.78