Learning Android
Beijing•Cambridge•Farnham•Köln•Sebastopol•TokyoLearning AndroidMarko Gargenta
Learning Androidby Marko GargentaCopyright © 2011 Marko Gargenta. All rights in the United States of by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA ’Reilly books may be purchased for educational, business, or sales promotional use. Online editionsare also available for most titles (). For more information, contact ourcorporate/institutional sales department: (800) 998-9938 or corporate@:Andy Oram and Brian JepsonIndexer:Jay MarchandProduction Editor:Holly BauerCover Designer:Karen MontgomeryCopyeditor:Genevieve d’EntremontInterior Designer:David FutatoProofreader:Jennifer KnightIllustrator:Robert RomanoPrinting History:March 2011:First Edition. Nutshell Handbook, the Nutshell Handbook logo, and the O’Reilly logo are registered trademarks ofO’Reilly Media, Inc. Learning Android, the image of a Little Owl, and related trade dress are trademarksof O’Reilly Media, of the designations used by manufacturers and sellers to distinguish their products are claimed astrademarks. Where those designations appear in this book, and O’Reilly Media, Inc., was aware of atrademark claim, the designations have been printed in caps or initial every precaution has been taken in the preparation of this book, the publisher and author assumeno responsibility for errors or omissions, or for damages resulting from the use of the information con-tained : 978-1-449-39050-1[LSI]1299702297
Table of ContentsPreface .................................................................... Overview ....................................................... 1Android Overview1Comprehensive1Open Source Platform2Designed for Mobile Devices2History3Google’s Motivation3Open Handset Alliance4Android Stack .............................................................. 7Stack Overview7Linux7Portability7Security8Features8Native Libraries9Dalvik9Android and Java10Application Framework11Applications12The APK12Application Signing12Application Start ............................................................ 15Installing the Android SDK15v
Setting Up a PATH to Tools16Installing Eclipse16Eclipse Workspace17Setting Up Android Development Tools17Hello, World18Creating a New Project18Manifest File20Layout XML Code21Strings21The R File22Java Source Code22The Emulator23An Emulator Versus a Physical Building Blocks ................................................... 27What Are Main Building Blocks?27A Real-World Example27Activities28Activity Life Cycle28Intents31Services31Content Providers32Broadcast Receivers34Application Project Overview ................................................ 37The Yamba Application37Design Philosophy39Project Design39Part 1: Android User Interface39Building an Activity40Networking and Multithreading41Debugging Android Apps41Part 2: Preferences, Filesystem, Options Menu, and Intents41The Activity41Menu System and Intents42Filesystem42Part 3: Android Services42Services42Application Object42Part 4: Working with Databases42vi|Table of Contents
SQLite and Android’s Support for It42Refactoring the Code Again43Part 5: Lists and Adapters43Timeline Activity43More Refactoring?43Part 6: Broadcast Receivers43Boot and Network Receivers44Timeline Receiver44Permissions44Part 7: Content Providers44Status Data44Android Widgets44Part 8: System Services45Compass and Location45Intent Service, Alarms, and User Interface .................................................. 47Two Ways to Create a User Interface47Declarative User Interface47Programmatic User Interface48The Best of Both Worlds48Views and Layouts48LinearLayout49TableLayout50FrameLayout50RelativeLayout50AbsoluteLayout50Starting the Yamba Project51The StatusActivity Layout52Important Widget Properties54Strings Resource55The StatusActivity Java Class56Creating Your Application-Specific Object and Initialization Code56Compiling Code and Building Your Projects: Saving Files59Adding the Library59Updating the Manifest File for Internet Permission61Logging in Android62LogCat62Threading in Android65Single Thread65Multithreaded Execution66AsyncTask67Table of Contents|vii
Other UI Events70Adding Color and Graphics74Adding Images74Adding Color76Alternative Resources79Optimizing the User Interface80Hierarchy , the Filesystem, the Options Menu, and Intents .................. 83Preferences83Prefs Resource84PrefsActivity87Update the Manifest File88The Options Menu89The Menu Resource89Android System Resources90Update StatusActivity to Load the Menu91Update StatusActivity to Handle Menu Events92Strings Resource92Shared Preferences93The Filesystem Explained95Exploring the Filesystem95Filesystem Partitions96System Partition96SDCard Partition96The User Data Partition97Filesystem ............................................................. 101The Yamba Application Object102The YambaApplication Class102Update the Manifest File104Simplifying StatusActivity105UpdaterService105Creating the UpdaterService Java Class106Update the Manifest File107Add Menu Items108Update the Options Menu Handling109Testing the Service109Looping in the Service110Testing the Service113viii|Table of Contents
Pulling Data from Twitter113Testing the Database ........................................................ 119About SQLite119DbHelper120The Database Schema and Its Creation120Four Major Operations121Cursors122First Example122Update UpdaterService124Testing the Service127Database Constraints129Refactoring Status and Adapters .................................................... 137TimelineActivity137Basic TimelineActivity Layout138Introducing ScrollView138Creating the TimelineActivity Class139About Adapters142Adding a ListView to TimelineActivity142Creating a Row Layout143Creating an Adapter in : A Better Alternative to TimelineAdapter149Updating the Manifest File150Initial App Setup152Base Activity153Toggle Receivers ................................................... 161About Broadcast Receivers161BootReceiver162Registering the BootReceiver with the AndroidManifest File162Testing the Boot Receiver163The TimelineReceiver163Broadcasting Intents165The Network Receiver167Adding Custom Permissions to Send and Receive Broadcasts169Table of Contents|ix
Declaring Permissions in the Manifest File170Updating the Services to Enforce Permissions171Updating TimelineReceiver to Enforce Providers ..................................................... 175Creating a Content Provider175Defining the URI176Inserting Data177Updating Data178Deleting Data179Querying Data179Getting the Data Type180Updating the Android Manifest File181Using Content Providers Through Widgets181Implementing the YambaWidget class182Creating the XML Layout185Creating the AppWidgetProviderInfo File185Updating the Manifest File186Testing the Services ...................................................... 189Compass Demo189Common Steps in Using System Services190Getting Updates from the Compass190Compass Main Activity191Custom Rose Widget194Location Service195Where Am I? Demo196Updating Yamba to Use the Location Service200Updating Our Preferences200Updating the Yamba Application201Updating the Status Activity202Intent Service206Alarms208Adding an Interval to Preferences209Updating BootReceiver210Sending Android Interface Definition Language ............................... 215Implementing the Remote Service215x|Table of Contents
Writing the AIDL216Implementing the Service217Implementing a Parcel218Registering with the Manifest File220Implementing the Remote Client221Binding to the Remote Service221Testing That It All Native Development Kit (NDK) ....................................... 227What Is and Isn’t the NDK For?227Problems Solved by the NDK227The Toolchain228Packaging Your Libs228Documentation and Standardized Headers228An NDK Example: Fibonacci229FibLib229The JNI Header File231C Implementation232The Makefile234Building the Shared Library234The Fibonacci Activity235Testing That It All Works236Summary237Index ..................................................................... 239Table of Contents|xi
PrefaceThis book sprang from years of delivering the Marakana Android Bootcamp trainingclass to thousands of software developers at some of the largest mobile companieslocated on four continents around the world. Teaching this class, over time I saw whatworks and what doesn’t. This book is a distilled version of the Android Bootcamptraining course that I developed at Marakana and fine-tuned over background is in Java from back before it was even called that. From the beginning,I was very interested in embedded development as a way to program various devicesthat surround us in everyday life. Because Java primarily took off in web applicationdevelopment, most of my experience in the previous decade has been in building largeenterprise systems. Then Android arrived, and once again I became very excited aboutbuilding software for nontraditional computers. My current interests lie in using An-droid on devices that may not even resemble a typical book teaches anyone who knows Java (or a similar language) how to develop areasonably complex Android application. I hope you find this book fairly comprehen-sive and that you find the example-based learning reasonably motivating. The goal ofLearning Android is to get you to think in Android ’s InsideChapter 1, Android OverviewIs an introduction to Android and its historyChapter 2, The StackIs an overview of the Android operating system and all its parts from a very highlevelChapter 3, Quick StartHelps you set up your environment for Android application developmentChapter 4, Main Building BlocksExplains the Android components application developers use to put together anappxiii
Chapter 5, Yamba Project OverviewExplains the Yamba application that we’ll build together through this book anduse as an example to learn Android’s various featuresChapter 6, Android User InterfaceExplains how to build the user interface for your applicationChapter 7, Preferences, the Filesystem, the Options Menu, and IntentsCovers some of the operating system features that make an application developer’slife easierChapter 8, ServicesCovers building an Android service to process background tasksChapter 9, The DatabaseExplains the Android framework’s support for the built-in SQLite database andhow to use it to persist the data in your own applicationChapter 10, Lists and AdaptersCovers an important feature of Android that allows large data sets to be linkedefficiently to relatively small screensChapter 11, Broadcast ReceiversExplains how to use the publish-subscribe mechanism in Android to respond tovarious system and user-defined messagesChapter 12, Content ProvidersShows how to design a content provider to share data between applications, in thiscase using it to enable our app widget to display data on the home screenChapter 13, System ServicesIntroduces various system services that an app developer can tap intoChapter 14, The Android Interface Definition LanguageCovers building an inter-process communication mechanism to allow for remoteaccess to a service from another applicationChapter 15, The Native Development Kit (NDK)Introduces how to write native C code as part of your Android applicationConventions Used in This BookThe following typographical conventions are used in this book:ItalicIndicates new terms, URLs, email addresses, filenames, and file widthUsed for program listings, as well as within paragraphs to refer to program elementssuch as variable or function names, data types, and XML width boldShows commands or other text that should be typed literally by the |Preface
Constant width italicShows text that should be replaced with user-supplied values or by values deter-mined by icon signifies a tip, suggestion, or general icon indicates a warning or Code ExamplesThis book is here to help you get your job done. In general, you may use the code inthis book in your programs and documentation. You do not need to contact us forpermission unless you’re reproducing a significant portion of the code. For example,writing a program that uses several chunks of code from this book does not requirepermission. Selling or distributing a CD-ROM of examples from O’Reilly books doesrequire permission. Answering a question by citing this book and quoting examplecode does not require permission. Incorporating a significant amount of example codefrom this book into your product’s documentation does require appreciate, but do not require, attribution. An attribution usually includes the title,author, publisher, and ISBN. For example: “Learning Android by Marko Gargenta(O’Reilly). Copyright 2011 Marko Gargenta, 978-1-449-39050-1.”If you feel your use of code examples falls outside fair use or the permission given here,feel free to contact us at permissions@® Books OnlineSafari Books Online is an on-demand digital library that lets you easilysearch over 7,500 technology and creative reference books and videos tofind the answers you need a subscription, you can read any page and watch any video from our library books on your cell phone and mobile devices. Access new titles before they areavailable for print, get exclusive access to manuscripts in development, and post feed-back for the authors. Copy and paste code samples, organize your favorites, downloadchapters, bookmark key sections, create notes, print out pages, and benefit from tonsof other time-saving |xv
O’Reilly Media has uploaded this book to the Safari Books Online service. To have fulldigital access to this book and others on similar topics from O’Reilly and other pub-lishers, sign up for free at to Contact UsPlease address comments and questions concerning this book to the publisher:O’Reilly Media, Gravenstein Highway NorthSebastopol, CA 95472800-998-9938 (in the United States or Canada)707-829-0515 (international or local)707 829-0104 (fax)We have a web page for this book, where we list errata, examples, and any additionalinformation. You can access this page at: comment or ask technical questions about this book, send email to:bookquestions@ more information about our books, courses, conferences, and news, see our websiteat us on Facebook: us on Twitter: us on YouTube: book is truly a result of outstanding teamwork. First, I’d like to thank my editorsat O’Reilly, Andy Oram and Brian Jepson. Andy, your comments were spot-on andconstructive. Brian, thank you for persuading me to take on writing this book in thefirst would like to thank all my technical editors: Dan Bornstein, Hervé Guihot, FrankMaker III, and Bill Schrickel. Thank you for diligently reading my half-baked drafts andproviding valuable book wouldn’t be what it is without field testing it on our numerous clients. Youwere the true pioneers on the cutting edge of Android, and your projects are all veryinspiring. Thank you for your |Preface
I’d like to thank my team at Marakana—Aleksandar (Saša) Gargenta, Ken Jones, andLaurent Tonon—for bringing back firsthand feedback from teaching Android Boot-camp courses using the draft of this book. Saša, special thanks to you for sending meback to the drawing board more times than I’d like to admit. This book is probablymonths past due because of your in-depth technical finally, a huge thanks to my wife, Lisa, and daughter, Kylie. I know what a sacrificeit was for you while I was crisscrossing the world working on this material. Thank youfor supporting me along the |xvii
CHAPTER 1Android OverviewIn this chapter, you will learn how Android came about. We’ll take a look at its historyto help us understand its future. As this mobile environment enters a make-or-breakyear, we look at the key players in this ecosystem, what motivates them, and whatstrengths and weaknesses they bring to the the end of this chapter, you will better understand the ecosystem from a businesspoint of view, which should help clarify the technology choices and how they relate tolong-term advantages for various OverviewAndroid is a comprehensive open source platform designed for mobile devices. It ischampioned by Google and owned by Open Handset Alliance. The goal of the allianceis to “accelerate innovation in mobile and offer consumers a richer, less expensive, andbetter mobile experience.” Android is the vehicle to do such, Android is revolutionizing the mobile space. For the first time, it is a trulyopen platform that separates the hardware from the software that runs on it. This allowsfor a much larger number of devices to run the same applications and creates a muchricher ecosystem for developers and ’s break down some of these buzz words and see what’s behind is a comprehensive platform, which means it is a complete software stack fora mobile developers, Android provides all the tools and frameworks for developing mobileapps quickly and easily. The Android SDK is all you need to start developing for An-droid; you don’t even need a physical
For users, Android just works right out of the box. Additionally, users can customizetheir phone experience manufacturers, it is the complete solution for running their devices. Other thansome hardware-specific drivers, Android provides everything else to make their Source PlatformAndroid is an open source platform. The entire stack, from low-level Linux modulesall the way to native libraries, and from the application framework to complete appli-cations, is totally so, Android is licensed under business-friendly licenses (Apache/MIT) so thatothers can freely extend it and use it for variety of purposes. Even some third-partyopen source libraries that were brought into the Android stack were rewritten undernew license , as a developer, you have access to the entire platform source code. This allows youto see how the guts of the Android operating system work. As manufacturer, you caneasily port Android OS to your specific hardware. You can also add your own propri-etary secret sauce, and you do not have to push it back to the development communityif you don’t want ’s no need to license Android. You can start using it and modifying it today, andthere are no strings attached. More so, Android has many hooks at various levels of theplatform, allowing anyone to extend it in unforeseen are couple of minor low-level pieces of code that are proprietaryto each vendor, such as the software stack for the cellular, WiFi, andBluetooth radios. Android tries hard to abstract those components withinterfaces so that vendor-specific code can be managed for Mobile DevicesAndroid is a purpose-built platform for mobile devices. When designing Android, theteam looked at which mobile device constraints likely were not going to change for theforeseeable future. For one, mobile devices are battery powered, and battery perform-ance likely is not going to get much better any time soon. Second, the small size ofmobile devices means that they will always be limited in terms of memory and constraints were taken into consideration from the get-go and were addressedthroughout the platform. The result is an overall better user was designed to run on all sorts of physical devices. Android doesn’t makeany assumptions about a device’s screen size, resolution, chipset, and so on. Its core isdesigned to be |Chapter 1: Android Overview
HistoryThe history of Android is interesting and offers some perspective on what the futuremight are the key events of the past few years:•In 2005, Google buys Android, Inc. The world thinks a “gPhone” is about to comeout.•Everything goes quiet for a while.•In 2007, the Open Handset Alliance is announced. Android is officially opensourced.•In 2008, the Android SDK is released. The G1 phone, manufactured by HTCand sold by the wireless carrier T-Mobile USA, follows shortly afterward.•2009 sees a proliferation of Android-based devices. New versions of the operatingsystem are released: Cupcake (), Donut (), and Eclair ( and ). Morethan 20 devices run Android.•In 2010, Android is second only to Blackberry as the best-selling smart phoneplatform. Froyo (Android ) is released and so are more than 60 devices thatrun 2005, when Google purchased Android, Inc., the world thought Google was aboutto enter the smart phone market, and there were widespread speculations about a de-vice called the ’s CEO, Eric Schmidt, made it clear right away that Android’s ambitions weremuch larger than a single phone. Instead, they envisioned a platform that would enablemany phones and other ’s MotivationGoogle’s motivation for supporting the Android project seems to be having Androideverywhere and by doing that, creating a level playing field for mobile devices. Ulti-mately, Google is a media company, and its business model is based on selling adver-tising. If everyone is using Android, then Google can provide additional services on topof it and compete fairly. This is unlike the business models of other software vendorswho depend on licensing Google does license some proprietary apps, such as Gmail and Maps, andmakes some money off the Android market, its primary motivation is still the adver-tising revenue that those apps bring |3
Open Handset AllianceFor this to be bigger than just Google, Android is owned by the Open Handset Alliance,a nonprofit group formed by key mobile operators, manufacturers, carriers, and alliance is committed to openness and innovation for the mobile user practice, the alliance is still very young and many members are still learning to workwith each other. Google happens to be putting the most muscle behind the Androidproject at the first version of the Android SDK was released without an actualphone on the market. The point of this is that you don’t really need aphone for Android development. There are some exceptions (hardwaresensors, telephony, etc.), but for the most part the Android SDK con-tains everything you’ll need for developing on this VersionsLike any software, Android is improved over time, which is reflected in its versionnumbers. However, the relationship between different version numbers can be con-fusing. Table 1-1 helps explain 1-1. Android versions through Android versionAPI levelNicknameAndroid (frozen yogurt)Android Android version number itself partly tells the story of the software platform’s majorand minor releases. What is most important is the API level. Version numbers changeall the time, sometimes because the APIs have changed, and other times because ofminor bug fixes or performance |Chapter 1: Android Overview
As application developers, you will want to make sure you know which API level yourapplication is targeting in order to run. That API level will determine which devices canand cannot run your your objective is to have your application run on as many devices as , with that in mind, try to shoot for an API level that is as low as possible. Keep inmind the distribution of Android versions on real devices out there. Figure 1-1 showsa snapshot of the Android Device Dashboard from 1-1. Historical Android version distribution through January 2011You may notice that there are not a lot of users of Android and . You may alsonotice that not a lot of users have the latest and greatest Android , but the numberof users is growing. This is because everyone with and got upgraded overthe air (OTA) automatically to . On the other hand, users who still have deviceswith Android and likely will never be able to upgrade to versions. Theirolder devices do not have the relevant firmware, and most manufacturers are not plan-ning on releasing firmware upgrades as they are busy working on new that in mind, you will probably choose or as your minimum developmenttarget, unless you truly need the features of the latest Android operating system was designed from the ground up to be a comprehensiveopen source platform for mobile devices. It is a game-changer in the industry and hasenjoyed great the next chapter, we’ll take a look at the entire Android operating system at a highlevel to gain a technical understanding of how all the pieces fit |5
CHAPTER 2The StackThis is the 9,000-foot overview of the Android platform. Although you’re concernedprimarily with writing Android applications, understanding the layout of the systemwill help shape your understanding about what you can or cannot do easily the end of this chapter, you’ll understand how the whole system works, at least fromthe high level. Stack OverviewThe Android operating system is like a cake consisting of various layers. Each layer hasits own characteristics and purpose. The layers are not cleanly separated but often seepinto each you read through this chapter, keep in mind that I am concerned only with thebig picture of the entire system and will get into the nitty-gritty details later on. Fig-ure 2-1 shows the parts of the Android is built on top of Linux. Linux is a great operating system and the poster childof open source. There are many good reasons for choosing Linux as the base of theAndroid stack. Some of the main ones are its portability, security, and is a portable platform that is relatively easy to compile on various hardwarearchitectures. What Linux brings to Android is a level of hardware abstractions. Bybasing Android on Linux, we don’t have to worry too much about underlying hardwarefeatures. Most low-level parts of Linux have been written in fairly portable C code,which allows for third parties to port Android to a variety of from Wow! eBook <>
Figure 2-1. Android stackSecurityLinux is a highly secure system, having been tried and tested through some very harshenvironments over the decades. Android heavily relies on Linux for security. All An-droid applications run as separate Linux processes with permissions set by the Linuxsystem. As such, Android passes many security concerns to the underlying comes with a lot of very useful features. Android leverages many of them, suchas support for memory management, power management, and |Chapter 2: The Stack
Native LibrariesThe native libraries are C/C++ libraries, often taken from the open source communityin order to provide necessary services to the Android application layer. Among others,they include:WebkitA fast web-rendering engine used by Safari, Chrome, and other browsersSQLiteA full-featured SQL databaseApache HarmonyAn open source implementation of JavaOpenGL3D graphics librariesOpenSSLThe secure locket layerAlthough many of these libraries are used as-is, one notable exception is Bionic, whichis basically a rewritten version of the standard C library. Bionic is used for two reasons:TechnologyTo make it purpose-built for tiny, battery-powered devicesLicenseTo make it license-friendly for others who might want to adopt it and change itGNU libc, the default C library for Linux, is licensed under a GPL li-cense, which requires any changes that you release publicly to be pushedback to the open source community. As such, it might not be the mostbusiness-friendly open source license when a company wants to keeptheir derivative work proprietary. Bionic, on the other hand, is licensedunder an Apache/MIT license, which doesn’t require derivative worksto be open is a purpose-built virtual machine designed specifically for Android, developedby Dan Bornstein and his team at Java virtual machine (VM) was designed to be a one-size-fits-all solution, and theDalvik team felt they could do a better job by focusing strictly on mobile devices. Theylooked at which constraints specific to a mobile environment are least likely to changein the near future. One of these is battery life, and the other is processing power. Dalvikwas built from the ground up to address those |9
Another side effect of replacing the Java VM with the Dalvik VM is the the Java language, Java tools, and Java libraries are free, the Java virtual ma-chine is not. This was more of an issue back in 2005 when the work on Dalvik , there are open source alternatives to Sun’s Java VM, namely theOpenJDK and Apache Harmony developing a truly open source and license-friendly virtual machine, Android yetagain provides a full-featured platform that others are encouraged to adopt for a varietyof devices without having to worry about the and JavaIn Java, you write your Java source file, compile it into a Java byte code using the Javacompiler, and then run this byte code on the Java VM. In Android, things are still write the Java source file, and you still compile it to Java byte code using thesame Java compiler. But at that point, you recompile it once again using the Dalvikcompiler to Dalvik byte code. It is this Dalvik byte code that is then executed on theDalvik VM. Figure 2-2 illustrates this comparison between standard Java (on the left)in Android using Dalvik (on the right).Figure 2-2. Java versus Dalvik10|Chapter 2: The Stack
It might sound like you have to do a lot more work with Android whenit comes to Java. However, all these compilation steps are automatedby tools such as Eclipse or Ant, and you never notice the may wonder, why not compile straight from Java into the Dalvik byte code? Thereare a couple of good reasons for the extra steps. Back in 2005, when work on Dalvikstarted, the Java language was going through frequent changes, but the Java byte codewas more or less set in stone. So, the Android team chose to base Dalvik on Java bytecode instead of Java source side effect of this is that in theory you could write Android applications in any otherlanguage that compiles down to Java byte code. For example, you could use Python orRuby. I say “in theory” because in practice the appropriate libraries that are part of theSDK would need to be available. But it is likely that the open source community willcome up with a solution to that in the thing to keep in mind is that Android Java is a nonstandard collection of Javaclasses. Java typically ships in:Java Standard EditionUsed for development on basic desktop-type applicationsJava Enterprise Edition (aka J2EE or JavaEE)Used for development of enterprise applicationsJava Micro Edition (aka J2ME or JavaME)Java for mobile applicationsAndroid’s Java set of libraries is closest to Java Standard Edition. The major differenceis that Java user interface libraries (AWT and Swing) have been taken out and replacedwith Android-specific user interface libraries. Android also adds quite a few new fea-tures to standard Java while supporting most of Java’s standard features. So, you havemost of your favorite Java libraries at your disposal, plus many new FrameworkThe application framework is a rich environment that provides numerous services tohelp you, the app developer, get your job done. This is the best-documented and mostextensively covered part of the platform because it is this layer that empowers devel-opers to get creative and bring fantastic applications to the the application framework layer, you will find numerous Java libraries specificallybuilt for Android. You will also find many services (or managers) that provide the eco-system of capabilities your application can tap into, such as location, sensors, WiFi,telephony, and so Framework|11
As you explore Android application development, most of your focus will be on thispart of the stack, and you will get to use many of the application finally, there are the applications that you and other developers create. Theseapplications are what end users find valuable about Android. They can come prein-stalled on the device or can be downloaded from one of the many Android APKAn application is a single application package (APK) file. An APK file roughly has threemain components. An API consists of the following major components:Dalvik executableThis is all your Java source code compiled down to a Dalvik executable. This is thecode that runs your are everything that is not code. Your application may contain a numberof images and audio/video clips, as well as numerous XML files describing layouts,language packs, and so on. Collectively, these items are the librariesOptionally, your application may include some native code, such as C/C++ li-braries. These libraries could be packaged together with your APK SigningAndroid applications must be signed before they can be installed on a device. For de-velopment purposes, we’ll be signing our example applications with a debug key—akey that you already have on your development platform. However, when you distrib-ute your application commercially, you’ll want to sign it with your own key. TheAndroid developer document titled “Signing Your Application” has the DistributionOne way in which Android is quite different from other platforms is the distributionof its apps. On most other platforms, such as iPhone, a single vendor holds a monopolyover the distribution of applications. On Android, there are many different stores, ormarkets. Each market has its own set of policies with respect to what is allowed, howthe revenue is split, and so on. As such, Android is much more of a free market spacein which vendors compete for |Chapter 2: The Stack
In practice, the biggest market currently is Android Market, run by Google. It is unclearwhether Google means to just seed the market space while other stores develop or plansto make it a profitable can also be distributed via the Web. When you download an APK filefrom a website through the browser, the application represented by the APK file isinstalled automatically on your about viruses, malware, spyware, and other bad things?With its decentralized application distribution system, it is certainly possible for anunsuspecting user to download a malicious app that consequently does bad things. Forexample, there have been reports of phishing attacks via fake banking , Android leaves it to the marketplace to sort it out. Eventually, there will be storesthat are more reputable and those that are less so, at least in theory. Google relies onuser reports for policing its Android Market, but other stores may choose to do moreproactive testing and raise the bar on what gets into the store in the first this chapter, you got a big-picture overview of what comprises the Android operatingsystem and how its various pieces fit together. You now understand what makes An-droid so complete, open, and attractive to the next chapter, we’ll look at how to set up your development environment so youcan get up to speed quickly. We’ll also look at a simple Hello World application anddissect it to help you understand the various pieces of an Android |13
CHAPTER 3Quick StartIn this chapter, you will learn how to set up your environment for Android develop-ment. I’ll go beyond just listing where you can download the software, and will coversome of the best practices in getting set up. I’ll look at development operating systemchoices as well as the Android tools available. You will see the good, the bad, and theugly of the various tool and platform choices that you’re about to make (or that some-one else has already made for you).By the end of this chapter, you will have your entire development environment set ’ll be able to write a Hello World application, build it, and run it on the emulator(or a physical device, if you want).I’m going to use ~ to refer to your home directory. On Mac OS X, that’stypically something like /Users/marko. On Linux, it would be /home/marko, and on Windows Vista and 7, C:\Users\marko (in Windows XP,it would be C:\Documents and Settings\marko). Also, I’m going to useUnix-style forward slashes and not Windows backslashes to denote filepath , if you’re on Windows, just change ~ to C:\Users\YourUserNameand / to \. Other than that, everything should be pretty much for differentoperating systems, regardless of whether you use OS X, Linux, the Android SDKThe Android Software Development Kit (SDK) is all you need to develop applicationsfor Android. The SDK comes with a set of tools as well as a platform to run it and seeit all work. You can download the Android SDK for your particular platform from theAndroid SDK Download
Once you download it, unzip (or on Linux, untar) it into a folder that is easy to get examples in the book will assume your SDK is in the folder ~/android-sdk. Ifit’s in a different location, use that location instead of ~/android-sdk. For example:WindowsC:\apps\android-sdk-windowsLinux/home/YourUserName/android-sdk-linux_86Mac OS X/Users/YourUserName/android-sdk-mac_86For Windows users, I strongly recommend choosing directories withoutspaces in them. This is because we’ll be doing work on the commandline and spaces just complicate things. Because the Windows XP homedirectory is in C:\Documents and Settings, I would recommend puttingandroid-sdk in a top-level directory that you create, such as C:\, on Windows Vista or 7, you can simply extract android-sdkinto C:\Users\ Up a PATH to ToolsThe Android SDK has a folder that contains all its major tools. Since we’re going to usethese tools from the command line, it is very helpful to add your ~/android-sdk/tools/and your ~/android-skd/platform-tools/ directories to your system PATH variable. Thiswill make it easier to access your tools without having to navigate to their specificlocation every single for setting up the PATH variable depend on the platform; see step 2 of the docu-ment “Installing Android SDK”.Installing EclipseEclipse is an open source collection of programming tools originally created by IBMfor Java. Nowadays, most developers in the Java community favor Eclipse as their Integrated Development Environment (IDE) of choice. Eclipse lives at has a lot of time-saving features, which I’ll be pointing out as we continue. Keepin mind that, although powerful, Eclipse tends to be very resource-hungry, and so youmight want to restart it once a day if it starts running you can do Android development with any favorite text editor or integrateddevelopment environment (IDE), most developers seem to be using Eclipse, and thusthat’s what I use in this |Chapter 3: Quick Start
If you choose not to use Eclipse, please refer to “Developing in OtherIDEs”.Download Eclipse at I recommend Eclipse IDE forJava Developers (not the twice-as-large Eclipse for Java EE Developers). You can installit in any directory you’d WorkspaceEclipse organizes all your work by projects. Projects are placed in a workspace, whichis a location you choose. So, where you put your workspace is significant. I recommend~/workspace as a simple place for your code. On Windows, however, I recommendstoring your workspace in a directory that doesn’t have spaces in it (they complicateanything you might do at the command line). C:\workspace is a good choice for Win-dows Up Android Development ToolsYou also need to set up Android Tools for Eclipse. The instructions are: Eclipse, then select Help→Install New Software (see Figure 3-1). the Available Software dialog, click the Add Site dialog that appears, enter a name for the remote site (for example,“Android Plugin”) in the “Name” the “Location” field, enter this URL: in the Available Software view, you should now see “Developer Tools” addedto the list. Select the checkbox next to Developer Tools, which will automaticallyselect the nested tools Android DDMS and Android Development Tools. the resulting Install Details dialog, the Android DDMS and Android Develop-ment Tools features are listed. Click Next to read and accept the license agreementand install any dependencies, then click you have trouble downloading the plug-in, you can try using “http”in the URL instead of “https” (https is preferred for security reasons).Installing the Android SDK|17
Figure 3-1. Install new softwareHello, WorldTo make sure everything is set up properly, we’re going to write a simple Hello Worldprogram. As a matter of fact, there’s not much for us to write, but a lot to is because Eclipse will create the project shell for us from some a New ProjectIn Eclipse, choose File→New→Android Project. Sometimes (especially the first time yourun Eclipse) the Android tools may not be appear there right away. They should showup in the future after you’ve used them for the first time. If Android Project is not anoption under File→New, choose Other and look for Android Project in |Chapter 3: Quick StartDownload from Wow! eBook <>
In the new project dialog window, fill out the following:1.“Project name” is an Eclipse construct. Eclipse organizes everything into project name should be one word. I like to use the CamelCase naming conventionhere. Go ahead and type , you need to choose the build target. The build target tells the build toolswhich version of the Android platform you are building for. In here you should seea list of available platforms and add-ons you have installed as part of your ahead and pick one of the newer ones, such as Android (but don’t choosethe targets named Google APIs—those are Google’s proprietary extensions to theAndroid platform). For our purposes, we’ll stick to Android Open Source versionsof the Android need to fill out your project properties next. The application name is the plainEnglish name of your application. Go ahead and enter something like Hello,World!!!. package name is a Java construct. In Java, all source code is organized intopackages. Packages are important because, among other things, they specify thevisibility of objects between the various Java classes in your project. In Android,packages are also important for application signing purposes. Your package nameshould be the reverse of your domain name with optional subdomains. I might if I were building a calculator app and my domain namewas . I’m going to be using for my package name can optionally specify an activity. I haven’t covered activities yet (you’ll learnabout them in Chapter 6), but think of them as corresponding to the various screensin your application. An activity is going to be represented by a Java class, andtherefore its name should adhere to Java class naming conventions: start with anupper-case letter and use CamelCase to separate words. So, type HelloWorld foryour activity minimum SDK version is the minimum version of Android—as representedby API level—that is required for the device to run this application. You want thisnumber to be as low as possible so that your app can run on as many devices aspossible. I’m going to put 8 here to represent Android , which I know I , click on the Finish button, and Eclipse will create your project. Let’s look atthe various files that this process created in Figure , World|19
Figure 3-2. HelloWorld new project windowManifest FileThe manifest file glues everything together. It is this file that explains what the appli-cation consists of, what all its main building blocks are, what permissions it requires,and so on (see Example 3-1).20|Chapter 3: Quick Start
Example 3-1. <?xml version="" encoding="utf-8"?><manifest xmlns:android=" package="" android:versionCode="1" android:versionName=""> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".HelloWorld" android:label="@string/app_name"> <intent-filter> <action android:name="" /> <category android:name="" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="8" /></manifest>Layout XML CodeThe layout file specifies the layout of your screen. In this case, shown in Example 3-2,we have only one screen, and it’s loaded by the code seen in Exam-ple 3-2. res/layout/<?xml version="" encoding="utf-8"?><LinearLayout xmlns:android=" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /></LinearLayout>StringsThis is another XML file that contains all the text that your application uses. For ex-ample, the names of buttons, labels, default text, and similar types of strings go intothis file. This is the best practice for separating the concerns of various files, even if theyare XML files. In other words, layout XML is responsible for the layout of widgets, butstrings XML is responsible for their textual content (see Example 3-3).Example 3-3. res/values/<?xml version="" encoding="utf-8"?><resources> <string name="hello">Hello World, HelloWorld!</string> <string name="app_name">Hello, World!!!</string></resources>Hello, World|21
The R FileThe R file is the glue between the world of Java and the world of resources (see Exam-ple 3-4). It is an automatically generated file, and as such, you never modify it. It isrecreated every time you change anything in the res directory, for example, when youadd an image or XML don’t need to look at this file much. We will use the data in it quite a bit, but we’lluse Eclipse to help us refer to values stored in this 3-4. gen/com/marakana/ AUTO-GENERATED FILE. DO NOT MODIFY. * * This class was automatically generated by the * aapt tool from the resource data it found. It * should not be modified by hand. */package ;public final class R { public static final class attr { } public static final class drawable { public static final int icon=0x7f020000; } public static final class layout { public static final int main=0x7f030000; } public static final class string { public static final int app_name=0x7f040001; public static final int hello=0x7f040000; }}Java Source CodeThe Java code is what drives everything. This is the code that ultimately gets convertedto a Dalvik executable and runs your application (see Example 3-5).Example 3-5. ;import ;import ;public class HelloWorld extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState);22|Chapter 3: Quick Start
setContentView(); }}The EmulatorRunning your application on a physical device versus an emulated device is pretty muchthe same thing. That is because the emulator is an actual code emulator, meaning itruns the same code base as the actual device, all the way down to the machine simulator and an emulator sound very similar, but are fundamentallydifferent. To emulate means to imitate the machine executing the binarycode. So, an emulator is sort of like a virtual machine. A simulator merelysimulates the behavior of the code at a higher level. Android SDK shipswith a true emulator, based on use the emulator, we’ll have to create an Android Virtual Device (AVD). The easiestway to do that is to start the android tool via create a new AVD, start the tool called Android SDK and AVD Manager (see Fig-ure 3-3). You can start this tool from Eclipse by clicking on the icon or via thecommand line by starting the tool called android, which is located in your SDK/ 3-3. Android SDK and AVD ManagerFrom within the Android SDK and AVD Manager window, choosing “New…” popsup a Create New AVD dialog window (see Figure 3-4). In this dialog, you specify theThe Emulator|23
parameters for your new AVD. The name can be any name you choose. The targetdesignates which version of Android you want installed on this particular AVD. Thelist of possible targets is based on platforms and add-ons that you have installed intoyour SDK. If you don’t have any targets, go back to the Android SDK and AVD Managerwindow and choose the “Available packages” tab to install at least one platform, forexample, Android - API level AVD can have an SD card. You can just specify a number here for your built-incard, in megabytes. The skin is the look and feel of your device as well as its form Hardware option lets you fine-tune what this AVD does and doesn’t 3-4. New AVD dialogOnce you are done with this dialog, you will have an AVD in your list. Go ahead andstart it, and an emulator will pop up (see Figure 3-5).24|Chapter 3: Quick Start
Figure 3-5. EmulatorAn Emulator Versus a Physical PhoneFor the most part, running your application on the emulator is identical to running iton a physical phone. There are some notable exceptions, mostly things that are justhard to virtualize, such as sensors. Other hardware-related features such as telephonyand location services, can be simulated in the up the Android development environment basically involves setting up AndroidSDK and Eclipse. Once you have set up your development environment, a good wayto test that everything is working is to use Eclipse to create a simple Hello World projectand run it in the emulator. If that runs fine, you are almost certain that your system isset up and ready for further |25
CHAPTER 4Main Building BlocksIn this chapter, you will learn about the big blocks in Android. We’ll give you a high-level overview of what activities are, how intents work, why services are cool, how touse broadcast receivers and content providers to make your app scale, and much the end of this chapter, you will understand the main Android components forbuilding applications. You should conceptually know when you’d use what compo-nent. You will also see how these components relate to a real-world Are Main Building Blocks?The main building blocks are components that you use as an application developer tobuild Android apps. They are the conceptual items that you put together to create abigger whole. When you start thinking about your application, it is good to take a top-down approach. You design your application in terms of screens, features, and theinteractions between them. You start with conceptual drawing, something that you canrepresent in terms of “lines and circles.” This approach to application developmenthelps you see the big picture—how the components fit together and how it all Real-World ExampleLet’s say that we want to build a Twitter app. We know that the user should be ableto post status updates. We also know the user should be able to see what her friendsare up to. Those are basic features. Beyond that, the user should also be able to set herusername and password in order to log into her Twitter account. So, now we know weshould have these three , we would like this app to work quickly regardless of the network connection orlack thereof. To achieve that, the app has to pull the data from Twitter when it’s onlineand cache the data locally. That will require a service that runs in the background aswell as a
We also know that we’d like this background service to be started when the device isinitially turned on, so by the time the user first uses the app, there’s already up-to-dateinformation on her , these are some straightforward requirements. Android building blocks make it easyto break them down into conceptual units so that you can work on them independently,and then easily put them together into a complete activity is usually a single screen that the user sees on the device at one time. Anapplication typically has multiple activities, and the user flips back and forth amongthem. As such, activities are the most visible part of your usually use a website as an analogy for activities. Just like a website consists of multiplepages, so does an Android application consist of multiple activities. Just like a websitehas a “home page,” an Android app has a “main” activity, usually the one that is shownfirst when you launch the application. And just like a website has to provide some sortof navigation among various pages, an Android app should do the the Web, you can jump from a page on one website to a page on another. Similarly,in Android, you could be looking at an activity of one application, but shortly after youcould start another activity in a completely separate application. For example, if youare in your Contacts app and you choose to text a friend, you’d be launching the activityto compose a text message in the Messaging Life CycleLaunching an activity can be quite expensive. It may involve creating a new Linuxprocess, allocating memory for all the UI objects, inflating all the objects from XMLlayouts, and setting up the whole screen. Since we’re doing a lot of work to launch anactivity, it would be a waste to just toss it out once the user leaves that screen. To avoidthis waste, the activity life cycle is managed via Activity Manager is responsible for creating, destroying, and managing activities. Forexample, when the user starts an application for the first time, the Activity Managerwill create its activity and put it onto the screen. Later, when the user switches screens,the Activity Manager will move that previous activity to a holding place. This way, ifthe user wants to go back to an older activity, it can be started more quickly. Olderactivities that the user hasn’t used in a while will be destroyed in order to free morespace for the currently active one. This mechanism is designed to help improve thespeed of the user interface and thus improve the overall user for Android is conceptually different than programming for some otherenvironments. In Android, you find yourself responding more to certain changes in thestate of your application rather than driving that change yourself. It is a managed,28|Chapter 4: Main Building Blocks
container-based environment similar to programming for Java applets or servlets. So,when it comes to an activity life cycle, you don’t get to say what state the activity is in,but you have plenty of opportunity to say what happens during the transitions fromstate to state. Figure 4-1 shows the states that an activity can go 4-1. Activity life cycleStarting stateWhen an activity doesn’t exist in memory, it is in a starting state. While it’s starting up,the activity will go through a whole set of callback methods that you as a developerhave an opportunity to fill out. Eventually, the activity will be in a running in mind that this transition from starting state to running state is one of themost expensive operations in terms of computing time, and this also directly affectsthe battery life of the device. This is the exact reason why we don’t automatically destroyactivities that are no longer shown. The user might want to come back to them, so wekeep them around for a stateThe activity in a running state is the one that is currently on the screen and interactingwith the user. We also say this activity is in focus, meaning that all user interactions—such as typing, touching the screen, and clicking buttons—are handled by this oneactivity. As such, there is only one running activity at any given |29
The running activity is the one that has priority in terms of getting the memory andresources it needs to run as quickly as possible. This is because Android wants to makesure the running activity is zippy and responsive to the stateWhen an activity is not in focus (., not interacting with the user) but still visible onthe screen, we say it’s in a paused state. This is not a typical scenario, because thedevice’s screen is usually small, and an activity is either taking up the whole screen ornone at all. We often see this case with dialog boxes that come up in front of an activity,causing it to become Paused. All activities go through a paused state en route to activities still have high priority in terms of getting memory and other is because they are visible and cannot be removed from the screen without makingit look very strange to the stateWhen an activity is not visible, but still in memory, we say it’s in a stopped state. Stoppedactivity could be brought back to the front to become a Running activity again. Or, itcould be destroyed and removed from system keeps activities around in a stopped state because it is likely that the userwill still want to get back to those activities some time soon, and restarting a stoppedactivity is far cheaper than starting an activity from scratch. That is because we alreadyhave all the objects loaded in memory and simply have to bring it all up to activities can be removed from memory at any stateA destroyed activity is no longer in memory. The Activity Manager decided that thisactivity is no longer needed and has removed it. Before the activity is destroyed, it canperform certain actions, such as save any unsaved information. However, there’s noguarantee that your activity will be stopped prior to being destroyed. It is possible fora paused activity to be destroyed as well. For that reason, it is better to do importantwork, such as saving unsaved data, en route to a paused state rather than a fact that an activity is in a running state doesn’t mean it’s doingmuch. It could be just sitting there and waiting for user input. Similarly,an activity in a stopped state is not necessarily doing nothing. The statenames mostly refer to how active the activity is with respect to userinput, in other words, whether an activity is visible, in focus, or notvisible at |Chapter 4: Main Building BlocksDownload from Wow! eBook <>
IntentsIntents are messages that are sent among the major building blocks. They trigger anactivity to start up, tell a service to start or stop, or are simply broadcasts. Intents areasynchronous, meaning the code that sends them doesn’t have to wait for them to intent could be explicit or implicit. In an explicit intent, the sender clearly spellsout which specific component should be on the receiving end. In an implicit intent, thesender specifies the type of receiver. For example, your activity could send an intentsaying it simply wants someone to open up a web page. In that case, any applicationthat is capable of opening a web page could “compete” to complete this you have competing applications, the system will ask you which one you’d liketo use to complete a given action. You can also set an app as the default. This mechanismworks very similarly to your desktop environment, for example, when you downloadedFirefox or Chrome to replace your default Internet Explorer or Safari web type of messaging allows the user to replace any app on the system with a customone. For example, you might want to download a different SMS application or anotherbrowser to replace your existing ones. Figure 4-2 shows how intents may be used to“jump” between various activities, in the same application or in another app 4-2. IntentsServicesServices run in the background and don’t have any user interface components. Theycan perform the same actions as activities, but without any user interface. Services areuseful for actions that we want to perform for a while, regardless of what is on theServices|31
screen. For example, you might want your music player to play music even as you areflipping between other ’t confuse the Android services that are part of an Android app withnative Linux services, servers, or daemons, which are a much lower-levelcomponent of the operating have a much simpler life cycle than activities (see Figure 4-3). You either starta service or stop it. Also, the service life cycle is more or less controlled by the developer,and not so much by the system. Consequently, we as developers have to be mindful torun our services so that they don’t consume shared resources unnecessarily, such asthe CPU and 4-3. Service life cycleThe fact that a service runs in the background doesn’t mean it runs ona separate thread. If a service is doing some processing that takes a whileto complete (such as performing network calls), you would typically runit on a separate thread. Otherwise, your user interface will run notice-ably slower. In other words, services and activities run on the same mainapplication thread, often called the UI ProvidersContent providers are interfaces for sharing data between applications. By default, An-droid runs each application in its own sandbox so that all data that belongs to anapplication is totally isolated from other applications on the system. Although smallamounts of data can be passed between applications via intents, content providers aremuch better suited for sharing persistent data between possibly large datasets. As such,the content provider API nicely adheres to the CRUD principle. Figure 4-4 illustrateshow the content provider’s CRUD interface pierces the application boundaries andallows other apps to connect to it to share |Chapter 4: Main Building Blocks
Figure 4-4. Content providerThe Android system uses this mechanism all the time. For example, Contacts Provideris a content provider that exposes all user contact data to various applications. SettingsProvider exposes system settings to various applications, including the built-in Settingsapplication. Media Store is responsible for storing and sharing various media, such asphotos and music, across various applications. Figure 4-5 illustrates how the Contactsapp uses Contacts Provider, a totally separate application, to retrieve data about users’contacts. The Contacts app itself doesn’t have any contacts data, and Contacts Providerdoesn’t have any user 4-5. Contacts application using Contacts Provider to get the dataContent Providers|33
This separation of data storage and the actual user interface application offers greaterflexibility to mash up various parts of the system. For example, a user could install analternative address book application that uses the same data as the default Contactsapp. Or, he could install widgets on the Home screen that allow for easy changes in theSystem Settings, such as turning on or off the WiFi, Bluetooth, or GPS features. Manyphone manufactures take advantage of content providers to add their own applicationson top of standard Android to improve overall user experience, such as HTC providers are relatively simple interfaces, with the standard insert(),update(), delete(), and query() methods. These methods look a lot like standard da-tabase methods, so it is relatively easy to implement a content provider as a proxy tothe database. Having said that, you are much more likely to use content providers thanwrite your ReceiversBroadcast receivers are Android’s implementation of a system-wide publish/subscribemechanism, or more precisely, an Observer pattern. The receiver is simply dormantcode that gets activated once an event to which it is subscribed system itself broadcasts events all the time. For example, when an SMS arrives, acall comes in, the battery runs low, or the system gets booted, all those events arebroadcasted, and any number of receivers could be triggered by our Twitter app example, we want to start the update service once the system startsup. To do that, we can subscribe to the broadcast that tells us the system has completedbooting can also send your own broadcasts from one part of your application to another,or to a totally different receivers themselves do not have any visual representation, nor are they ac-tively running in memory. But when triggered, they get to execute some code, such asstarting an activity, a service, or something ContextSo far you have seen activities, services, content providers, and broadcast , they make up an application. Another way of saying this is that they liveinside the same application context refers to the application environment and the process within whichall its components are running. It allows applications to share the data and resourcesbetween various building application context gets created whenever the first component of this applicationis started up, regardless of whether that component is an activity, service, or something34|Chapter 4: Main Building Blocks
else. Application context lives as long as your application is alive. As such, it is inde-pendent of the activities life cycle. You can easily obtain a reference to the context bycalling () or (). Keep in mindthat activities and services are already subclasses of context, and as such they inheritall its this chapter, you learned about some of the most important Android applicationcomponents. We put together these components to create various applications, froma simple Hello World to much more complex the next chapter, we’ll outline a Yamba application as an example of how all thesebits and pieces come together to form a working Android |35
CHAPTER 5Yamba Project OverviewThe best way to learn is by an example, and that example has to meet certain working with thousands of new Android developers and using various exampleapplications to explain some of the unique concepts that this platform has to offer, Iconcluded that the best example has to be:ComprehensiveA good example app should demonstrate most of the aspects of the applicationframework that are unique to Android. Additionally, there should be a good reasonto use a specific feature in order to get the job done. This is important in order tocreate the right motivation for those new to example application should be simple to understand. We want to focus ondesign and implementation, and not on features and Yamba ApplicationThe application I picked for this book is a Twitter-like application. We call it Yamba,which stands for Yet Another Micro Blogging App. Yamba lets a user connect to a servicesuch as Twitter, pull down friends’ statuses, and update that user’s own covers most of the main Android building blocks in a natural way. As such, it’sa great sample application to illustrate both how various components work individuallyand how they fit together. Services such as Twitter are more or less familiar to mostpeople, so the features of the application do not require much 5-1 through 5-3 show what a finished product could look
Figure 5-1. List of status messages from other people, called a timelineFigure 5-2. Screen where the user can enter a status messageFigure 5-3. User preferences38|Chapter 5: Yamba Project OverviewDownload from Wow! eBook <>
Figure 5-1 shows how Yamba displays a list of status messages from your 5-2 shows the initial Yamba screen, and Figure 5-3 shows the user PhilosophyWe’re going to adopt a certain design philosophy in tackling this project. This philos-ophy will help guide us in our development and serve as a north star when in doubtabout what to do next. Spelling out the design philosophy here should also help elim-inate some confusion in the process we’re following:Small incrementsThe Yamba application will start out small and will constantly grow in functionalityand complexity. Initially, the app will not do much, but it will grow organicallyone step at a time. Along the way, we’ll explain each step so that you’re expandingyour skills as you whole and completeThe application must always work. In other words, we’ll add new features in small,self-contained chunks and pull them back into the main project so that you cansee how it fits together as a whole. The application must always work at eachstopping codeOnce in a while, we’ll have to take a step back and refactor the application toremove duplicate code and optimize the design. The goal is to reuse the code andnot reinvent the wheel. But we are going to cross those bridges as we get to them,providing the motivation for refactoring along the way. This process will teach youabout some general software development best practices as DesignIf you remember from Chapter 4, an Android application is a loose collection of activ-ities, services, content providers, and broadcast receivers. These are the componentsfrom which we put together an application. Figure 5-4 shows the design of the entireYamba application, which incorporates most of the main Android building 1: Android User InterfaceThis part, covered in Chapter 6, will focus on developing the first component of theYamba application: the Status Update screen. Our tasks are building an activity, net-working and multithreading, and 1: Android User Interface|39
Figure 5-4. Yamba design diagramBuilding an ActivityWe are going to start by introducing the Android user interface (UI) model. In its UI,Android is quite different from some other paradigms that you might be familiar unique feature is its dual approach to UI via both Java and |Chapter 5: Yamba Project Overview
In this chapter, you will learn how to develop the user interface for Figure 5-2, wherethe user updates her status. Through this process, you will use XML and Java to puttogether a working UI. You will learn about Layouts and Views, units in Android, howto work with images, and how to make the UI look approach will focus on best practices in UI development so that your applicationlooks good and works well on any Android device, regardless of screen size and MultithreadingOnce we have a working screen, we will want to post the user input to the cloud that purpose, we are going to use a third-party library to help us with the TwitterAPI web service making the network calls, you’ll notice that the UI starts behaving sluggishly,due to the unpredictable nature of the network. The network latency might even causeour application to stop responding. At that point, we will introduce multithreading inAndroid and explain how to develop an app that works well regardless of Android AppsA few things are going to go wrong in this section of the book. This is by design, becausedebugging is a normal part of application development. We’ll show you how to use theAndroid SDK tools to quickly find and fix problems. Debugging will become secondnature to 2: Preferences, Filesystem, Options Menu, and IntentsThis part, covered in Chapter 7, is all about the preferences screen. At the end of thispart, your Yamba application will have two screens, one for status updates and theother for setting up the preferences. At this point, Yamba is configurable for varioususers and starts being a useful app. The elements we’ll create at this stage are the activity,the menu system and intents, and the ActivityFirst, we’ll create the screen, which is an activity, one of Android’s basic buildingblocks. You will see the steps involved and understand what it takes to create 2: Preferences, Filesystem, Options Menu, and Intents|41
Menu System and IntentsNext, we’ll need a way to get to that screen. For that purpose, we’ll introduce a menusystem in Android and show how it works. You will also learn about intents and howto send these to open up a specific , we’ll learn about the filesystem on a typical Android device. You will gain adeeper understanding of how the operating system is put together, and you will alsolearn more about Android 3: Android ServicesIn this part, covered in Chapter 8, introduces background services. By the end of thispart, your Yamba application will be able to periodically connect to the cloud and pulldown your friends’ status services are very useful building blocks. They allow a process to run in thebackground without requiring any user interface. This is perfect for Yamba, as we’llhave an update process connect to the cloud periodically and pull the data. In thissection, you will also learn about multithreading considerations as they apply to back-ground ObjectAt this point, we’ll notice repetition in the code and recognize that our system is nolonger as elegant as it could be. So we are going to introduce the Application object asa way to refactor Yamba and make it easier to 4: Working with DatabasesWe now have the data from our updater service but still need a place to store it. In thispart, covered in Chapter 9, we’ll introduce you to Android’s support for databases. Bythe end of that chapter, our data from the cloud will be persisted in the and Android’s Support for ItAndroid ships with a built-in database called SQLite. In addition to this cool littledatabase, the Android framework offers a rich API that makes SQLite easier for us touse. In this section, you will learn how to use SQLite and the API for it. You do not42|Chapter 5: Yamba Project Overview
have to be an SQL buff to understand what is going on, but some basic understandingof SQL always the Code AgainAt this point, we’ll have yet another opportunity to refactor and streamline our will be a strong motivation for refactoring at that moment, and the effort will befurther rewarded in later 5: Lists and AdaptersIt might sound like we’re back in UI mode, but Lists and Adapters are more organiza-tional aids than user interface elements in Android. They form very powerful compo-nents that allow our tiny UI to connect to very large datasets in an efficient and scalablemanner. In other words, users will be able to use Yamba in the real world without anyperformance hits in the long the data is all there in the database, but we have no way to view it. In thispart, covered in Chapter 10, the Yamba application will get the much-needed TimelineActivity and a way for the user to see what his friends are chatting about ActivityWe’re going to develop this third and final activity in multiple stages. First, we’ll useour existing knowledge of the Android UI and put something together. It will work,sort of. Next, we’ll improve on that design. The app will look better, but it still won’tbe ready for the prime time because our design won’t be able to handle real-world , we’ll get it right by introducing Lists and Adapters to the mix. Finally, we’ll getit right by introducing Lists and Adapters to the mix and use them to tie the data toour user Refactoring?We’ll have yet another opportunity to refactor our code by introducing a base activityfor all our common activity needs. This will give the user a more consistent feel for theapp across multiple screens and will make it easier for us to manage the code 6: Broadcast ReceiversIn this part, covered in Chapter 11, we’ll equip Yamba with receivers so it can react toevents around it in an intelligent way. For that purpose, we’ll use broadcast 6: Broadcast Receivers|43
Boot and Network ReceiversIn our example, we want to start our updates when the device is powered up. We alsowant to stop pulling the data from the cloud when the network is unavailable, and startit again only when we’re back online. This goal will introduce us to one type of broad-cast ReceiverThis type of receiver will exist only at certain times. Also, it won’t receive messagesfrom the Android system, but from other parts of our own Yamba application. Thiswill demonstrate how we can use receivers to put together loosely coupled componentsin an elegant and flexible this point in the development process you know how to ask for system permissions,such as access to the Internet or filesystem. In this section we’ll learn how to define ourown permissions and how to enforce them. After all, Yamba components might notwant to respond to any other application for some Yamba-specific 7: Content ProvidersIn this part, covered in Chapter 12, we’ll revisit content providers and refactor ourdatabase code to use them. To demonstrate that it all works, we’ll throw in an AndroidApp DataOur status data is OK the way it is if nobody else cares about it. But what if we wantto expose some of this data to the rest of the system? After all, other applications mightleverage our friends’ timelines in new and creative ways. To do that, we’ll create acontent provider and expose our status WidgetsBut who will remember to pull up our app? To demonstrate the usefulness of our newstatus data, we’ll put together an app widget. App widgets are those little componentsthat the user can put on the home screen to see weather updates and such. We’ll createa widget that will pull the latest status update from the Yamba database via the statusdata content provider and display it on the home |Chapter 5: Yamba Project Overview
Part 8: System ServicesThe Android OS comes with many useful system services, which include processes youcan access easily to ask for things such as your location, sensor readings, WiFi hotspots,and much more. In this part, covered in Chapter 13, you will add some cool new featuresto Yamba, such as the user’s current and LocationThis example will illustrate how system services work in general, and you will walkaway understanding some common patterns for using these services. We’ll illustratebuilding a compass app using sensors, and later, we’ll put this knowledge to use byletting Yamba display the user’s location when posting status Service, Alarms, and NotificationsIt turns out that some of the cool features provided by Android services can make ourUpdater service much simpler. So we’ll refactor our code yet again. This time, we’llintroduce Intent Services that respond to intents. But we’re going to need somethingto fire off these intents on a regular basis, and for that we’ll use the Alarm service. We’llalso add a feature to notify the user of new updates by putting a notification in thenotification bar. For that, we’ll use the Notification service. All this refactoring willcreate a substantially more elegant solution to our Updater service chapter is intended as a road map for the next eight chapters. By the end of allthese iterations, you will have built a medium-size Android app from scratch. Evenmore, you will understand various constructs and how to put them together into ameaningful whole. The hope is that you’ll start developing a way of thinking in |45
CHAPTER 6Android User InterfaceIn this chapter, you will learn how to build a user interface in Android. You will createyour first Activity, learn how to create an XML layout for it, and see how to connect itto Java. You will learn about Views (aka widgets) and Layouts, and learn how to handleJava events, such as button clicks. Additionally, you’ll add support for a Twitter-likeAPI into your project as an external .jar file so your app can make web service calls tothe the end of this chapter, you will have written your own Twitter-like Android app will feature a single screen that will prompt the user for her current statusupdate and post that update Ways to Create a User InterfaceThere are two ways to create a user interface (UI) in Android. One is declarative, andthe other one is programmatic. They are quite different but often are used together toget the job User InterfaceThe declarative approach involves using XML to declare what the UI will look like,similar to creating a web page using HTML. You write tags and specify elements toappear on your screen. If you have ever handcoded an HTML page, you did prettymuch the same work as creating an Android advantage of the declarative approach is that you can use what-you-see-is-what-you-get (WYSIWYG) tools. Some of these tools ship with the Eclipse Android Devel-opment Tools (ADT) extension, and others come from third parties. Additionally, XMLis fairly human-readable, and even people who are unfamiliar with the Android plat-form and framework can readily determine the intent of the user from Wow! eBook <>
The disadvantage of a declarative UI approach is that you can get only so far with is great for declaring the look and feel of your user interface, but it doesn’t providea good way of handling user input. That’s where the programmatic approach comes User InterfaceA programmatic user interface involves writing Java code to develop UI. If you haveever done any Java AWT or Java Swing development, Android is pretty much the samein that respect. It is similar to many UI toolkits in other languages as , if you want to create a button programmatically, you have to declare thebutton variable, create an instance of it, add it to a container and set any button prop-erties that may make sense, such as color, text, text size, background, and so on. Youprobably also want to declare what the button does once it’s clicked, so that’s anotherpiece of code. All in all, you end up writing quite a few lines of you can do declaratively, you can also do programmatically. But Java alsoallows you to specify what happens when that button is actually clicked. This is themain advantage of a programmatic approach to the user Best of Both WorldsSo which approach to use? The best practice is to use both. You would use a declarative(XML) approach to declare everything about the user interface that is static, such asthe layout of the screen, all the widgets, etc. You would then switch to a programmatic(Java) approach to define what goes on when the user interacts with the various widgetsin the user interface. In other words, you’d use XML to declare what the “button” lookslike and Java to specify what it that there are two approaches to developing the actual user inter-face, but at the end of the day, all the XML is actually “inflated” intoJava memory space as if you actually wrote Java code. So, it’s only Javacode that and LayoutsAndroid organizes its UI elements into layouts and views. Everything you see, such asa button, label, or text box, is a view. Layouts organize views, such as grouping togethera button and label or a group of these you have prior experience with Java AWT or Swing, layouts are similar to Java con-tainers and views are similar to Java components. Views in Android are sometimesreferred to as |Chapter 6: Android User Interface
Don’t confuse widgets in the Android UI with App Widgets. The latterare miniature application views that can be embedded in other appli-cations (such as the Home screen application). Here, we are referring towidgets as the views in our , a layout can contain other children. Those children can furthermore be layoutsthemselves, allowing for a complex user interface layout is responsible for allocating space for each child. Different layouts use differentapproaches to laying out their child widgets, as shown in Figure 6-1. Layouts and Views relationshipThere are several main layouts that we use more frequently than others, such asLinearLayout, TableLayout, FrameLayout, RelativeLayout, and is one of the simplest and most common layouts. It simply lays out itschildren next to each other, either horizontally or vertically. The order of the childrenmatters. As LinearLayout asks its children how much space they need, it allocates thedesired space to each child in the order they are added. So, if an “older” child comesalong and asks for all the space on the screen, there won’t be much left for the subse-quent widgets in this important property for LinearLayout is layout_orientation. Its valid options arevertical or and Layouts|49
Although Linear Layout is probably the simplest and most commonlyused layout, it is not always the best choice. A good rule of thumb isthat if you start to nest multiple Linear Layouts, you should probablyuse a different layout, such as Relative Layout. Too many nested layoutscan have major consequences on the time needed to inflate the UI andon overall CPU and battery lays out its children in a table and consists of only other TableRow widg-ets. TableRow represents a row in a table and can contain other UI widgets. TableRowwidgets are laid out next to each other horizontally, sort of like LinearLayout with ahorizontal those familiar with HTML, Table Layout is similar to the <table> element, andTable Row is similar to the <tr> element. Whereas in HTML we also have <td> torepresent each cell in the table, in Android the columns are determined dynamicallybased on the number of views we add to a table important property for TableLayout is stretch_columns, indicating which columnof the table to stretch. You can also use * to stretch all places its children on top of each other so that the latest child is coveringthe previous, like a deck of cards. This layout policy is useful for tabs, for is also used as a placeholder for other widgets that will be added pro-grammatically at some later point in lays out its children relative to each other. As such, it is very powerfulbecause it doesn’t require you to nest unnecessary layouts to achieve a certain look. Atthe same time, using RelativeLayout can minimize the total number of widgets thatneed to be drawn, thus improving the overall performance of your application. Havingsaid that, RelativeLayout requires each of its child views to have an ID set so that wecan position it relative to other positions its children at absolute coordinates on the screen. It is thefavorite layout for WYSIWYG tools, and although it is very simple, it is not very user interface would look good on one particular screen, but as soon as the screensize, orientation, or density changed, AbsoluteLayout would not be able to |Chapter 6: Android User Interface
Starting the Yamba ProjectWe are about to start our Yamba project. So, fire up Eclipse and click onFile→New→Android will get a dialog window asking you about your new Android project (see Fig-ure 6-2). Let’s explain again all the significant fields:Project nameThe name under which Eclipse organizes our project. It is a good idea not to useany spaces in your project name. This makes it easier to access from the commandline later. Enter “Yamba” this as is—set to creating a new project—since that’s what we intend to TargetThis field indicates the type of Android system we intend to run this applicationon. This could be any Android platform, either standard or proprietary. I assumewe’re working with Android (API level 9) and thus will choose the Android nameSimply a plain-text name for your application. It can be any text. For our app, feelfree to enter “Yamba”.Package nameThis field designates a Java package, and as such it needs to adhere to Java packagenaming conventions. In a nutshell, you want to use the reverse of your domainname for your package. I’m going to use “” ActivityAn option to create an activity as part of this project. You can leave it the activity name, we must adhere to Java class naming conventions. Doingthat simply means using upper CamelCase. I’m going to enter “StatusActivity” SDK VersionRepresents the minimum version of Android SDK that must be installed on thedevice for it to run this particular application. Typically, this number will corre-spond to the API level that you picked for your target, in our case, Android , if the app doesn’t depend on the latest and greatest API or is capable ofscaling gracefully to a lower API, you should rethink this number. In our case, theapp will be able to work on API level 4 (Android ), so enter 4 here. This is agood choice because we can distribute our app to way more people than if theminimum were Android on Finish. Your Yamba project should now appear in Eclipse’s Package the Yamba Project|51
Figure 6-2. New project dialogThe StatusActivity LayoutLet’s start by designing the user interface for our screen where we’ll enter the new statusand click a button to update default, Eclipse created a file called under the res/layout folder. For con-sistency purposes, we should rename this file to to match our |Chapter 6: Android User Interface
To rename a file in Eclipse, right-click on it, choose Refactor→Rename…, and enter thenew name. Eclipse is somewhat smart about renaming files and does more than justchange the name. It also offers to look up all references to this file and update those aswell. Although this feature works well when renaming a Java file, it is not fully auto-matic with XML files. So, renaming this file requires us to change the line in Java wherewe refer to it via the R class. To do that, in your StatusActivity’s onCreate(), changesetContentView(); to setContentView();.This screen will have four components:•A title at the top of the screen. This will be a TextView widget.•A big text area to type our 140-character status update. We’ll use an EditTextwidget for this purpose.•A button to click to update the status. This will be a Button widget.•A layout to contain all these widgets and lay them out one after another in a verticalfashion. For this screen, we’ll use LinearLayout, one of the more common 6-1 contains the source code for our StatusActivity 6-1. res/layout/<?xml version="" encoding="utf-8"?><!-- Main Layout of Status Activity --><LinearLayout xmlns:android=" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <!-- Title TextView--> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="30sp" android:layout_margin="10dp" android:text="@string/titleStatus"/> <!-- Status EditText --> <EditText android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:hint="@string/hintText" android:id="@+id/editText" android:gravity="top|center_horizontal"></EditText> <!-- Update Button --> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/buttonUpdate" android:textSize="20sp" android:id="@+id/buttonUpdate"></Button></LinearLayout>This code was generated by Eclipse Graphical Layout, shown in Figure 6-3. AndroidDevelopment Tools (ADT) for the Eclipse plug-in provides this to help you work withAndroid-specific XML files. Since ADT knows that you are working on a UI layout, itopens in Graphical Layout mode. You can also view the raw XML by choos-The StatusActivity Layout|53
ing the tab at the bottom of this window. That will give you the XML sourcecode for this screen, as displayed in this 6-3. Graphical Layout mode for we discussed the basic meanings of these XML resources in a previous chap-ter, there are some details in the code that you should know more about, which we’llexamine in the following Widget PropertiesThe properties you are most likely to use regularly are:layout_height and layout_widthDefines how much space this widget is asking from its parent layout to displayitself. Although you could enter a value in pixels, inches, or something similar, thatis not a good practice. Since your application could run on many different deviceswith various screen sizes, you want to use relative size for your components, notabsolute. So, best practice would be to use either fill_parent or wrap_content forthe value. fill_parent means that your widget wants all the available space fromits parent. wrap_content means that it requires only as much space as it needs todisplay its own content. Note that in API Level 8 and higher, fill_parent has beenrenamed to |Chapter 6: Android User Interface
layout_weightLayout weight is a number between 0 and 1. It implies the weight of our layoutrequirements. For example, if our Status EditText had a default layout weight of0 and required a layout height of fill_parent, then the Update button would bepushed out of the screen because Status and its request for space came before thebutton. However, when we set the Status widget’s layout weight to 1, we are sayingwe want all available space height-wise, but are yielding to any other widget thatalso may need space, such as the Update _gravitySpecifies how this particular widget is positioned within its layout, both horizon-tally and vertically. Values could be top, center, left, and so on. Notice the dif-ference between this property and gravity, explained next. For example, if youhave a widget that has its width set to fill_parent, trying to center it wouldn’t domuch, because it’s already taking all available space. However, if our Title TextView had its width set to wrap_content, centering it with layout_gravity wouldgenerate the desired how the content of this widget is positioned within the widget itself. It iscommonly confused with layout_gravity. Which one to use will depend on thesize of your widget and the desired look. For example, if our Title TextView hadthe width fill_parent, then centering it with gravity would work, but centeringit with layout_gravity wouldn’t do all widgets have this property, but many do, such as Button, EditText, andTextView. It simply specifies the text for the widget. However, it is not a goodpractice to just enter the text, because then your layout will work in only one locale/language. Best practice is to define all text in a resource and refer to aparticular string using this notation: @string/ is simply the unique identifier for this particular widget in a particular layoutresource file. Not every widget needs an id, and I recommend removing unneces-sary ids to minimize clutter. But widgets that we’ll need to manipulate later fromJava do need ids. id has the format @+id/someName, where someName is whatever youwant to call your widget. My naming convention is to use the type followed by thename, for example, @+id/ ResourceAndroid tries hard to keep data in separate files. So, layouts are defined in their ownresources, and all text values (such as button text, title text, etc.) should be defined intheir own file called . This later allows you to provide multiple versions ofstrings resources for various languages, such as English, Japanese, or StatusActivity Layout|55
Example 6-2 shows what our file looks like at this 6-2. res/values/<?xml version="" encoding="utf-8"?><resources> <string name="app_name">Yamba 1</string> <string name="titleYamba">Yamba</string> <string name="titleStatus">Status Update</string> <string name="hintText">Please enter your 140-character status</string> <string name="buttonUpdate">Update</string></resources>The file simply contains sets of name/value use a certain naming convention for my resource names. Let’s look attitleYamba, for example. First, I prefix the resource with the name ofwhat it is, in this case a title of the activity. Second, I give it a name,Yamba. This naming convention helps keep many different resourcessorted in an easy-to-find way. Finally, I use CamelCase for my names,though some may prefer to use underscores to separate StatusActivity Java ClassNow that we have our UI designed in XML, we are ready to switch over to Java. Re-member from earlier in this chapter that Android provides two ways for building userinterfaces. One is by declaring it in XML, which is what we just did, and we got as faras we could (for now). The other one is to build it programmatically in Java. We alsosaid earlier that the best practice is to get as far as possible in XML and then switchover to Java class for this is , and the Eclipse New Project dialog hasalready created the stub for this class. The class is part of the Javapackage, and as such is part of that Your Application-Specific Object and Initialization CodeAs with all main building blocks in Android, such as activities, services, broadcastreceivers, and content providers, you usually start by subclassing a base class providedby the Android framework and overriding certain inherited methods. In this case, wesubclass Android’s Activity class and override its onCreate() method. As you recall,activities have a certain life cycle (see “Activity Life Cycle” on page 28), or state machinethrough which they go. We as developers do not control what state the activity is in,but we do get to say what happens during a transition to a particular state. In this case,the transition we want to override is the onCreate() method that the system’s56|Chapter 6: Android User InterfaceDownload from Wow! eBook <>
ActivityManager invokes when the activity is first created (., when it goes from astarting to a running state). This sort of programming, when we subclass a system classand fill in the blanks, is also known as the Template addition to doing some standard housekeeping, our onCreate() will carry out twomajor tasks that the application needs done just once, at the beginning: set up ourbutton so it responds to clicks and connect to the that onCreate() takes a Bundle as a parameter. This is a small amount of datathat can be passed into the activity via the intent that started it. The data provided ina Bundle is typically limited to basic data types; more complex ones need to be speciallyencoded. For the most part, we’re not going to be using Bundle in our Yamba example,as there’s no real need for in mind that whenever you override a method, you first want to make a call tothe original method provided by the parent. That’s why we have a ()call , once you subclass the framework’s class, override the appropriate method, and callsuper’s method in it, you are still back where you started: your code does the samething the original class did. But now we have a placeholder where we can add our very first thing we typically do in an activity’s onCreate() is to load the UI from theXML file and inflate it into the Java memory space. In other words, we write some Javacode that opens up our XML layout file, parses it, and for each element in XML, createsa corresponding Java object in our memory space. For each attribute of a particularXML element, this code will set that attribute on our Java object. This process is calledinflating from XML, and the line of code that does all this is setContentView();.Remember that the R class is the automatically generated set of pointers that helpsconnect the world of Java to our world of XML and other resources in the /res , points to our /res/layout/ setContentView() method does a lot of work, in other words. It reads the XMLfile, parses it, creates all the appropriate Java objects to correspond to XML elements,sets object properties to correspond to XML attributes, sets up parent/child relation-ships between objects, and overall inflates the entire view. At the end of this one line,our screen is ready for objects are not the only ones that define methods and respond to external stimuli. Android’s user interface objects do that too. Thus, you can tell your Button to executecertain code when its clicked. To do that, you need to define a method namedonClick() and put the code there that you want executed. You also have to run thesetOnClickListener method on the Button. You pass this as an argument to setOnClickListener because your object is where you define onClick(). Example 6-3 shows ourThe StatusActivity Java Class|57
first version of , with some additional explanation following 6-3. , version 1package ;import ;import ;import ;import ;import ;import ;import ;import ;public class StatusActivity1 extends Activity implements OnClickListener { // private static final String TAG = "StatusActivity"; EditText editText; Button updateButton; Twitter twitter; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find views editText = (EditText) findViewById(); // updateButton = (Button) findViewById(); (this); // twitter = new Twitter("student", "password"); // (" } // Called when button is clicked // public void onClick(View v) { (().toString()); // (TAG, "onClicked"); }}To make StatusActivity capable of being a button listener, it needs to implementthe OnClickListener views inflated from the XML layout and assign them to Java the button to notify this (., StatusActivity) when it gets to the online service that supports the Twitter API. At this point, we hard-code the username and |Chapter 6: Android User Interface
The method that is called when button is clicked, as part of the the web service API call to the cloud to update our Code and Building Your Projects: Saving FilesOnce you make changes to your Java or XML files, make sure you save them beforemoving on. Eclipse builds your project automatically every time you choose File→Saveor press Ctrl-S. So, it is important to save files and make sure you do not move to anotherfile until the current file is fine. You will know your file is fine when there are no littlered x symbols in your code and the project builds successfully. Because Java dependson XML and vice versa, moving to another file while the current one is broken justmakes it even more difficult to find errors typically are easy to find since the little red x in the code navigates youstraight down to the line number where the error occurred (see Figure 6-4). By puttingyour mouse right on that error, Eclipse will tell you what the error is and will also offeryou some possible fixes. This Eclipse feature is very useful and is analogous to thespellchecker in a word 6-4. Tracing Java errorsAdding the LibraryWe are connecting to the online service that implements the Twitter-compatible APIin our application. This connection is done via a series of web service calls. Since An-droid uses standard Java networking capabilities, Android doesn’t offer much moreThe StatusActivity Java Class|59
Figure 6-5. Properties for Yamba dialog window in Eclipse, where we add the filewith respect to web services than we already have in Java. So, as such, there’s little valuein reinventing the make our life with web services and the Twitter API easier, we’re going to use a third-party library, , provided by Winterwell Associates. This library con-tains a simple Java class that interacts with the online service and abstracts all theintricacies of making network calls and passing the data back and forth. If no one hadbeen kind enough to provide a high-level library for what we need to do, we couldalways use standard Java networking libraries to get the job done. It just would havebeen more library provided with this code has been slightly modi-fied from the official Winterwell version to make it work in our you download this library, you can put it inside your project in Eclipse. Simplydrag the file and drop it in the root of your Eclipse project in the PackageManager window. This makes the file part of the project, but our Java code is still unableto locate searches for all the classes in its classpath. To add to the classpath,right-click on your project, select Properties, and you will get a Properties for Yambadialog window (see Figure 6-5). Select Java Build Path, and choose the Libraries there, click on Add JARs… and locate your |Chapter 6: Android User Interface
Updating the Manifest File for Internet PermissionBefore this application can work, we must ask the user to grant us the right to use theInternet. Android manages security by specifying the permissions needed for certaindangerous operations. The user then must explicitly grant those permissions to eachapplication when he first installs the application. The user must grant all or no per-missions that the application asks for; there’s no middle ground. Also, the user is notprompted about permissions when upgrading an existing we are running this application in debug mode and installingit via a USB cable, Android doesn’t prompt us for permissions like itwould the end user. However, we still must specify that the applicationrequires certain this case, we want to ask the user to grant this application the INTERNET need Internet access to connect to the online service. So, open up the by double-clicking on it. Note that Eclipse typically opens this file in a WYSIWYGeditor with many tabs on the bottom. As always, you can make most of the changes tothis file via this interface, but since Eclipse tools are limited and sometimes buggy, weprefer to go straight into the XML view of this file. So, choose the right-most tab at thebottom that says , and add a <uses-permissionandroid:name="" /> element within the <manifest> block(see Example 6-4).Example 6-4. <?xml version="" encoding="utf-8"?><manifest xmlns:android=" android:versionCode="1" android:versionName="" package=""> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".StatusActivity" android:label="@string/titleStatus"> <intent-filter> <action android:name="" /> <category android:name="" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="" /> <!-- --></manifest>Defines the <uses-permission> element for the INTERNET StatusActivity Java Class|61
Logging in AndroidAndroid offers a system-wide logging capability. You can log from anywhere in yourcode by calling (TAG, message), where TAG and message are some strings. TAGshould be a tag that is meaningful to you given your code. Typically, a tag would bethe name of your app, your class, or some module. Good practice is to define TAG asa Java constant for your entire class, such as:private static final String TAG = "StatusActivity";Before your code will compile, you need to import the Log class. Eclipsehas a useful feature under Source→Organize Imports, or Ctrl+O forshort. Usually, this feature will automatically organize your importstatements. However, in the case of Log, often there is a conflict becausethere are multiple classes named Log. This is where you have to use yourcommon sense and figure it out. In this case, the ambiguity is betweenthe Android Log and Apache Log classes, so choice should be that Log takes different severity levels. .d() is for debug level, but you can alsospecify .e() for error, .w() for warning, or .i() for info. There’s also a .wtf() severitylevel for errors that should never happen. (It stands for What a Terrible Failure, in caseyou were wondering.) Eclipse color-codes log messages based on their severity ’s Organize Imports tool can sometimes lead to hard-to-findproblems. For example, if your project doesn’t have generated(which might happen because there’s an earlier problem with one of theXML resources), then Organize Imports will import the other R class is part of the Android framework and has the samename as your local R class, making it hard to notice. So, if you have manycompilation errors around your references to R resources, check is not Android system log is outputted to LogCat, a standardized system-wide loggingmechanism. LogCat is readily available to all Java and C/C++ code. The developer caneasily view the logs and filter their output based on severity, such as debug, info,warning, or error, or based on custom-defined tags. As with most things in Androiddevelopment, there are two ways to view the LogCat: via Eclipse or via the |Chapter 6: Android User Interface
LogCat from the Eclipse DDMS perspectiveTo view LogCat in Eclipse, you need to open the LogCat View (see Figure 6-6). Youcan switch to the DDMS perspective by clicking on the DDMS button in the top-rightcorner of Eclipse:or by selecting Window→Open Perspective→DDMS in the Eclipse stands for Dalvik Debug Monitor Server. DDMS is the connection between yourapplication running on the device and your development environment, such as 6-6. LogCat in EclipseYou can define filters for LogCat as well. Click on the little green plus button, and theLogCat Filter dialog will come up (see Figure 6-7). You can define a filter based on atag, severity level, or process ID. This will create another window within LogCat thatshows you only the log entries that match your in Android|63
Figure 6-7. LogCat FilterDDMS might not show up in the top-right corner if you haven’t used itbefore. If that’s the case, go to Window→Open Perspective and chooseDDMS there. From there on, it should show up in your window tab from the command lineJust like all the tools, anything you can do in Eclipse also can be done from the com-mand line. To view LogCat, open up your terminal window and type:[user:~]> adb logcatThis will give you the tail of the current LogCat and will be updated as your devicekeeps generating log entries. You can also filter log entries on the command line, butthe syntax is not the most intuitive. To only see StatusActivity-tagged entries, youspecify StatusActivity:*, meaning you want all severity levels for this tag. However,you also have to specify what you don’t want to see. To do that, you add *:S, meaningsilence all other tags. The following command line illustrates that:[user:~]> adb logcat StatusActivity:* *:SI find it useful to keep a command-line window open with adb logcatrunning in it at all times. This makes it easy for me to quickly see what’sgoing on with my app and is certainly much faster than switching to theDDMS perspective in |Chapter 6: Android User Interface
Threading in AndroidA thread is a sequence of instructions executed in order. Although each CPU can proc-ess only one instruction at a time, most operating systems are capable of handlingmultiple threads on multiple CPUs, or interleaving them on a single CPU. Differentthreads need different priorities, so the operating system determines how much timeto give each one if they have to share a Android operating system is based on Linux and as such is fully capable of runningmultiple threads at the same time. However, you need to be aware of how applicationsuse threads in order to design your application ThreadBy default, an Android application runs on a single thread. Single-threaded applicationsrun all commands serially, meaning the next command is not completed until the pre-vious one is done. Another way of saying this is that each call is single thread is also known as the UI thread because it’s the thread that processesall the user interface commands as well. The UI thread is responsible for drawing allthe elements on the screen as well as processing all the user events, such as touches onthe screen, clicks of the button, and so on. Figure 6-8 shows the execution of our codeon a single UI 6-8. Single-threaded executionThe problem with running StatusActivity on the single thread is our network call toupdate the status. As with all network calls, the time it takes to execute is outside ofour control. Our call to () is subject to all the network availabilityand latency issues. We don’t know whether the user is on a super-fast WiFi connectionor is using a much slower protocol to connect to the cloud. In other words, our appli-cation cannot respond until the network call is Android system will offer to kill any application that is not re-sponding within a certain time period, typically around five seconds foractivities. This is known as the Application Not Responding dialog, orANR for short (see Figure 6-9).Threading in Android|65Download from Wow! eBook <>
Figure 6-9. Application Not Responding dialogMultithreaded ExecutionA much better solution is to have the potentially long operations run on a separatethread. When multiple tasks run on multiple threads at the same time, the operatingsystem slices the available CPU so that no one task dominates the execution. As a result,it appears that multiple tasks are running in parallel at the same our example, we could put the actual network call for updating our status in thecloud in a separate thread. That way our main UI thread will not block while we’rewaiting for the network, and the application will appear much more responsive. Wetend to talk of the main thread as running in the foreground and the additional threadsas running in the background. They’re really all equal in status, alternating their exe-cution on the device’s CPU, but from the point of view of the user, the main thread isin the foreground because it deals with the UI. Figure 6-10 shows the execution of ourcode’s two threads—the main UI thread, as well as the auxiliary thread we use toperform potentially long-running network 6-10. Multithreaded executionThere are multiple ways of accomplishing multithreading. Java has a Thread class thatallows for many of these operations. We could certainly use any of the regular Javafeatures to put the network call in the , using the standard Java Thread class is problematic because another threadis not allowed to update the elements in the main UI thread. This makes sense becauseto update the UI thread, we would need to synchronize with the current state of itsobjects, and that would be a job on its addition to standard Java threading support, Android provides the utility classAsyncTask specifically designed for this |Chapter 6: Android User Interface
AsyncTaskAsyncTask is an Android mechanism created to help handle long operations that needto report to the UI thread. To take advantage of this class, we need to create a newsubclass of AsyncTask and implement the doInBackground(), onProgressUpdate(), andonPostExecute() methods. In other words, we are going to fill in the blanks for what todo in the background, what to do when there’s some progress, and what to do whenthe task ’ll extend our earlier example with an asynchronous posting to the cloud. The firstpart of Example 6-5 is very similar to the code in Example 6-3, but hands off the postingto the asynchronous thread. A new AsyncTask does the posting in the 6-5. , version 2package ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;public class StatusActivity2 extends Activity implements OnClickListener { private static final String TAG = "StatusActivity"; EditText editText; Button updateButton; Twitter twitter; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find views editText = (EditText) findViewById(); updateButton = (Button) findViewById(); (this); twitter = new Twitter("student", "password"); (" } // Asynchronously posts to twitter class PostToTwitter extends AsyncTask<String, Integer, String> { // // Called to initiate the background activityThreading in Android|67
@Override protected String doInBackground(String... statuses) { // try { status = (statuses[0]); return ; } catch (TwitterException e) { (TAG, ()); (); return "Failed to post"; } } // Called when there's a status to be updated @Override protected void onProgressUpdate(Integer... values) { // (values); // Not used in this case } // Called once the background activity has completed @Override protected void onPostExecute(String result) { // (, result, _LONG).show(); } } // Called when button is clicked public void onClick(View v) { String status = ().toString(); new PostToTwitter().execute(status); // (TAG, "onClicked"); }}The PostToTwitter class in this case is an inner class of StatusActivity. It also sub-classes AsyncTask. Notice the use of Java generics to describe the data types that thisAsyncTask will use in its methods. I’ll explain these three types next. The first datatype is used by doInBackground, the second by onProgressUpdate, and the third () is the callback that specifies the actual work to be done on theseparate thread, as if it’s executing in the background. The argument String... isthe first of the three data types that we defined in the list of generics for this innerclass. The three dots indicate that this is an array of Strings, and you have to declareit that way, even though you want to pass only a single () is called whenever there’s progress in the task execution. Theprogress should be reported from the doInBackground() call. In this case, we do nothave any meaningful progress to report. If this example were instead a file download,for instance, this could report the percentage of completion or amount of data68|Chapter 6: Android User Interface
downloaded thus far. The actual data type—in this case, Integer—refers to thesecond argument in the generics definition of this () is called when our task completes. This is our callback method toupdate the user interface and tell the user that the task is done. In this particularcase, we are using a Toast feature of the Android UI to display a quick message onthe screen. Notice that Toast uses the makeText() static method to make the actualmessage. Also, do not forget to include show(); otherwise, your message will neverbe displayed, and there won’t be any errors—a hard bug to find. The argument thatthis method gets is the value that doInBackground() returns, in this case a String. Thisalso corresponds to the third generics datatype in the class we have our AsyncTask set up, we can use it. To use it, we simply instantiateit and call execute() on it. The argument that we pass in is what goes into thedoInBackground() call. Note that in this case we are passing a single string that isbeing converted into a string array in the actual method later on, which is an exampleof Java’s variable number of arguments this point, when the user clicks on the Update Status button, our activity will createa separate thread using AsyncTask and place the actual network operation on thatthread. When done, the AsyncTask will update the main UI thread by popping up aToast message to tell the user that the operation either succeeded or failed. This ap-proach makes our application much more responsive, and users should never get the“Application Not Responding: Force Close or Wait” message shown in Figure 6-9. Atthis point, our application looks like Figure 6-11 when 6-11. StatusActivity, part 1Threading in Android|69
Other UI EventsSo far, you have seen how to handle the click events by implementing OnClickListener and providing the onClick() method, which is invoked when the button isclicked. Imagine that we want to provide a little counter telling the user how manycharacters of input are still available out of the maximum of 140. To do that, we needanother type of provides many different listeners for various events, such as touch, click, andso on. In this case, we’re going to use TextWatcher to watch for text changes in the edittext field. Steps for this listener are similar to the steps for OnClickListener and manyother the user’s standpoint, we’ll add another TextView to our layout to indicate howmany characters are still available. This text will change color, from green to yellow tored, as the user approaches the 140-character Java, we’ll implement TextWatcher and attach it to the field where the user is typingthe actual text. The TextWatcher methods will be invoked as the user changes the text,and based on the amount of text entered, we’ll update the counter. See Example 6-6. res/layout/<?xml version="" encoding="utf-8"?><!-- Main Layout of Status Activity --><LinearLayout xmlns:android=" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <!-- Title TextView--> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:text="@string/titleStatus" android:textSize="30sp" android:layout_margin="10dp" /> <!-- Text Counter TextView --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:id="@+id/textCount" android:text="000" android:layout_marginRight="10dp" /> <!-- Status EditText --> <EditText android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:hint="@string/hintText" android:id="@+id/editText" android:gravity="top|center_horizontal"></EditText> <!-- Update Button --> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/buttonUpdate" android:textSize="20sp" android:id="@+id/buttonUpdate"></Button>70|Chapter 6: Android User Interface
</LinearLayout>New TextView that represents how many characters are still available for the user totype. We start at 140 and then go down as the user enters version of StatusActivity shown in Example 6-7 implements the TextWatcher in-terface, and the new methods in this example appear at the end of the class. Initiallythe text of the counter is in green to indicate we can keep on typing. As we approachthe maximum, the text turns yellow and eventually changes to red to indicate we arebeyond the maximum message 6-7. , final versionpackage ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;public class StatusActivity extends Activity implements OnClickListener, TextWatcher { // private static final String TAG = "StatusActivity"; EditText editText; Button updateButton; Twitter twitter; TextView textCount; // /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find views editText = (EditText) findViewById(); updateButton = (Button) findViewById(); (this); textCount = (TextView) findViewById(); // ((140)); // (); // Other UI Events|71
(this); // twitter = new Twitter("student", "password"); (" } // Called when button is clicked public void onClick(View v) { String status = ().toString(); new PostToTwitter().execute(status); (TAG, "onClicked"); } // Asynchronously posts to twitter class PostToTwitter extends AsyncTask<String, Integer, String> { // Called to initiate the background activity @Override protected String doInBackground(String... statuses) { try { status = (statuses[0]); return ; } catch (TwitterException e) { (TAG, ()); (); return "Failed to post"; } } // Called when there's a status to be updated @Override protected void onProgressUpdate(Integer... values) { (values); // Not used in this case } // Called once the background activity has completed @Override protected void onPostExecute(String result) { (, result, _LONG).show(); } } // TextWatcher methods public void afterTextChanged(Editable statusText) { // int count = 140 - (); // ((count)); (); // if (count < 10) (); if (count < 0) (); } public void beforeTextChanged(CharSequence s, int start, int count, int after) { // }72|Chapter 6: Android User Interface
public void onTextChanged(CharSequence s, int start, int before, int count) { // }}We declare that StatusActivity now implements TextWatcher. This means we needto actually provide the implementation for this interface, which we do later on inthis is our text view, defined in Example , we need to find the textCount in the inflated set the initial text to 140 because that’s the maximum length of a status messagein our app. Note that TextView takes text as value, so we convert a number to textCount field will change color dynamically based on the number of remainingcharacters. In this case, we start with green. Notice that the Color class is part of theAndroid framework and not Java. In other words, we’re using and not . is one of the few colors defined as aconstant in this class (more on colors in the next section).Here we attach TextWatcher to our editText field. In other words, editText will callthe TextWatcher instance, in this case this, which refers to this object () is one of the methods provided by the TextWatcher method is called whenever the text changes in the view that this TextWatcher iswatching. In our case, whenever the user changes the underlying text in editText,this method is invoked with the current we do some math to figure out how many characters are left, given the 140-character , based on the availability of the text, we update the color of the counter. So, ifmore than 10 characters are available, we are still in the green. Fewer than 10 meanswe are approaching the limit, thus the counter turns yellow. If we are past the limitof 140 characters, the counter turns method is called just before the actual text replacement is completed. In thiscase, we don’t need this method, but as part of implementing the TextWatcher in-terface, we must provide its implementation, event though it’s , we are not using onTextChanged() in this case, but must provide its 6-12 shows what the TextWatcher looks like in our appli-cation when UI Events|73
Figure 6-12. StatusActivity, part 1Adding Color and GraphicsOur application works well, but it’s a bit dull looking. A little bit of color and somegraphics could go a long way. Android offers a lot of support to make your applicationsnazzy. We’re going to see some basics ImagesFor starters, we want to add a background to our screen. This background is going tobe some kind of graphics file. In Android, most images go to a resource folder calleddrawable. You may notice that you already have three folders with this name:•/res/drawable-hdpi for devices with high-density screens•/res/drawable-mdpi for devices with medium-density screens•/res/drawable-ldpi for devices with low-density screensWe are going to create another drawable folder called simply /res/drawable. To do that,right-click on the res folder and choose New→Folder. For the name, enter can now put your graphics that are independent of screen density in this ’re going to assume you found some cool background graphics and that you savedthe file in this new folder under the name . Although Android supportsmany different file formats, PNG is preferred to the GIF standard because PNG is loss-less and doesn’t require any patent |Chapter 6: Android User InterfaceDownload from Wow! eBook <>
Although PNG officially stands for Portable Network Graphics, it is alsocommonly known as PNG’s Not Gif, to reflect its departure from thecontroversial GIF that all resources are being “watched” by Eclipse, and the moment we putsomething in there, Eclipse will use its Android SDK tools to update the R class auto-matically. So at this point, we’ll have a reference to and coulduse this resource from Java. But we won’ are going to update the status activity layout file res/layout/ next. Ourgoal is to make this background file the background graphic for the entire screen. Todo that, we’ll update the top layout in our file and set its background to point to thisnew background PNG file, which means we have to open the layout. Nowwe have two ways of adding the background to the top the WYSIWYG editor in EclipseOne way is to use Eclipse’s WYSIWYG tool, as shown in Figure 6-13. In this tool, weneed to first select the main layout, which might be difficult since many other compo-nents are in front of it. The red border indicates which view or layout is way of making your selection is to open up your Outline view in Eclipse andselect the top element there. This view might not be currently visible in your Eclipse,depending on how you arranged the many available windows. One sure way to get theOutline view is to go to Window→Show View→Outline and open it up that way. Onceyou open this view, you can select the top layout, in this case our LinearLayout. Youwill know it’s selected if a red border is around your entire , you want to open up the Properties view in Eclipse. Again, this view might alreadybe opened, but if it’s not visible as a window in Eclipse, go to Window→ShowView→Other, and under the General section, pick Properties. This will open up a viewin which you can change various properties for this particular property we want to modify is background. You can now click on the little …button, which will bring up the Reference Chooser dialog (see Figure 6-14). In thisdialog, choose Drawable→ will set the background of your top layout to @drawable/background. As you recall,this is the way that one XML resource refers to another resource. In this case, layout is referring to the drawable. Notice that we do notuse extensions when referring to other file resources. Android figures out the bestfile format automatically, in case there are files with the same name but Color and Graphics|75
Figure 6-13. Eclipse Graphical Layout EditorUpdating directly in XML codeAnother approach is to go straight into the XML code and make changes there. Re-member that everything you can do with Eclipse tools, you can also do in a plain-texteditor. To switch to the XML code view, select the tab at the bottom of thewindow, next to the Layout tab. This will open up the file with your standard this case, to add the background resource to our entire activity, we simply addandroid:background="@drawable/background" to our <LinearLayout> now on, we’re going to be making changes directly in the XML code because it’smuch simpler to explain. Also, the WYSIWYG editor can do only so much, and oftenyou run into its ColorWe now have the background for the entire screen, but what about the actual text boxthat users type the text into? The current design is stock. We could improve on it byadding some color and |Chapter 6: Android User Interface
Figure 6-14. Reference ChooserAndroid uses the standard RGB color set, but it also optionally expands it with an Alpha channel. So, you can express color as RGB or ARGB, where A is the amount oftransparency, R is the amount of red, G is for green, and B stands for blue. The com-bination of these three colors and optional transparency gives you every conceivablecolor from white to black, and from opaque to fully transparent! That’s the whole pointof ARGB. Of course, the granularity isn’t exactly what Monet would be happy with;each value has only 256 of each channel can be represented either as values between 0 and 255 or byusing the hexadecimal system values between 0 and FF. So, the actual values could beAARRGGBB, where each letter can be replaced with a value between 0 and F. There’salso a shorter version of ARGB, where each value is repeated. For example, #3A9F isthe same as #33AA99FF and corresponds to #33 for alpha, #AA for red, #99 for green,and #FF for blue. Notice that we use the # symbol in front of hexadecimal values todistinguish them from decimal , we could update the background of our EditText element to be #cfff, which is asomewhat transparent white , we can update the color of the title text by changing the textColor property forthat TextView. A good color would be white, for example. One way to specify white is#fff, but alternatively we could enter @android:color/white. The android: part of thatAdding Color and Graphics|77
statement refers to the Android operating system’s set of resources, in this case a pre-defined color white. Example 6-8 shows these new additions to our 6-8. res/layout/<?xml version="" encoding="utf-8"?><!-- Main Layout of Status Activity --><LinearLayout xmlns:android=" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="@drawable/background"><!----> <!-- Title TextView--> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center" android:text="@string/titleStatus" android:textSize="30sp" android:layout_margin="10dp" android:textColor="@android:color/white" /><!----> <!-- Text Counter TextView --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:id="@+id/textCount" android:text="000" android:layout_marginRight="10dp" /> <!-- Status EditText --> <EditText android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" android:hint="@string/hintText" android:id="@+id/editText" android:gravity="top|center_horizontal" android:background="#cfff" /><!----> <!-- Update Button --> <Button android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/buttonUpdate" android:textSize="20sp" android:id="@+id/buttonUpdate" /></LinearLayout>We set the background of the main layout to point to the file inour /res/drawable/ set the color of the title text to point to the color defined in the system colorresource set the background of the EditText area to a transparent white by specifying#cfff, a hexadecimal ARGB this point you’ve seen multiple ways to specify colors for different properties ofvarious views in your activity. Android offers many properties and many differentwidgets. You should be able to extrapolate from this how to set other properties andmake your application UI look exactly the way you |Chapter 6: Android User Interface
Alternative ResourcesAndroid supports multiple competing sets of resources. For example, you could havemultiple versions of a file, layout, or image. Youmight want multiple versions of same resource so that the best version can be usedunder different circumstances. We touched on this in “Adding Images” on page that your application is used in another country with a different language. Inthat case, you could provide a version specifically for that language. Orimagine that a user runs your application on a different device, with a different screenthat has more pixels. In that case, you’d want versions of your images specifically forthis screen’s pixel density. Similarly, users might simply rotate the device from portraitto landscape mode. Our application will redraw properly, but there are further en-hancements we could make to the layout of the UI given the orientation of the provides for all these cases in an elegant way. Basically, you simply need tocreate alternative folders for specific constraints. For example, our standard layoutfiles go into the /res/layout folder, but if we wanted to provide an alternative layoutspecifically for landscape mode, we’d simply create a new file called /res/layout-land/. And if you wanted to provide a translated version of your filefor users who are in a French-speaking part of Canada, you’d put it in file called res/values-fr-rCA/ you see from these examples, alternative resources work by specifying the qualifiersin the names of their resource folders. In the case of the French Canadian strings, An-droid knows that the first qualifier -fr refers to language, and the second qualifier-rCA specifies that the region is Canada. In both cases, we use two-letter ISO codes tospecify the country. So in this case, if the user is in Quebec and her device is configuredto favor the French language, Android will look for string resources in the /res/values-fr-rCA/ file. If it doesn’t find a specific resource, it will fall back to the de-fault /res/values/ file. Also, if the user in France, in this case Android will usethe default resource, because our French Canadian qualifiers do not match French qualifiers, you can create alternative resources for languages and regions, screensizes and orientations, device input modes (touch screen, stylus), keyboard or no key-board, and so on. But how do you figure out this naming convention for resource foldernames? The easiest solution is to use Eclipse’s New Android XML File dialog (seeFigure 6-15). To open the New Android XML File dialog, choose File→New…→AndroidXML File from the Eclipse Resources|79
Figure 6-15. Alternative resources with New Android XML File dialogOptimizing the User InterfaceThe user interface is one of the most expensive parts of a typical Android create a simple screen, your application has to inflate the XML from resources. Foreach element, it has to create a new Java object and assign its properties to it. Then, itneeds to draw each widget on the screen. All this takes many computing this, it is worth keeping in mind few optimization points. You may want to tryto limit the number of widgets you have on the screen. This is specially true when youare using nested layouts to achieve a desired look. This layout approach can sometimesget out of control, and if you are nesting unnecessary objects in a loop (say, displayingrows of data on the screen), then the number of widgets quickly explodes, and youruser interface becomes |Chapter 6: Android User Interface
Generally, you want your structure to be flat instead of deep. You can accomplish thisby replacing nested layouts with relative ViewerThere’s a very useful tool that ships with the Android SDK called Hierarchy Viewer(see Figure 6-16). Go ahead and start it; it is in your SDK/tools Viewer allows you to attach to any Android device, emulator, or physicalphone and then introspect the structure of the current view. It shows you all the widgetscurrently loaded in memory, their relationships to each other, and all their can introspect not just your screens, but the screens of any application on yourdevice. This is also a good way to see how some other applications are 6-16. Hierarchy ViewerOptimizing the User Interface|81
SummaryBy the end of this section, your application should run and should look like Fig-ure 6-17. It should also successfully post your tweets to your Twitter account. You canverify it is working by logging into an online service of your choice that supports theTwitter API, such as , using the same username and pass-word that are hardcoded in the 6-17. StatusActivityFigure 6-18 illustrates what we have done so far as part of the design outlined inFigure 6-18. Yamba completion82|Chapter 6: Android User InterfaceDownload from Wow! eBook <>
CHAPTER 7Preferences, the Filesystem,the Options Menu, and IntentsIn this chapter, you will learn how to create preferences for your application, how thefilesystem is organized, and how to use intents and the options menu to jump from oneactivity to are user-specific settings for an application. Preferences usually consist ofsome configuration data as well as a user interface to manipulate that the user interface point of view, preferences can be simple text values, check-boxes, selections from a pull-down menu, or similar items. From a data point of view,preferences are a collection of name-value pairs, also known as key-value or attribute-value pairs. The values are basic data types, such as integers, booleans, and micro-blogging application needs to connect to a specific server in the cloud usingspecific user account information. For that, Yamba needs to know the username andpassword for that account as well as the URL of the server it’s connecting to. This URLis also known as the API root. So, in our case, we’ll have three fields where the user canenter and edit his username, password, and the API root. This data will be stored enable our app to handle user-specific preferences, we need to build a screen toenter the information, Java code to validate and process that information, and somekind of mechanism to store this this sounds like a lot of work, but Android provides a framework to help streamlineworking with user preferences. First, we’ll define what our preference data looks likein a Preference resource create preferences for our application, we need to:83
a Preference resource file called the file that inflates that resource this new activity with the a way to start that activity from the rest of the ResourceWe are going to start by creating , a resource file that outlines what our pref-erence screen will look like. The easiest way to create it is to use the New Android XMLFile tool in Eclipse, as shown in Figure 7-1. To start the New Android XML File dialog,go to File→New→Android XML File, or click on the little a+ icon in Eclipse’s top menubar: The key is to give the new file a name, in this case , and to choose Preferencefor the type of resource. The tool should automatically suggest creating this new file inthe /res/xml folder and that the root element for the XML file should bePreferenceScreen. As discussed before in “Alternative Resources” on page 79, we couldcreate alternative versions of this same resource by applying various qualifiers, such asscreen size and orientation, language and region, ’re using Eclipse tools where applicable to get the job done morequickly. If you were to use another tool, you’d have to create this filemanually and put it in the correct you click on Finish, Eclipse will create a new file for you and open it up. Eclipsetypically opens the XML files it knows about in its developer-friendly this view, you can create the username preference entry by selecting PreferenceScreenon the left, and then choosing Add→EditTextPreference. On the right-hand side, ex-pand the “Attributes from Preferences” section. Eclipse will offer you a number ofattributes to set for this all attributes are equally important. Typically, you will care about the following:KeyA unique identifier for each preference item. This is how we’ll look up a particularpreference preference name that the user will see. It should be a short name that fits ona single line of the preference short description of this preference item. This is optional, but using it is |Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
Figure 7-1. New Android XML FileFor the username preference, we’ll put “username” for its key. We will define the Titleand Summary in , as this is the best of modifying the file directly, you can use an Eclipse shortcut. Here’show it goes: on Browse and select New String…. This will open a dialog to create a newstring titleUsername for the . value and Username for the String OK, and this will insert a new string resource in can now pick that value from the list of these instructions for adding the Username preference item, you can now repeatthe same steps for Password and API Root can switch to the actual XML code by clicking on the tab at the bottom of thewindow, shown in Figure |85
Figure 7-2. in developer-friendly viewThe raw XML for the preference resource looks like the code shown in Example 7-1. res/xml/<?xml version="" encoding="utf-8"?><PreferenceScreen xmlns:android=" <EditTextPreference android:title="@string/titleUsername" android:summary="@string/summaryUsername" android:key="username"></EditTextPreference> <EditTextPreference android:title="@string/titlePassword" android:password="true" android:summary="@string/summaryPassword" android:key="password"></EditTextPreference> <EditTextPreference android:title="@string/titleApiRoot" android:summary="@string/summaryApiRoot" android:key="apiRoot"></EditTextPreference></PreferenceScreen><PreferenceScreen> is the root element that defines our main preference screen. It hasthree children, all <EditTextPreference>. This is simply a piece of editable text. Othercommon elements here could be <CheckBoxPreference>, <ListPreference>, and so main property of any of these elements is the key. The key is how we’ll look upthese values later on. Remember, preferences is just a set of name-value pairs at the endof the |Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
Like we said a couple of times earlier, although Eclipse does provide developer-friendlytools to manage XML files, you often run into certain limitations with Eclipse. Forexample, we would like to hide the actual text that the user types in the password field,which is a common practice. Android does provide support for that, but Eclipse toolshaven’t yet integrated this function. Since we can always edit the XML directly, in thiscase we add an android:password="true" property to our password property. This willcause the password to be masked while the user types it that we have the preferences defined in their own XML resource file, we can createthe activity to display these preferences. You may recall from <<Activities> that everyscreen in an Android app is an activity. So, to display the screen where a user entersthe username and password for his online account, we’ll create an activity to handlethat screen. This will be a special preference-aware create an activity, we create a new Java class. In Eclipse, select your package underyour src folder, right-click on the package, and select New→Class. A New Java Classwindow will pop up. You just need to enter PrefsActivity for the Name and clickFinish. This will create a file under your package in your source PrefsActivity class, shown in Example 7-2, is a very simple Java file. This is be-cause we inherit from PreferenceActivity, an Android framework class that knowshow to handle 7-2. ;import ;import ;public class PrefsActivity extends PreferenceActivity { // @Override protected void onCreate(Bundle savedInstanceState) { // (savedInstanceState); addPreferencesFromResource(); // }}Unlike regular activities, PrefsActivity will subclass (., extend) the PreferenceActivity like any other activity, we override the onCreate() method to initialize regular activities that usually call setContentView(), our preference activitywill set its content from the file via a call to addPreferencesFromResource().Preferences|87
If you don’t want to type the long signature of onCreate() and othermethods that we often have to implement or override, you could use an Eclipse tool to help you with that. While in your fileand after you add ...extends PreferenceActivity..., you can chooseSource→Override/Implement Methods…. This will bring up a dialogbox with an appropriate selection of methods you could override orimplement, given that you are subclassing the PreferenceActivity here, you can choose onCreate(), and Eclipse will insert the stub forthis method into your the Manifest FileWhenever we create one of these main building blocks (Activities, Services, BroadcastReceivers, or Content Providers), we need to define them in the . In this case, we have a new PrefsActivity and must add it to the manifest like with any Android XML file, opening in Eclipse typicallywill bring up the developer-friendly view of that file. In this file view, you could choosethe Application tab, and then under Application Nodes, choose Add→Activity andname it ., we can also do this straight from the raw XML by clicking on the tab on the bottom of this window. I find that Eclipse is useful when itcomes to creating XML files, but often editing the raw XML is faster and gives youmuch more editing code in Eclipse, you can use the Ctrl-space bar key short-cut to invoke the type-ahead feature of Eclipse. This is very useful forboth XML and Java code and is context-sensitive, meaning Eclipse issmart enough to know what could possibly be entered at that point inthe code. Using Ctrl-space bar makes your life as a programmer mucheasier because you don’t have to remember long method names andtags, and it helps avoid our manifest file now looks like the code shown in Example 7-3. <?xml version="" encoding="utf-8"?><manifest xmlns:android=" android:versionCode="1" android:versionName="" package=""> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".StatusActivity" android:label="@string/titleStatus"> <intent-filter> <action android:name="" /> <category android:name="" /> </intent-filter>88|Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
</activity> <activity android:name=".PrefsActivity" android:label="@string/titlePrefs" /> <!-- --> </application> <uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="" /></manifest>Defines the new now have a new preference activity, but there’s no good way of getting to it need a way to launch this new activity. For that, we use the options Options MenuThe options menu is an Android user interface component that provides standardizedmenus to applications. The menus appear at the bottom of the screen when the userpresses the Menu button on the add support for the options menu to an application, we need to do the following: the resource where we specify what the menu consists onCreateOptionsMenu() to the activity that should have this menu. This iswhere we inflate the handling of menu events in onOptionsItemSelected().The Menu ResourceWe start by defining the menus in an XML resource for the options menu. Just likewith other Android XML files, we can use the little a+ icon in the Eclipse toolbar orchoose File→New…→Android XML to launch the New Android XML File dialog. Inthis dialog, enter “” in the file field, and for Type, select Menu. Click theFinish button, and Eclipse will create a new folder called /res/menu that contains file and will open this file in the developer-friendly view (see Figure 7-3).In this view, you can click on Add→Item, which will add a new menu item to yourmenu. In the Attributes section on the right, you can see over a dozen attributes thatwe can set for this menu item. Just like before, not all attributes are equally important:IdThe unique identifier of this resource. Just as when we designed the layout inChapter 6, this identifier is typically of the form @+id/someId , where someId is thename that you give it. This name should contain only letters, numbers, and theunderscore Options Menu|89
Figure 7-3. in developer-friendly viewTitleThe title of this menu as it will appear on the display. Keep in mind that screenspace typically is limited, so keep the title short. Additionally, you can provide a“Title condensed” attribute to specify a shorter version of the title that will beshown instead if space is limited. Just like before, best practice is to define theactual text value of the title in the resource and just reference it icon that displays along with the menu item’s title. Although not required, itis a very useful visual cue from a usability point of view. In this case it also illustrateshow to point to Android system next section describes these resources in more System ResourcesJust like your application can have resources, so can the Android system. Like mostother operating systems, Android comes with some preloaded images, graphics, soundclips, and other types of resources. Recall that our app resources are in /res/. To referto Android system resources, prefix them with the android: keyword in XML, for ex-ample, @android:drawable/ic_menu_preferences. If you are referring to an Android sys-tem resource from Java, then you use instead of the usual R |Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
The actual resource files are in your SDK, inside a specific platformfolder. For example, if you are using Android 9 (Gingerbread), the re-source folder would be android-sdk/platforms/android-9/data/res/.The raw XML of is shown in Example 7-4. res/menu/<?xml version="" encoding="utf-8"?><menu xmlns:android=" <item android:id="@+id/itemPrefs" android:title="@string/titlePrefs" android:icon="@android:drawable/ic_menu_preferences"></item></menu>As you can see, there’s just one <item> element within our <menu> element, making thisa single-item StatusActivity to Load the MenuRecall that the options menu is loaded by your activity when the user clicks on herdevice’s Menu button. The first time the Menu button is pressed, the system will callthe activity’s onCreateOptionsMenu() method to inflate the menu from the . This process is similar to inflating the user interface from layout resources,discussed in “The StatusActivity Java Class” on page 56. Basically, the inflater readsthe XML code, creates a corresponding Java object for each element, and sets each XMLobject’s properties that point on, the menu is in memory, and onCreateOptionsMenu() doesn’t getcalled again until the activity is destroyed. Each time the user selects a menu item,though, onOptionsItemSelected() gets called to process that click. We’ll talk about thisin the next need to update the StatusActivity to load up the options menu. To do that, addan onCreateOptionsMenu() method to StatusActivity. This method gets called only thefirst time the user clicks on the menu button:// Called first time user clicks on the menu button@Overridepublic boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); // (, menu); // return true; // }We get the MenuInflater object from the the inflater to inflate the menu from the XML must return true for this menu to be Options Menu|91
Update StatusActivity to Handle Menu EventsWe also need a way to handle various clicks on the menu items. To do that, we addanother callback method, onOptionsItemSelected(). This method is called every timethe user clicks on a menu item:// Called when an options item is clicked@Overridepublic boolean onOptionsItemSelected(MenuItem item) { switch (()) { // case : startActivity(new Intent(this, )); // break; } return true; // }Since the same method is called regardless of which item the user clicks, we need tofigure out the ID of that item, and based on that, switch to a specific case to handleeach item. At this point, we have only one menu item, but that might change in thefuture. Switching an item ID is a very scalable approach and will adapt nicely as ourapplication grows in startActivity() method in context allows us to launch a new activity. In thiscase, we are creating a new intent that specifies starting the PrefsActivity true to consume the event like before, you could use the Eclipse shortcut Source→Override/Implement Methods to add both onCreateOptionsMenu() andonOptionsItemSelected().Strings ResourceOur updated now looks like the code shown in Example 7-5. res/values/<?xml version="" encoding="utf-8"?><resources> <string name="app_name">Yamba 2</string> <string name="titleYamba">Yamba 2</string> <string name="hintText">Please enter your 140-character status</string> <string name="buttonUpdate">Update</string> <string name="titleStatus">Status Update</string> <string name="titlePrefs">Prefs</string> <string name="titleUsername">Username</string> <string name="titlePassword">Password</string>92|Chapter 7: Preferences, the Filesystem, the Options Menu, and IntentsDownload from Wow! eBook <>
<string name="titleApiRoot">API Root</string> <string name="summaryUsername">Please enter your username</string> <string name="summaryPassword">Please enter your password</string> <string name="summaryApiRoot">URL of Root API for your service</string></resources>You should be able to run your application at this point and see the new PrefsActivity by clicking on Menu→Prefs in StatusActivity (see Figure 7-4). Try changingyour username and password, then reboot your phone, restart the app, and verify thatthe information is still 7-4. PrefsActivityShared PreferencesNow that we have a preference activity and a way to save our username, password, andAPI root, it is time to make use of it. To programmatically access your preferences, we’lluse the SharedPreference class provided by the Android class is called SharedPreference because this preference is easily accessible fromany component of this application (activities, services, broadcast receivers, and contentproviders).In StatusActivity, add a definition for the prefs object globally to the class: SharedPreferences prefs;Now, to get the preference object, add the following to onCreate():@Override public void onCreate(Bundle savedInstanceState) { ... // Setup preferencesShared Preferences|93
prefs = (this); // (this); // }Each application has its own shared preferences available to all components ofthis application context. To get the instance of this SharedPreferences, weuse () and pass it this as the cur-rent context for this app. The name “shared” could be confusing. To clarify, it meansthat this preference object contains data shared by various parts of this applicationonly; it is not shared with any other user can and will change preferences. So we need a mechanism to notify thisactivity that the old values are stale. To do that, we register this, meaning ourStatusActivity with our shared preferences. For this to work, we’ll need toadd ...implements OnSharedPreferenceChangeListener to our class definition as wellas implement the required onSharedPreferenceChanged() method. This method willbe explained in a that we have the username, password, and API root coming from user-definedpreferences, we can refactor our Twitter code so it no longer hardcodes them. To dothat, we add a private method to StatusActivity responsible for returning a validtwitter object. This method lazily initializes twitter, which means that if twitter ex-ists, it returns it as-is; otherwise, the method creates it:private Twitter getTwitter() { if (twitter == null) { // String username, password, apiRoot; username = ("username", ""); // password = ("password", ""); apiRoot = ("apiRoot", " // Connect to twitter = new Twitter(username, password); // (apiRoot); // } return twitter;}Only if twitter is null (., undefined), we create the username and password from the shared preference object. The first pa-rameter in getString() is the key we assigned to each preference item, such asusername and password. The second argument is the default value in case such apreference is not found. Keep in mind that the first time a user runs your application,the preference file doesn’t exist, so defaults will be used. So, if the user hasn’t set upher preferences in PrefsActivity, this code will attempt to log in with an emptyusername and password, and thus fail. However, the failure will happen when theuser tries to do the actual status update because that’s how the jtwitter library log into the Twitter service with user-defined |Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
Remember that we need to update the actual service that we are using by updatingthe API root URL for that we don’t use the twitter object directly anymore, but instead call getTwitter()to get it. So, onClick() becomes like this:public void onClick(View v) { // Update twitter status try { getTwitter().setStatus(().toString()); } catch (TwitterException e) { (TAG, "Twitter setStatus failed: " + e); }}Note that although we moved the code where we initialize our connection to the cloud,we still need the AsyncTask to deal with the fact that this call is still blocking and maytake a while to complete, as it’s subject to network availability and we mentioned before when updating onCreate() and registering for preference up-dates, we need to handle what happens when the user changes his username or pass-word. By registering (this) inonCreate() and implementing OnSharedPreferenceChangeListener, we got a callbackmethod onSharedPreferenceChanged() that the system will invoke whenever preferen-ces change. In this method, we simply invalidate the twitter object, so the next timeit is needed, getTwitter() will recreate it:public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { // invalidate twitter object twitter = null;}The Filesystem ExplainedSo, where does the device store these preferences? How secure is my username andpassword? To answer that, we need to look at how the Android filesystem is the FilesystemThere are two ways for you to access the filesystem on an Android device: via Eclipseor the command Eclipse, we use the File Explorer view to access the filesystem. To open up the FileExplorer view, go to Window→Show View→Other…→Android→File Explorer. You canalso access the File Explorer view via the DDMS perspective. Select the DDMS per-spective icon in the top-right corner of Eclipse:The Filesystem Explained|95
or go to Window→Open Perspective→Other…→DDMS. If you have multiple devicesconnected to your workstation, make sure you select which one you are working within the Devices view. You should now be able to navigate through the device’s you prefer the command line, you can always use adb shell to get to the shell of thedevice. From there you can explore the filesystem like you would on any other PartitionsThere are three main parts of the filesystem on every Android device. As shown inFigure 7-5, they are:•The system partition (/system/)•The SDCard partition (/sdcard/)•The user data partition at (/data/)System PartitionYour entire Android operating system is located in the system partition. This is themain partition that contains all your preinstalled applications, system libraries, An-droid framework, Linux command-line tools, and so system partition is mounted read-only, meaning that you as developer have verylittle influence over it. As such, this partition is of limited interest to system partition in the Emulator corresponds to the file in your platformimages directory, located in the android-sdk/platforms/android-8/images/ PartitionThe SDCard partition is a free-for-all mass storage area. Your app can read files fromthis partition as well as write files to it if it holds WRITE_TO_EXTERNAL_STORAGE is a great place to store large files, such as music, photos, videos, and similar that since the FroYo version of Android, the /sdcard mount point appears in theEclipse File Explorer under the /mnt/sdcard location. This is due to the new feature inFroYo that allows for storing and running applications on the SDCard as an app developer, the SDCard partition is very useful and important to you. At thesame time, this partition is not very partition typically corresponds to in your Android Virtual Device(AVD) directory. This directory is in your ~/.android/avd/ folder and will have a sub-directory for each specific virtual device. On the physical device, it is an actual SD |Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
Figure 7-5. The filesystem as seen via File Explorer in EclipseThe User Data PartitionAs user and app developer, the most important partition is the user data partition. Thisis where all your user data is stored, all the downloaded apps are located, and mostimportantly, all the applications’ data. This includes both preinstalled apps as well asuser-downloaded , while user apps are stored in the /data/app/ folder, the most important folder to usas app developers is the /data/data/ folder. More specifically, within this folder there’sa subfolder corresponding to each app. This folder is identified by the Java packageThe Filesystem Explained|97
that this app used to sign itself. Again, this is why Java packages are important toAndroid Android framework provides a number of handy methods as part of its contextthat help you access the user data filesystem from within your application. For example,take a look at getFilesDir().This partition typically corresponds to in your Android Virtual Device(AVD) directory. As before, this directory is in your ~/.android/avd/ folder and will havea subdirectory for each specific virtual you create a new app, you assign your Java code to a specific package. Typically,this package follows the Java convention of reverse domain name plus app name. Forexample, the Yamba app is in the package. So, once installed,Android creates a special folder just for this app under /data/data/ This folder is the cornerstone of our private, secured filesystem dedicated toeach will be subfolders in /data/data/ but they arewell-defined. For example, the preferences are in /data/data/ As a matter of fact, if you open up the DDMS perspective in Eclipse andselect File Explorer, you can navigate to this partition. You will probably see file in there. You could pull this file and ex-amine it, or you could use adb shell is another one of those common adb subcommands to access the shell of yourdevice (either physical or virtual). For instance, you could just open up your command-line terminal and type:[user:~]> adb shell# cd /data/data/# cat <?xml version='' encoding='utf-8' standalone='yes' ?><map><string name="password">password</string><string name="apiRoot"> name="username">student</string></map>#This XML file represents the storage for all our preference data for this application. Asyou can see, our username, password, and API root are all stored in SecuritySo, how secure is this? This is a common question posed by security folks. Storingusernames and passwords in clear text always raises answer this question, I usually compare it to finding someone’s laptop on the we can easily gain access to the “hard drive” via the adb tool, that doesn’t98|Chapter 7: Preferences, the Filesystem, the Options Menu, and Intents
mean we can read its data. Each folder under /data/data/ belongs to a separate useraccount managed by Linux. Unless our app is that app, it won’t have access to thatfolder. So, short of us reading byte-by-byte on the physical device, even clear-text datais the Emulator, we have root permissions, meaning we can explore the entire file-system. This is useful for development this point, the user can specify her username and password for the micro-bloggingsite. This makes the app usable to way more people than the previous version in whichthis information was 7-6 illustrates what we have done so far as part of the design outlined earlier inFigure 7-6. Yamba completionSummary|99
CHAPTER 8ServicesServices are among the main building blocks in Android. Unlike an activity, a servicedoesn’t have a user interface; it is simply a piece of code that runs in the backgroundof your are used for processes that should run independently of activities, which maycome and go. Our Yamba application, for example, needs to create a service to peri-odically connect to the cloud and check for new statuses from the user’s friends. Thisservice will be always on and always running, regardless of whether the user ever startsthe like an activity, a service has a well-defined life cycle. You as the developer get todefine what happens during transitions between states. Whereas an activity’s state ismanaged by the runtime’s ActivityManager, service state is controlled more by , whenever an activity needs your service, it will invoke it through an intentthat starts the service. An already running service can receive the start message repeat-edly and at unanticipated times. You can also stop a service, which is also called de-stroying service can be bound or unbound. Bound services can provide more specific APIs toother applications via an interface called AIDL (Android Interface Definition Language;see Chapter 14). For now, we’ll focus on unbound services, where the life cycle of aservice is not tied to the life cycle of the activities that started them. The only states forbound services are started and stopped (destroyed).In this chapter, you will create a service. The purpose of this service is to run in thebackground and update your app with the latest timeline from the user’s Twitter ac-count. Initially, the service will just print your friends’ timeline to the logfile. The servicewill create a separate thread, so you will learn about concurrency in this chapter as will also learn about Toasts and understand the context in which services andactivities the end of this chapter, you will have a working app that can both post to Twitterand periodically check what friends are up
The Yamba Application ObjectWe now have support for preferences in our StatusActivity. We also have the utilitymethod getTwitter() to help us get the actual Twitter object that we use to connect tothe online is likely that we’ll need some of these features in other parts of our application. Insteadof copying them from file to file, it would be useful if we could put this code in a separateplace that is accessible by most parts of our app. Android provides just a place for thatin the form of an Application Application object represents the common state of your entire application. As longas any part of your application is running, the application object will be created. Mostapplications use the default class that the framework pro-vides. However, you can implement your own instance of this object and add the com-mon app features to are going to create our own instance of this object and call it YambaApplication. Thesteps for creating the YambaApplication class are: the Java class representing the new class with the YambaApplication ClassFirst, we are going to create a new Java class in the same package as the rest of ourclasses. We’ll call this class YambaApplication, and it will extend the Application baseclass from the , we’re going to move common tasks into this base object. We anticipate that moreparts of our application are going to need to connect to the online service as well asread the preference in Example 8-1 that the Application object has the usual onCreate() method,but it also provides the onTerimante() callback as a place to implement any cleanupthat we might want to do. At this point we don’t have anything to clean up, but this isa good opportunity to create some logging information so we can see when the appli-cation actually shuts down. We might expand on this 8-1. ;import ;import ;import ;import ;import ;import ;102|Chapter 8: ServicesDownload from Wow! eBook <>
import ;public class YambaApplication1 extends Application implements OnSharedPreferenceChangeListener { // private static final String TAG = (); public Twitter twitter; // private SharedPreferences prefs; @Override public void onCreate() { // (); = (this); (this); (TAG, "onCreated"); } @Override public void onTerminate() { // (); (TAG, "onTerminated"); } public synchronized Twitter getTwitter() { // if ( == null) { String username = ("username", ""); String password = ("password", ""); String apiRoot = ("apiRoot", " if (!(username) && !(password) && !(apiRoot)) { = new Twitter(username, password); (apiRoot); } } return ; } public synchronized void onSharedPreferenceChanged( SharedPreferences sharedPreferences, String key) { // = null; }}For YambaApplication to be a valid application object, it must subclass the frame-work-provided Application class. Notice that we also moved responsibility for beingthe OnSharedPreferenceChangeListener from StatusActivity to and SharedPreferences are now part of this common object and no longerpart of () is called when the application is first created. The application is createdwhenever any of its parts, such as an activity or a service, is first Yamba Application Object|103
onTerminate() is a placeholder for us to do some cleanup when the application isabout to shut down. At this point, we just use it for logging also moved getTwitter() from StatusActivity to YambaApplication because it’sgoing to be used by other parts of our application and we want to maximize the codereuse. Notice the use of the synchronized keyword here. A synchronized method inJava means that only one thread can be inside of such a method at one time. This isnow important because this method could be used by different threads that ourapplication might () is now also part of YambaApplication instead that we have YambaApplication and have moved some responsibilities fromStatusActivity to this new class, we can simplify StatusActivity even further, as shownin Example 8-2. StatusActivity using YambaApplication... status = ((YambaApplication) getApplication()) .getTwitter().updateStatus(statuses[0]); // ...We now use the getTwitter() method from YambaApplication instead of keeping itlocally. This way, the same method can be reused by other parts of the applicationthat need access to the cloud the Manifest FileThe final step is to tell our application to use the YambaApplication class instead of thedefault Application class. To do that, we need to update the Android manifest file andadd an attribute to the <application> element:<?xml version="" encoding="utf-8"?><manifest xmlns:android=" android:versionCode="1" android:versionName="" package=""> <application android:icon="@drawable/icon" android:label="@string/app_name" android:name=".YambaApplication"> <!----> ... </application> ...</manifest>The attribute android:name=".YambaApplication" in the <application> element tellsthe Android system to instantiate our YambaApplication object as the , at this point we have successfully moved common functionality fromStatusActivity to YambaApplication. This process is also known as code refactoringand is a good practice as we keep on adding new features to our |Chapter 8: Services
Simplifying StatusActivityNow that the functionality for getting the Twitter object has been moved toYambaApplication, we can simplify StatusActivity to refer to that functionality ’s what our new PostToTwitter AsyncTask would look like:class PostToTwitter extends AsyncTask<String, Integer, String> { // Called to initiate the background activity @Override protected String doInBackground(String... statuses) { try { YambaApplication yamba = ((YambaApplication) getApplication()); // status = ().updateStatus(statuses[0]); // return ; } catch (TwitterException e) { (TAG, "Failed to connect to twitter service", e); return "Failed to post"; } } ...}We get the reference to the Application object via the getApplication() call in thecurrent context. Since we have a custom YambaApplication object, we need to castthe generic Application into we have the reference to our application object, we can call its methods, suchas the getTwitter() have seen how we have refactored our StatusActivity to move some of the com-mon functionality into a shared Application object. Now that we have done that, wecan create our UpdaterService, which will use some of this common mentioned in the introduction to this chapter, we need a service to run as an always-on background process pulling the latest Twitter statuses into a local database. Thepurpose of this pull mechanism is to cache updates locally so our app can have dataeven when it’s offline. We’ll call this service to creating a service are: the Java class representing your the service in the Android manifest the |105
Creating the UpdaterService Java ClassThe basic procedure for creating a service, as with activities and other main buildingblocks, is to subclass a Service class provided by the Android create the new service, we need to create a new Java file. Go ahead and select yourJava package in the src folder, right-click and choose New→Class, and type in“UpdaterService” as the class name. This will create a new file aspart of your may recall from “Services” on page 31 that a typical service goes through the lifecycle illustrated in Figure 8-1. Service life cycleNext, we want to override some of the main life cycle methods:onCreate()Called when the service is created for the first timeonStartCommand()Called when the service is startedonDestroy()Called when the service is terminatedTo do that, you can use Eclipse tool Source→Override/Implement Methods and selectthose three this point, in the spirit of producing a minimally working app at each stage of learn-ing, we’ll write just a little code that logs a note in each of the overridden methods. Sothe shell of our service looks like the code in Example 8-3. , version 1package ;import ;import ;import ;import ;106|Chapter 8: Services
public class UpdaterService1 extends Service { static final String TAG = "UpdaterService"; // @Override public IBinder onBind(Intent intent) { // return null; } @Override public void onCreate() { // (); (TAG, "onCreated"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // (intent, flags, startId); (TAG, "onStarted"); return START_STICKY; } @Override public void onDestroy() { // (); (TAG, "onDestroyed"); }}As in all major classes, I like to add the TAG constant because I use () quite a () is used in bound services to return the actual implementation of somethingcalled a binder. Since we are not using a bound service, we can just return null () is called when the service is initially created. It is not called for subsequentstartService() calls, so it is a good place to do work that needs to be done only onceduring the life of a () is called each time the service receives a startService() intent. Aservice that is already stated could get multiple requests to start again, and each willcause onStartCommand() to () is called just before the service is destroyed by the stopService() re-quest. This is a good place to clean up things that might have been initialized inonCreate().Update the Manifest FileNow that we have the shell of our service, we have to define it in the manifest file, justlike any other main building block; otherwise, we won’t be able to call our open , click on the right-most tab to see the raw XML code,and add the following within the <application> element:UpdaterService|107
... <application android:icon="@drawable/icon" android:label="@string/app_name"> ... <service android:name=".UpdaterService" /> <!-- --> ... </application>...UpdaterService are equal to activities as Android building blocks, so they appear at the samelevel in the manifest Menu ItemsNow that we have the service defined and declared, we need a way to start and stop easiest way would be to add a menu button to our options menu that we havealready created. Later on, we’ll have a more intelligent way of starting services, but fornow this manual approach is easier to add start/stop menu buttons, we’ll add two more menu items to our , just as we created the Prefs menu item before in “The Menu Re-source” on page 89. The updated now looks like the code in Example 8-4. <?xml version="" encoding="utf-8"?><menu xmlns:android=" <item android:id="@+id/itemPrefs" android:title="@string/titlePrefs" android:icon="@android:drawable/ic_menu_preferences"></item> <!-- --> <item android:title="@string/titleServiceStart" android:id="@+id/itemServiceStart" android:icon="@android:drawable/ic_media_play"></item> <!-- --> <item android:title="@string/titleServiceStop" android:id="@+id/itemServiceStop" android:icon="@android:drawable/ic_media_pause"></item> <!-- --></menu>This is the item we defined in the previous ServiceStart item has the usual id, title, and icon attributes. This icon isanother Android system ServiceStop item is similar to the ServiceStart that the menu resource has been updated, it’s time to handle those items whenthe user clicks on |Chapter 8: Services
Update the Options Menu HandlingTo handle new menu items, we need to update the onOptionsItemSelected()method in StatusActivity, just as we did in “Update StatusActivity to Handle MenuEvents” on page 92. So open your file and locate the onOptionsItemSelected() method. We now have a framework in this method to support any numberof menu items. To add support for starting and stopping our service, we launch intentspointing to our UpdaterService via startService() and stopService() calls. The finalcode looks like this:// Called when an options item is clicked@Overridepublic boolean onOptionsItemSelected(MenuItem item) { switch (()) { case : startService(new Intent(this, )); // break; case : stopService(new Intent(this, )); // break; case : startActivity(new Intent(this, )); break; } return true;}Creates an intent to start UpdaterService. If the service doesn’t already exist, theruntime calls the service’s onCreate() method. Then onStartCommand() is called, re-gardless of whether this service is new or already , this uses the stopService() call to send an intent intended for UpdaterService. This will cause onDestroy() to be called on the service if the service isrunning. If it isn’t, nothing happens, and this intent is simply this example, we are using explicit intents to specify exactly which class the intentsare intended for, namely the ServiceAt this point, you can restart your application. Note that you do not need to restart theemulator. When your application starts up, click on the menu, and your new buttonsshould appear in the menu options. You can now freely click on the start and stopservice verify that your service is working, open up your LogCat and look for the appropriatelog messages that you generated in your service code. Remember from “Logging inAndroid” on page 62 that you can view the LogCat both in Eclipse and via the |109
Another way to verify that the service is running is to go to the Android Settings appand see whether it is listed. To do that, go to the Home screen, press Menu, and chooseSettings. Then go to Applications→Running services. You should see your service listed,as shown in Figure 8-2. Running servicesYour service is now working, although it’s not doing much at this in the ServiceBy design, our service is supposed to wake up every so often, check the online servicefor new status updates, an then go back to “sleep” for some time. And this work needsto keep on happening forever, until the service is stopped. A good way to implementthis is to have our service run in a loop and pause execution between iterations. Javaprovides a () method that we can use to make the currently running threadpause and relinquish CPU for some number of consideration to keep in mind is that the service could require a good deal oftime to make its connection to Twitter and pull in friends’ status data. The behaviorof networking calls depends on the type of network connection we have at the moment,the responsiveness of the server, and all sorts of other factors that collectively make upthe network we run our update operation on the default thread, any delay caused by the networkupdate will cause our user interface to block. This in turn will make our applicationappear sluggish to the user and may even lead to the Android system offering to killour application by bringing up the “Force Close or Wait” dialog window, as discussedin “Threading in Android” on page |Chapter 8: Services
The best solution to this problem is to put the actual work of the network update in aseparate thread. To do this, we can use standard Java threading support, as shown inExample 8-5. The work of a service should often be in a separate thread from the mainUI thread, regardless of how little time you expect the service to take. You always needto separate the noninteractive processing from user interaction. When you have net-work activity, as in Yamba, it’s even more important to keep it separate, but the prin-ciple applies to any 8-5. , version 2package ;import ;import ;import ;import ;public class UpdaterService2 extends Service { private static final String TAG = "UpdaterService"; static final int DELAY = 60000; // a minute private boolean runFlag = false; // private Updater updater; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { (); = new Updater(); // (TAG, "onCreated"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { (intent, flags, startId); = true; // (); (TAG, "onStarted"); return START_STICKY; } @Override public void onDestroy() { (); = false; // Looping in the Service|111Download from Wow! eBook <>
(); // = null; (TAG, "onDestroyed"); } /** * Thread that performs the actual update from the online service */ private class Updater extends Thread { // public Updater() { super("UpdaterService-Updater"); // } @Override public void run() { // UpdaterService2 updaterService = ; // while () { // (TAG, "Updater running"); try { // Some work goes here... (TAG, "Updater ran"); (DELAY); // } catch (InterruptedException e) { // = false; } } } } // Updater}Specifies the constant for the delay between network updates. We could make thisconfigurable via preferences as flag helps us know whether the service is currently is the separate thread that performs the actual network update. Because thethread needs to be created only once, we do so in the service’s onCreate() the service is to start, its onStartCommand() method is called. This is also agood place to start our Updater thread and update the flag identifying it as , onDestroy() is a good place to stop our network update thread and updatethe flag to show that it is no longer stop the actual thread from running, we invoke interrupt() on it. We also set itto null to help the garbage collection process clean it is where we define the Updater class. It is a thread, so it extends Java’s purpose of this is to simply give our thread a name. Having a name helps identifyvarious running threads and aids in |Chapter 8: Services
A Java thread must provide a run() method. This is where the actual work is simply creates a reference to our service, of which this thread is an inner is the loop that keeps this network update going as long as the service is notstopped. Remember that runFlag is set in the service’s onStartCommand() andonDestroy() call to () pauses the execution of this particular Updater thread forsome number of milliseconds. Earlier we set our DELAY constant to one we signal interrupt() to a running thread, it will cause an InterruptedException in the run() method. We handle the exception simply by setting the runFlag tofalse so the thread doesn’t keep trying to run again until it is the ServiceAt this point, you can run the application and start the service. If you observe the logfile,you’ll notice that every minute or so the service logs that it ran our job. Also, stoppingthe service will stop further execution of the ’s the LogCat output of what is going on with our service:D/UpdaterService( 3494): onCreatedD/UpdaterService( 3494): onStartedD/UpdaterService( 3494): Updater runningD/UpdaterService( 3494): Updater ranD/UpdaterService( 3494): Updater runningD/UpdaterService( 3494): Updater ran...D/UpdaterService( 3494): onDestroyedAs you can see, the service was created and started. It also ran couple of times beforeit finally got Data from TwitterWe now have a framework and are ready to make the actual connection to the onlineTwitter-like service, pull the status data, and display that data in our application. Twit-ter and Twitter-like services offer many different APIs to retrieve our friends’ library exposes most of them to us via the Twitter class. Perhaps oneof the most appropriate methods is getFriendsTimeline(), which returns the 20 mostrecent posts made over the past 24 hours from the user and her use this Twitter API feature, we need to connect to the online service. And to dothat, we need the username, password, and root API for our online service. As you recallfrom the previous chapter, we have already refactored most of this functionality intothe YambaApplication object (see “The Yamba Application Object” on page 102). WePulling Data from Twitter|113
can reuse all those features here because our service is part of the same application andas such has access to the same Application , we do need to make a minor update to YambaApplication, because we wouldalso like to know whether our service is running. To do that, we’ll add a flag toYambaApplication and provide setter and getter methods to access and update that flag:public class YambaApplication extends Application implements OnSharedPreferenceChangeListener { private boolean serviceRunning; // ... public boolean isServiceRunning() { // return serviceRunning; } public void setServiceRunning(boolean serviceRunning) { // = serviceRunning; }}The flag that indicates whether the service is running. Note that this flag is privateto this class, so nobody else can directly access it and change public method to check the status of the serviceRunning public method to set the state of the serviceRunning we can write new code for UpdaterService and have it connect to the online APIto pull the latest status updates from our friends. Example 8-6 shows the final 8-6. , final versionpackage ;import ;import ;import ;import ;import ;import ;import ;public class UpdaterService extends Service { private static final String TAG = "UpdaterService"; static final int DELAY = 60000; // wait a minute private boolean runFlag = false; private Updater updater; private YambaApplication yamba; // @Override public IBinder onBind(Intent intent) { return null; }114|Chapter 8: Services
@Override public void onCreate() { (); = (YambaApplication) getApplication(); // = new Updater(); (TAG, "onCreated"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { (intent, flags, startId); = true; (); (true); // (TAG, "onStarted"); return START_STICKY; } @Override public void onDestroy() { (); = false; (); = null; (false); // (TAG, "onDestroyed"); } /** * Thread that performs the actual update from the online service */ private class Updater extends Thread { List<> timeline; // public Updater() { super("UpdaterService-Updater"); } @Override public void run() { UpdaterService updaterService = ; while () { (TAG, "Updater running"); try { // Get the timeline from the cloud try { timeline = ().getFriendsTimeline(); // } catch (TwitterException e) { (TAG, "Failed to connect to twitter service", e); // Pulling Data from Twitter|115
} // Loop over the timeline and print it out for ( status : timeline) { // (TAG, ("%s: %s", , )); // } (TAG, "Updater ran"); (DELAY); } catch (InterruptedException e) { = false; } } } } // Updater}This variable allows access to the YambaApplication object that contains our sharedfeatures, such as a way to read preferences and connect to the online get the reference to our YambaApplication object by using the getApplication() we start the service, we update the serviceRunning flag in the shared applica-tion object, , when the service stops, we update the flag in the application are using Java generics to define the timeline variable as a List of call getTwitter() in YambaApplication to get the twitter object, and then call getFriendsTimeline() on it to get the last 20 status posts from the past 24 that this is the actual method that implements the web service call to our cloudservice. As such, it could take some time to complete, depending on the networklatency. Because we run this in our dedicated thread, we won’t affect the main userinterface thread while we wait for the network operation to network call can fail for any number of reasons. Here we handle failure by printingthe stack trace of what went wrong. The actual printout will be visible in that we have initialized the timeline list, we can loop over it. The easiest ap-proach is to use Java’s “for each” loop, which automatically iterates over our list,assigning each element in turn to the status now, we simply print out the statuses of who said what to the LogCat output. 116|Chapter 8: Services
Testing the ServiceNow we can run our application, start the service, and see the list of our friends’ statusesin the LogCat:D/UpdaterService( 310): Marko Gargenta: it is great that you got my messageD/UpdaterService( 310): Marko Gargenta: hello this is a test message from my phoneD/UpdaterService( 310): Marko Gargenta: TestD/UpdaterService( 310): Marko Gargenta: right!...SummaryWe now have a working service, which we start and stop and in a relatively crude,manual way. The service connects to the cloud service and pulls down the status postsfrom our friends. For now, we just print this data in the LogCat, but in the next chapterwe’ll insert the data into the 8-3 illustrates what we have done so far as part of the design outlined earlier inFigure 8-3. Yamba completionSummary|117
CHAPTER 9The DatabaseThe Android system uses databases to store useful information that needs to be per-sisted even when the user kills the app or even shuts down the device and powers itback on. The data includes contacts, system settings, bookmarks, and so , why use a database in a mobile application? After all, isn’t it better to keep our datain a cloud where it’s always backed up instead of storing it in a mobile device that iseasily lost or damaged?A database in a mobile device is very useful as a supplement to the online world. Al-though in many cases it is much better to count on the data living in the cloud, it isuseful to store it locally in order to access it more quickly and have it available evenwhen the network is not available. In this case, we are using a local database as a is also how we use it in our Yamba this chapter, you will learn how Android supports databases. You will learn to createand use a database inside the Yamba application to store our status updates data will help Yamba display statuses to the user quickly, without having to waitfor the network to provide the data. Our service will run in the background and peri-odically update the database so that the data is relatively fresh. This will improve theoverall user experience of the SQLiteSQLite is an open source database that has been around for a long time, is quite stable,and is popular on many small devices, including Android. There are couple of goodreasons why SQLite is a great fit for Android app development:•It’s a zero-configuration database. That means there’s absolutely no database con-figuration for you as the developer. This makes it relatively simple to use.•It doesn’t have a server. There’s no SQLite database process running. It is basicallya set of libraries that provide the database functionality. Not having a server toworry about is also a good
•It’s a single-file database. This makes database security straightforward, as it boilsdown to filesystem security. We already know that Android sets aside a special,secure sandbox for each application.•It’s open Android framework offers several ways to use SQLite easily and effectively, andwe’ll look at the basic usage in this chapter. You may be pleased to find that, althoughSQLite uses SQL, Android provides a higher-level library with an interface that is mucheasier to integrate into an SQLite support is built into Android, it is by no means youronly option when it comes to data persistence for your app. You canalways use another database system, such as JavaDB or MongoDB, butyou’d have to bundle the required libraries with your app and wouldnot be able to rely on Android’s built-in database support. SQLite is notan alternative to a full SQL server; instead, it is an alternative to using alocal file with an arbitrary provides an elegant interface for your app to interact with an SQLite access the database, you first need a helper class that provides a “connection” tothe database, creating the connection if it doesn’t already exist. This class, provided toyou by the Android framework, is called SQLiteOpenHelper. The database class it returnsis an instance of the following subsections I’ll explain some of the background concepts you shouldunderstand when working with DbHelper. I’m not going to explain SQL or basic data-base concepts such as normalization, because there are hundreds of good places to findthat information, and I expect most of my readers already know it. However, thischapter should give you enough to get started, even if your knowledge of databases Database Schema and Its CreationA schema is just a description of what’s in a database. In our Yamba database, forinstance, we want fields for the following information about each tweet we retrievefrom Twitter:created_atThe date when the tweet was senttxtThe text of the tweet120|Chapter 9: The Database
userThe user who sent the tweetSo each row in our table will contain the data for one tweet, and these four items willbe the columns in our schema, along with a unique ID for each tweet. We need the IDso we can easily refer to a tweet. SQLite, like most databases, allows us to declare theID as a primary key and even assigns a unique number automatically to each tweet schema has to be created when our application starts, so we’ll do it in theonCreate() method of DbHelper. We might add new fields or change existing ones ina later version of our application, so we’ll assign a version number to our schema andprovide an onUpgrade() method that we can call to alter the () and onUpgrade() are the only methods in our application when we need touse SQL. We’ll execute CREATE TABLE in onCreate() to create a table in our a production application, we’d use ALTER TABLE in onUpgrade() when the schemachanges, but that requires a lot of complex introspection of the database, so for nowwe’ll use DROP TABLE and recreate the table. Of course, DROP TABLE destroys any datacurrently in the table, but that’s not a problem for our Yamba application. It alwaysrefills the table with tweets from the past 24 hours, which are the only ones our userswill care Major OperationsThe DbHelper class offers you a high-level interface that’s much simpler than SQL. Thedevelopers realized that most applications use databases for only four major operations,which go by the appealing acronym CRUD: create, read (query), update, and fulfill these requirements, DbHelper offers:insert()Inserts one or more rows into the databasequery()Requests rows matching the criteria you specifyupdate()Replaces ones or more rows that match the criteria you specifydelete()Deletes rows matching the criteria you specifyEach of these methods has variants that enhance it with other functions. To use one ofthe methods, create a ContentValues container and place in it the information you wantinserted, updated, etc. This chapter will show you the process for an insert, and theother operations work in similar , why not use SQL directly? There are three good reasons |121Download from Wow! eBook <>
First, from a security point of view, an SQL statement is a prime candidate for a securityattack on your application and data, known as an SQL injection attack. That is becausethe SQL statement takes user input, and unless you check and isolate it very carefully,this input could embed other SQL statements with undesirable , from a performance point of view, executing SQL statements repeatedly ishighly inefficient because you’d have to parse the SQL every time the statement , the DbHelper methods are more robust and less likely to pass through the com-piler with undetected errors. When you include SQL in a program, it’s easy to createerrors that turn up only at 's database framework only supports prepared statements for standard CRUDoperations: INSERT, UPDATE, DELETE, and SELECT. For other SQL statements, wepass them directly to SQLite. That’s why we used execSQL() to run the code to CREATETABLE.... This is OK because that code doesn’t depend on any user input, and as suchSQL injection is not possible. Additionally, that code runs very rarely, so there’s noneed to worry about the performance query returns a set of rows along with a pointer called a cursor. You can retrieveresults one at a time from the cursor, causing it to advance each time to the next can also move the cursor around in the result set. An empty cursor indicates thatyou’ve retrieved all the general, anything you do with SQL could lead to an SQL exception because it’s codeis interacting with a system that’s outside of our direct control. For example, thedatabase could be running out of space or somehow corrupted. So, it is a good practiceto handle all the SQLExceptions by surrounding your database calls in try/catch ’s easy to do this using the Eclipse shortcut: the code for which you’d like to handle exceptions. Typically this would bemost of your SQL the Eclipse menu, choose Source→Surround With→Try/catch Block. Eclipse willgenerate the appropriate try/catch statements around your code for the properexception this exception in the catch block. This might be a simple call to () topass the tag, message, and the exception object ExampleSo now we’re going to create our own helper class to help us open our Yamba database(see Example 9-1). We’ll call the class DbHelper. It will create the database file if one122|Chapter 9: The Database
doesn’t already exist, or it will upgrade the user’s database if the schema has changedbetween many other classes in Android, we usually start by subclassing a framework class,in this case SQLiteOpenHelper. We then need to implement the class’s constructor, aswell as onCreate() and onUpgrade() 9-1. , version 1package ;import ;import ;import ;import ;import ;public class DbHelper1 extends SQLiteOpenHelper { // static final String TAG = "DbHelper"; static final String DB_NAME = ""; // static final int DB_VERSION = 1; // static final String TABLE = "timeline"; // static final String C_ID = BaseColumns._ID; static final String C_CREATED_AT = "created_at"; static final String C_SOURCE = "source"; static final String C_TEXT = "txt"; static final String C_USER = "user"; Context context; // Constructor public DbHelper1(Context context) { // super(context, DB_NAME, null, DB_VERSION); = context; } // Called only once, first time the DB is created @Override public void onCreate(SQLiteDatabase db) { String sql = "create table " + TABLE + " (" + C_ID + " int primary key, " + C_CREATED_AT + " int, " + C_USER + " text, " + C_TEXT + " text)"; // (sql); // (TAG, "onCreated sql: " + sql); } // Called whenever newVersion != oldVersion @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // // Typically do ALTER TABLE statements, but...we're just in development, // so: ("drop table if exists " + TABLE); // drops the old database (TAG, "onUpdated"); onCreate(db); // run onCreate to get new databaseFirst Example|123
}}Start by subclassing is the database is the version of our database. The version number is important so that later,when you change the schema, you can provide existing users with a way to upgradetheir database to the latest following are some database constants specific to our application. It is handyto define these as constants to that we can refer to them from other override SQLiteOpenHelper by passing the constants to super and retaining thelocal reference to the is the actual SQL that we’ll pass on to the database to have it create the appro-priate SQL schema that we we have our SQL to create the database, run execSQL () on the database objectthat was passed into onCreate().onUpgrade() is called whenever the user’s database version is different than the ap-plication version. This typically happens when you change the schema and releasethe application update to users who already have older version of your mentioned earlier, you would typically execute ALTER TABLE ... SQLstatements in onUpgrade(). Since we don’t have an old database to alter,we are assuming this application is still in prerelease mode and are justdeleting any user data when recreating the , we need to update the service in order to have it open up the database connection,fetch the data from the network, and insert it into the UpdaterServiceRemember that our UpdaterService connects to the cloud and gets the data. SoUpdaterService also is responsible for inserting this data into the local Example 9-2, we update the UpdaterService to pull the data from the cloud and storeit in the 9-2. , version 1package ;import ;124|Chapter 9: The Database
import ;import ;import ;import ;import ;import ;import ;import ;public class UpdaterService1 extends Service { private static final String TAG = "UpdaterService"; static final int DELAY = 60000; // wait a minute private boolean runFlag = false; private Updater updater; private YambaApplication yamba; DbHelper1 dbHelper; // SQLiteDatabase db; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { (); = (YambaApplication) getApplication(); = new Updater(); dbHelper = new DbHelper1(this); // (TAG, "onCreated"); } @Override public int onStartCommand(Intent intent, int flag, int startId) { if (!runFlag) { = true; (); ((YambaApplication) ()).setServiceRunning(true); (TAG, "onStarted"); } return _STICKY; } @Override public void onDestroy() { (); = false; (); = null;Update UpdaterService|125
(false); (TAG, "onDestroyed"); } /** * Thread that performs the actual update from the online service */ private class Updater extends Thread { List<> timeline; public Updater() { super("UpdaterService-Updater"); } @Override public void run() { UpdaterService1 updaterService = ; while () { (TAG, "Updater running"); try { // Get the timeline from the cloud try { timeline = ().getFriendsTimeline(); // } catch (TwitterException e) { (TAG, "Failed to connect to twitter service", e); } // Open the database for writing db = (); // // Loop over the timeline and print it out ContentValues values = new ContentValues(); // for ( status : timeline) { // // Insert into database (); // (_ID, ); (_CREATED_AT, ()); (_SOURCE, ); (_TEXT, ); (_USER, ); (, null, values); // (TAG, ("%s: %s", , )); } // Close the database (); // (TAG, "Updater ran"); (DELAY); } catch (InterruptedException e) { = false; } }126|Chapter 9: The Database
} } // Updater}Because we likely need db and dbHelper objects throughout the class, we declarethem globally to the the instance of DbHelper and pass this as its context. This works because theAndroid Service class is a subclass of Context. DbHelper will figure out whether thedatabase needs to be created or need to connect to the online service, get the latest updates, and insert them intothe database. getTwitter() in YambaApplication is our lazy initialization of theTwitter object. Then, we call the actual Twitter API call getFriendsTimeline() toget the last 20 statuses from friends posted in the last 24 the writable database so we can insert new statuses into it. The first time wemake this call, onCreate() in DbHelper will run and create the database file for is a simple name-value pair data structure that maps database tablenames to their respective loop over all the status data that we received. In this case, we are using a Javafor-each loop to make the iteration each record, we create a content value. We are reusing the same Java object,clearing it each time we start the loop and populating appropriate values for thestatus insert the content value into the database via an insert() call to the SQLiteDatabase object. Notice that we are not piecing together an SQL statement here, butrather using a prepared statement approach to inserting into the , remember to close the database. This is important because another activitycould be trying to read or write from this shared are now ready to run our code and test it to make sure everything the ServiceAt this point, we can test whether the database was created properly and whether theservice has populated it with some data. We’re going to do this step by that the database was createdIf the database file was created successfully, it will be located in the /data/data/ file. You can use the Eclipse DDMS per-spective and File Explorer view to look at the filesystem of the device, or you can useUpdate UpdaterService|127
adb shell on your command line, and then run ls /data/data/ to make sure the file is use File Explorer in Eclipse, either open the DDMS perspective in the top-rightcorner of your Eclipse or go to Windows→Show View→Other…→Android→File Ex-plorer. This will open the view of the filesystem of the device you are currently far, you know that the database file is there, but don’t really know whether thedatabase schema was created properly. The next section addresses sqlite3Android ships with the command-line tool sqlite3. This tool gives you access to thedatabase see whether your database schema was created properly: up your terminal or command-line adb shell to connect to your running emulator or physical the directory to the location of your database file by typing cd /data/data/ to the database with the sqlite3 this point, you should be connected to the database. Your prompt should besqlite>, indicating that you are inside the SQLite:[user:~]> adb shell# cd /data/data/# # sqlite3 version ".help" for instructionsEnter SQL statements terminated with a ";"sqlite>At this point, you can send two types of commands to your SQLite database:•Standard SQL commands, such as insert ..., update ..., delete ..., andselect ..., as well as create table ..., alter table ..., and so on. Note thatSQL is another language altogether, and as such is not covered in this book. Weassume here that you have a very basic knowledge of SQL. Also note that insqlite3, you must terminate your SQL statements with a semi-colon (;).•sqlite3 commands. These are commands that are specific to SQLite. You can seethe list of all commands by typing .help at the sqlite3> prompt. For now, we’lljust use .schema to verify that the schema was created:# sqlite3 version |Chapter 9: The Database
Enter ".help" for instructionsEnter SQL statements terminated with a ";"sqlite> .schemaCREATE TABLE android_metadata (locale TEXT);CREATE TABLE timeline ( _id integer primary key,created_at integer, source text, txt text, user text );The last line tells us that our database table timeline indeed was created and looks likewe expected, with the columns _id, created_at, source, txt, and Android developers often execute the sqlite3 com-mand in a wrong folder, and then wonder why the database table wasn’tcreated. SQLite will not complain if the file you are referring to doesn’texist; it will simply create a brand-new database. So, make sure youare either in the correct folder (/data/data/ when you execute sqlite3 , or run the commandspecifying the full path to your file: sqlite3 /data/data/ that we have a way to create and open up our database, we are ready to updatethe service that will insert the data into the this point we should be getting the data from the online service as well as insertingthat data in the database. We can also verify that the data is indeed in the database byusing ConstraintsWhen your service runs for the second time, you’ll notice that it fails and that you getmany SQLExceptions in the LogCat. You will also notice that it complains about thedatabase constraint happens because we have duplicate IDs. If you remember, we are fetching all thedata from the online service, including the IDs used online. We are then inserting thisin to our local database. But we get the data via the getFriendsTimeline() call, whichreturns the 20 most recent posts made in the past 24 hours, and we do this every minuteor so. So, unless you have friends who post more than 20 posts a minute, you’ll likelyget duplicates. That means we’re attempting to insert duplicate IDs into a database thatis set up with _id as the primary key, which means they must be unique. This fails forduplicate entries, and that’s why the database complains and throws an could check with the database that there are no duplicates before performing aninsert, but that would mean writing that logic. Since the database is already good atdatabase stuff, it is more efficient to attempt to insert duplicate entries, fail at it, andignore that do that, we need to change () to (), catch the SQLException, and ignore it:Update UpdaterService|129
...try { (, null, values); // (TAG, ("%s: %s", , ));} catch (SQLException e) { // // Ignore exception}...Attempts to insert into the database, but if it fails, it throws an catch this exception and ignore it. We will improve on this later in the this point, our code works, but it’s not ideal. There’s an opportunity to refactor Status DataThe work we did previously for the UpdaterService is not ideal for supporting our nextuser of this data: the TimelineActivity. Since TimelineActivity will also need to accessthe same database and fetch the same data, it would be better if we would share someof the same functionality between the UpdaterService and the order to do that, we’ll create a new Java class, StatusData, and make it the commoncontainer for database-related functionality (see Example 9-3). It will be hiding (en-capsulating) SQLite in a higher-level class accessible to other parts of the Yamba ap-plication. The rest of our app will then just ask for StatusData and will not be concernedwith how that data is generated. This is a better design and later will allow us to improveit even further with Content Providers, as explained in Chapter 9-3. ;import ;import ;import ;import ;import ;import ;public class StatusData { // private static final String TAG = (); static final int VERSION = 1; static final String DATABASE = ""; static final String TABLE = "timeline"; public static final String C_ID = "_id"; public static final String C_CREATED_AT = "created_at"; public static final String C_TEXT = "txt";130|Chapter 9: The Database
public static final String C_USER = "user"; private static final String GET_ALL_ORDER_BY = C_CREATED_AT + " DESC"; private static final String[] MAX_CREATED_AT_COLUMNS = { "max(" + _CREATED_AT + ")" }; private static final String[] DB_TEXT_COLUMNS = { C_TEXT }; // DbHelper implementations class DbHelper extends SQLiteOpenHelper { public DbHelper(Context context) { super(context, DATABASE, null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { (TAG, "Creating database: " + DATABASE); ("create table " + TABLE + " (" + C_ID + " int primary key, " + C_CREATED_AT + " int, " + C_USER + " text, " + C_TEXT + " text)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { ("drop table " + TABLE); (db); } } private final DbHelper dbHelper; // public StatusData(Context context) { // = new DbHelper(context); (TAG, "Initialized data"); } public void close() { // (); } public void insertOrIgnore(ContentValues values) { // (TAG, "insertOrIgnore on " + values); SQLiteDatabase db = (); // try { (TABLE, null, values, _IGNORE); // } finally { (); // } } /** * * @return Cursor where the columns are _id, created_at, user, txtRefactoring Status Data|131
*/ public Cursor getStatusUpdates() { // SQLiteDatabase db = (); return (TABLE, null, null, null, null, null, GET_ALL_ORDER_BY); } /** * * @return Timestamp of the latest status we ahve it the database */ public long getLatestStatusCreatedAtTime() { // SQLiteDatabase db = (); try { Cursor cursor = (TABLE, MAX_CREATED_AT_COLUMNS, null, null, null, null, null); try { return () ? (0) : _VALUE; } finally { (); } } finally { (); } } /** * * @param id of the status we are looking for * @return Text of the status */ public String getStatusTextById(long id) { // SQLiteDatabase db = (); try { Cursor cursor = (TABLE, DB_TEXT_COLUMNS, C_ID + "=" + id, null, null, null, null); try { return () ? (0) : null; } finally { (); } } finally { (); } }}Most of the StatusData code is a direct cut-and-paste from . This isbecause it now makes sense to make DbHelper an inner class because DbHelper nowexists only in the context of StatusData and is private to it. In other words, outsideof StatusData, no other part of the system is concerned with the fact that we areusing a database to store our data. That also makes our system flexible, which wewill see later with the use of Content |Chapter 9: The DatabaseDownload from Wow! eBook <>
This is the private and final reference to the dbHelper instance. Making it final en-sures that this object is created only once, whichever part of the system requests constructor simply constructs a new instance of need to expose close() for the dbHelper so users of it close it is the new and improved version of the ...() method that we had inDbHelper open the database only when we need it, which is right before writing to this case, we use insertWithOnConflict() and pass _IGNORE as the final parameter to indicate that when there’s a conflict, the ex-ception should be ignored. Remember that we did have a conflict with the duplicateIDs, as explained in “Database Constraints” on page that we close the database right after we are done. We do this in thefinally section of our exception handling. This ensures the database is shut downproperly, regardless of whethersomething went wrong. This theme is something we repeat in getLatestStatusCreatedAtTime() and getStatusTextById().This method simply returns all the statuses in the database, with the latest () returns the timestamp of the latest status in thedatabase. Having a way to determine the newest locally cached status is useful later,to ensure we add only new statuses into the a given ID, getStatusTextById() returns the actual text of this that we have a new common place to handle status data, we can have it hangoff of our common Application object so that any part of the application can accessthe data easily (see Example 9-4). Consequently, the UpdaterService andTimelineActivity classes are in a has-a relationship to StatusData via the YambaApplication 9-4. ...private StatusData statusData; // ...public StatusData getStatusData() { // return statusData;}// Connects to the online service and puts the latest statuses into DB.// Returns the count of new statusespublic synchronized int fetchStatusUpdates() { // (TAG, "Fetching status updates"); Twitter twitter = ();Refactoring Status Data|133
if (twitter == null) { (TAG, "Twitter connection info not initialized"); return 0; } try { List<Status> statusUpdates = (); long latestStatusCreatedAtTime = () .getLatestStatusCreatedAtTime(); int count = 0; ContentValues values = new ContentValues(); for (Status status : statusUpdates) { (_ID, ()); long createdAt = ().getTime(); (_CREATED_AT, createdAt); (_TEXT, ()); (_USER, ().getName()); (TAG, "Got update with id " + () + ". Saving"); ().insertOrIgnore(values); if (latestStatusCreatedAtTime < createdAt) { count++; } } (TAG, count > 0 ? "Got " + count + " status updates" : "No new status updates"); return count; } catch (RuntimeException e) { (TAG, "Failed to fetch status updates", e); return 0; }}...The Yamba application now encapsulates the status data as a private object is available to the rest of the application for viewing only via this is where we moved most of the code from the previous version of the UpdaterService. This was the code that was running on the Updater thread, connecting tothe online service to get the data, and then saving that data in the can now simplify the UpdaterService so it uses the refactored code in theYambaApplication to get the latest data (see Example 9-5). Note that most of theUpdater’s run() method has been moved to YambaApplication’s fetchStatusUpdates()method. In addition, the Updater doesn’t need any access to the StatusData object,which is totally hidden from 9-5. ...private class Updater extends Thread {134|Chapter 9: The Database
public Updater() { super("UpdaterService-Updater"); } @Override public void run() { UpdaterService updaterService = ; while () { (TAG, "Running background thread"); try { YambaApplication yamba = (YambaApplication) updaterService .getApplication(); // int newUpdates = (); // if (newUpdates > 0) { // (TAG, "We have a new status"); } (DELAY); } catch (InterruptedException e) { = false; } } }} // Updater...We get the reference to the YambaApplication object, which is readily available tothe Android Service and thus our own UpdaterService use the newly created fetchStatusUpdates() method in YambaApplication, whichnow houses most of the functionality that was previously part of this run() feature of fetchStatusUpdates() is that it returns the number of new recordsthat were fetched. We can use this info for debugging for now, but later will use itfor an additional this point, Yamba can pull the statuses of our friends from the cloud and post theminto the local database. We still don’t have a way to view this data, but we can verifythat the data is there in the 9-1 illustrates what we have done so far as part of the design outlined earlier inFigure |135
Figure 9-1. Yamba completion136|Chapter 9: The Database
CHAPTER 10Lists and AdaptersIn this chapter, you will learn how to create selection widgets, such as a ListView. Butthis isn’t just a chapter about user interface elements. We are deepening our under-standing of data from the previous chapter by learning how to read data from the statusdatabase and first simply output it to the screen as scrollable text. You will then learnabout adapters in order to connect your database directly with the list and create acustom adapter to implement some additional functionality. You will link this newactivity with your main activity so that the user can both post and read the end of this chapter, your app will be able to post new tweets, as well as pull themfrom Twitter, store them in the local database, and let the user read the statuses in anice and efficient UI. At that point, your app will have three activities and a ’re going to create a new activity called TimelineActivity to display all the statusesfrom our friends. This activity pulls the data from the database and displays it on thescreen. Initially, we do not have a lot of data in the database, but as we keep on usingthe application, the amount of statuses might explode. Our application needs to ac-count for are going to build this activity in a few steps, keeping the application whole andcomplete as we make each improvement: first iteration of TimelineActivity uses a TextView to display all the outputfrom the database. Since there may be quite a bit of data, we will use ScrollViewto wrap our text and provide scroll second iteration uses the much more scalable and efficient ListView andAdapter approach. In this step, you will learn how adapters and lists
, we will create a custom Adapter to handle some additional business this point, we are going under the hood of an adapter and adding custom pro-cessing. You’ll understand the purpose and usage of adapters better after TimelineActivity LayoutIn this first iteration, we are creating a new layout for the TimelineActivity. This layoutinitially uses a TextView to display all the data that we have in the database. This is fineinitially when we don’t have too many statuses to ScrollViewSince it’s unlikely that all our data will fit on a single page, we need a way to scroll thetext. To do that, we use ScrollView. ScrollView is like a window that uses scroll barsto display part of a larger component that takes more space than the screen make some potentially large views scrollable, you wrap them with this ScrollView. For example, we have a printout of friends’ statuses in the form of a TextView. Asmore statuses are added, this TextView could become large. In order to make it scrollableon a small screen, we put it into a ScrollView can contain only one direct child. If you want to combine multiple viewsinto a single view that scrolls, you first need to organize those views into another layout,like you did previously in “The StatusActivity Layout” on page 52, and than add thatlayout into the you want ScrollView to take all the available space on the screen, so you willspecify its layout width and height as ScrollView usually is not manipulated from Java, so it doesn’t require an Example 10-1, we wrap our TextView with a ScrollView so that when there’s a lot oftext to display, ScrollView automatically adds scroll 10-1. res/layout/<?xml version="" encoding="utf-8"?><LinearLayout xmlns:android=" android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent" android:background="@drawable/background"> <!-- Title --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:text="@string/titleTimeline" android:textColor="#fff" android:textSize="30sp" /> <!-- Text output wrapper --> <ScrollView android:layout_height="fill_parent"138|Chapter 10: Lists and Adapters
android:layout_width="fill_parent"> <!-- Text output --> <TextView android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/textTimeline" android:background="#6000" /> </ScrollView></LinearLayout>This is the title that we show at the top of this activity’s screen. Notice that we definedthe titleTimeline string resource in the /res/values/ file, just like we didbefore in “Strings Resource” on page ScrollView that wraps our TextView and adds scroll bars as TextView that shows the actual text, in this case our friends’ statuses from the TimelineActivity ClassNow that we have the layout file, we need to create the TimelineActivity class. Justas with any other Java file, go to the Eclipse Package Explorer, right-click on package, choose New→Class, and name it just as before, whenever we create a new Java class that is also a main buildingblock—an activity, service, broadcast receiver, or content provider—we first subclassa base class provided by the Android framework. In the case of activities, that class method we almost universally override in any activity is onCreate(). This is a greatplace for us to initialize the database. The flip side of the coin is onDestroy(), a goodplace to clean up anything that we create in onCreate(). In this case, we close the da-tabase in onDestroy(). Because we’d like the data to be as fresh as possible, we put thecode for querying the database and outputting the data in onResume(), the method calledevery time this activity is brought up front. Example 10-2 shows our 10-2. , version 1package ;import ;import ;import ;import ;import ;public class TimelineActivity1 extends Activity { // DbHelper dbHelper; SQLiteDatabase db; Cursor cursor; TextView textTimeline;Basic TimelineActivity Layout|139
@Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find your views textTimeline = (TextView) findViewById(); // Connect to database dbHelper = new DbHelper(this); // db = (); // } @Override public void onDestroy() { (); // Close the database (); // } @Override protected void onResume() { (); // Get the data from the database cursor = (, null, null, null, null, null, _CREATED_AT + " DESC"); // startManagingCursor(cursor); // // Iterate over all the data and print it out String user, text, output; while (()) { // user = ((_USER)); // text = ((_TEXT)); output = ("%s: %s\n", user, text); // (output); // } }}This is an activity, so we start by subclassing the Android framework’s Activity need access to the database to get the timeline data. onCreate() is a good placeto connect to the dbHelper opens the database file, we need to ask it for the actual databaseobject. To do that, we can use either getReadableDatabase() or getWritableDatabase(). In this case, we are only reading the data from the timeline, so we open thedatabase for reading |Chapter 10: Lists and Adapters
At some point we need to close the database and release that resource. If the databasewas opened in onCreate(), the counterpart to that would be onDestroy(). So, weclose the database there. Remember that onDestroy() is called only when the systemhas to free up query the data from the database, we use the query() method. This method seemsto contain almost endless parameters, but most of them map nicely to various partsof the SQL SELECT statement. So this line is equivalent to SQL’s SELECT * FROM timeline ORDER BY created_at DESC. The various null values refer to parts of theSELECT statement we are not using, such as WHERE, GROUPING, and HAVING. The datareturned to us is of type Cursor, which is an () is a convenience method that tells the activity to start man-aging the cursor’s life cycle the same way it manages its own. This means that whenthis activity is about to be destroyed, it will make sure to release any data referredto by the cursor, thus helping Java’s garbage collector clean up memory morequickly. The alternative is for us to add code manually in various override methodsand worry about cursor management , if you recall from “Cursors” on page 122, represents all the data we receivedfrom the database SELECT statement that was effectively executed by our query()method. This data is generally in the form of a table, with many rows and row represents a single record, such as a single status in our timeline. Each rowalso has columns that we predefined, such as _id, created_at, user, and txt. As wementioned before, cursor is an iterator, meaning we can step through all its data onerecord at a time. The first call to cursor’s moveToNext() positions the cursor at thestart. moveToNext() stops when there’s no more data to each record that the cursor currently points to, we can ask for its value by typeand column index. So (3) returns a string value of the status, (1) gives us the timestamp indicating when this record was back to Chapter 9 to see how we define strings such as C_USER and C_TEXT inour program that map to column names in the database. However, havinghardcoded column indices is not a good practice, because if we ever change theschema, we’ll have to remember to update this code. Also, the code is not veryreadable in this form. A better practice is to ask the database for the index of eachcolumn. We do that with the () use () to format each line of the output. Because we chose theTextView widget to display the data, we can only display text, or in other words,formatted strings. In a later iteration of this code, we’ll improve on finally append that new line of output to our text view textTimeline so the usercan see it on the this approach works for smaller data sets, it is not optimal or better approach is to use a ListView to represent the list of statuses stored in theBasic TimelineActivity Layout|141
database. ListView, which we’ll use in the next version of our TimelineActivity, is muchmore scalable and AdaptersA ScrollView will work for a few dozen records. But what if your status database hashundreds or even thousands of records? Waiting to get and print them all would behighly inefficient. The user probably doesn’t even care about all of the data address this issue, Android provides adapters. These are a smart way to connect aView with some kind of data source (see Figure 10-1). Typically, your view would be aListView and the data would come in the form of a Cursor or Array. So adapters comeas subclasses of CursorAdapter or 10-1. AdapterAdding a ListView to TimelineActivityAs before, our first stop in upgrading our applications is our resources file. We’ll adda ListView to the timeline layout by editing , shown in Example 10-3. res/layout/<?xml version="" encoding="utf-8"?><LinearLayout xmlns:android=" android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent" android:background="@drawable/background"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:text="@string/titleTimeline" android:textColor="#fff" android:textSize="30sp" /> <!-- --> <ListView android:layout_height="fill_parent" android:layout_width="fill_parent" android:id="@+id/listTimeline" android:background="#6000" />142|Chapter 10: Lists and AdaptersDownload from Wow! eBook <>
</LinearLayout>Adding ListView to your layout is like adding any other widget. The main attributesare id, layout_height, and versus ListActivityWe could have used ListActivity as the parent class for our is an activity that has a ListView. Either approach would work, but wechose to subclass Activity and create ListView separately to provide step-by-step, in-cremental is slightly easier to use in cases where the built-in ListView is the onlywidget in the activity. ListActivity also makes it very easy to assign an existing arrayof elements to its list via the XML binding. However, we are using a Cursor for dataand not an array (because our data comes from the database), and we do have anadditional TextView for the scrollview’s title, so the simplicity of ListActivity in thiscase is outweighed by the customization we a Row LayoutThere’s one more XML file to take care of. Although describes the entireactivity, we also need to specify what a single row of data looks like—that is, a singleline item on the screen that will show information such as who said what and easiest way to do that is to create another XML file just for that row. As for anynew XML file, we use the Android New XML File dialog window: File→New→AndroidNew XML File. Let’s name this file and select Layout for the this layout, we chose one LinearLayout with two lines arranged vertically. The firstline consists of the user and timestamp, and the second contains the actual status mes-sage. Notice that the first line uses another LinearLayout to position the user and time-stamp horizontally next to each row of data in the ListView is represented by a custom layout defined in file, shown in Example 10-4. res/layout/<?xml version="" encoding="utf-8"?> <!-- --><LinearLayout xmlns:android=" android:layout_height="wrap_content" android:orientation="vertical" android:layout_width="fill_parent"> <!-- --> <LinearLayout android:layout_height="wrap_content" android:layout_width="fill_parent">About Adapters|143
<!-- --> <TextView android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_weight="1" android:id="@+id/textUser" android:text="Slashdot" android:textStyle="bold" /> <!-- --> <TextView android:layout_height="wrap_content" android:layout_width="fill_parent" android:layout_weight="1" android:gravity="right" android:id="@+id/textCreatedAt" android:text="10 minutes ago" /> </LinearLayout> <!-- --> <TextView android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="@+id/textText" android:text="Firefox comes to Android" /></LinearLayout>The main layout for the entire row. It is vertical because our row consists of two layout that runs horizontally and represents the first line of data, namely the userand user who posted this timestamp indicating when it was posted. It should be a relative time (., 10minutes ago).The actual an Adapter in that we have the XML files sorted out, we are ready to update the Java code,shown in Example 10-5. First, we need to create the adapter. Adapters generally comein two flavors: those that represent array data and those that represent cursor our data is coming from the database, we are going to use the cursor-basedadapter. One of the simplest of those is requires us to describe a single row of data (which we do ), the data (a cursor in our case), and the mapping for a single record of datato the single row in the list. The last parameter maps each cursor column to a view inthe 10-5. , version 2package ;import ;import ;import ;import ;144|Chapter 10: Lists and Adapters
import ;import ;public class TimelineActivity2 extends Activity { DbHelper dbHelper; SQLiteDatabase db; Cursor cursor; // ListView listTimeline; // SimpleCursorAdapter adapter; // static final String[] FROM = { _CREATED_AT, _USER, _TEXT }; // static final int[] TO = { , , }; // @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find your views listTimeline = (ListView) findViewById(); // // Connect to database dbHelper = new DbHelper(this); db = (); } @Override public void onDestroy() { (); // Close the database (); } @Override protected void onResume() { (); // Get the data from the database cursor = (, null, null, null, null, null, _CREATED_AT + " DESC"); startManagingCursor(cursor); // Set up the adapter adapter = new SimpleCursorAdapter(this, , cursor, FROM, TO); // (adapter); // }}Cursor to all the status updates that we have in the is our ListView that displays the is our custom adapter, explained in the text that follows this Adapters|145
FROM is a string array specifying which columns in the cursor we’re binding from. Weuse the same strings already used to refer to columns in our is an array of integers representing IDs of views in the layout to whichwe are binding data. The number of elements in FROM and TO must be the same, sothat element at index 0 in FROM maps to element 0 in TO, and so get the ListView from the XML we have the data as a cursor, the layout of a single row from the file,and the FROM and TO constants for mapping the data, we are ready to create , we need to tell our ListView to use this this point, TimelineActivity is complete, but not yet registered with the manifestfile. We’ll do that in the next section. However, if we were to run this activity, you’dquickly notice that the timestamp doesn’t look quite the way we imagined that we are storing the status creation time in the database as a long valuerepresenting the number of milliseconds since January 1st, 1970. And since that’s thevalue in the database, that’s the value we show on the screen as well. This is the standardUnix time, which is very useful for representing actual points in time. But the value isnot very meaningful to users. Instead of showing value 1287603266359, it would be muchnicer to represent it to the user as “10 Minutes Ago.” This friendly time format is knownas relative time, and Android provides a method to convert from one format to the question is where to inject this conversion. As it stands right now, theSimpleCursorAdapter is capable only of mapping straight from a database value to layoutview. This doesn’t work for our needs, because we need to add some business logic inbetween the data and the view. To do this, we’ll create our own is our custom adapter, shown in Example 10-6. Although SimpleCursorAdapter did a straightforward mapping of data in the database to views on the screen,we had an issue with the timestamp. The job of TimelineAdapter is to inject some busi-ness logic to convert the Unix timestamp to relative time. The method inSimpleCursorAdapter that creates a displayable view from input data is bindView(), sowe’ll override that method and ask it to massage the data before it is , if you are not sure which method to override, look at the online documen-tation for the particular system class that you are modifying (in this case, 10-6. ;146|Chapter 10: Lists and Adapters
import ;import ;import ;import ;import ;import ;public class TimelineAdapter extends SimpleCursorAdapter { // static final String[] FROM = { _CREATED_AT, _USER, _TEXT }; // static final int[] TO = { , , }; // // Constructor public TimelineAdapter(Context context, Cursor c) { // super(context, , c, FROM, TO); } // This is where the actual binding of a cursor to view happens @Override public void bindView(View row, Context context, Cursor cursor) { // (row, context, cursor); // Manually bind created at timestamp to its view long timestamp = (cursor .getColumnIndex(_CREATED_AT)); // TextView textCreatedAt = (TextView) (); // ((timestamp)); // }}To create our own custom adapter, we subclass one of the Android standard adapt-ers, in this case the same SimpleCursorAdapter we used in the previous constant defines the columns of interest to us in the database, as in the constant specifies the IDs of views that we’ll map those columns we’re defining a new class, we need a constructor. It simply calls the parentconstructor using only method we override is bindView(). This method is called for each row tomap its data to its views, and it’s where the gist of the adapter work happens. Inorder to reuse most of the data-to-views mapping provided by SimpleCursorAdapter, we call () override default mapping for the timestamp, we first get the actual timestampvalue from the , we find the specific TextView in the |147
Finally, we set the value of textCreatedAt to the relative time since the do this, we use the Android SDK method ().At this point, we can further simplify our TimelineActivity class because we movedsome of the adapter details to TimelineAdapter. Example 10-7 shows this 10-7. , version 3package ;import ;import ;import ;import ;import ;public class TimelineActivity3 extends Activity { DbHelper dbHelper; SQLiteDatabase db; Cursor cursor; ListView listTimeline; TimelineAdapter adapter; // @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find your views listTimeline = (ListView) findViewById(); // Connect to database dbHelper = new DbHelper(this); db = (); } @Override public void onDestroy() { (); // Close the database (); } @Override protected void onResume() { (); // Get the data from the database cursor = (, null, null, null, null, null, _CREATED_AT + " DESC"); startManagingCursor(cursor);148|Chapter 10: Lists and Adapters
// Create the adapter adapter = new TimelineAdapter(this, cursor); // (adapter); // }}We change SimpleCursorAdapter to a new instance of the TimelineAdapter, and pass it the context and the our ListView to connect to the data via the of the shortcomings of overriding bindView() is that we use () tobind all views first, and then replace its behavior for one particular element. This issomewhat wasteful. The final version of our application in this chapter will optimizethe : A Better Alternative to TimelineAdapterInstead of creating a new TimelineAdapter that is a subclass of SimpleCursorAdapter andoverriding its bindView() method, we could attach the business logic directly to theexisting SimpleCursorAdapter. This approach is more efficient because we are not over-riding bindView() and we do not require a separate custom adapter attach business logic to an existing SimpleCursorAdapter, use its setViewBinder()method. We will need to supply the method with an implementation of is an interface that specifies setViewValue(), where the actual binding of aparticular date element to a particular view , we discovered the setViewBinder() feature of this SimpleCursorAdapter frame-work class by reading its reference our final iteration of TimelineAdapter, we create a custom ViewBinder as a constantand attach it to the stock SimpleCursorAdapter, as shown in Example 10-8. with ViewBinder ... @Override protected void onResume() { ... (VIEW_BINDER); // ... } // View binder constant to inject business logic that converts a timestamp to // relative time static final ViewBinder VIEW_BINDER = new ViewBinder() { // ViewBinder: A Better Alternative to TimelineAdapter|149Download from Wow! eBook <>
public boolean setViewValue(View view, Cursor cursor, int columnIndex) { // if (() != ) return false; // // Update the created at text to relative time long timestamp = (columnIndex); // CharSequence relTime = (view .getContext(), timestamp); // ((TextView) view).setText(relTime); // return true; // } }; ...We attach a custom ViewBinder instance to our stock adapter. VIEW_BINDER is definedlater in our actual implementation of a ViewBinder instance. Notice that we are implement-ing it as an inner class. There’s no reason for any other class to use it, and thus itshouldn’t be exposed to the outside world. Also notice that it is static final,meaning that it’s a only method that we need to provide is setViewValue(). This method is calledfor each data element that needs to be bound to a particular we check whether this view is the view we care about, ., our TextView rep-resenting when the status was created. If not, we return false, which causes theadapter to handle the bind itself in the standard manner. If it is our view, we moveon and do the custom get the raw timestamp value from the cursor the same Android helper method we used in our previous example,(), we convert the timestamp to a human-readable format. This is that business logic that we are the text on the actual true so that SimpleCursorAdapter does not process bindView() on this ele-ment in its standard the Manifest FileNow that we have the TimelineActivity, it would make sense to make it the “main”activity for our Yamba application. After all, users are more likely to check what theirfriends are doing than to update their own |Chapter 10: Lists and Adapters
To do that, we need to update the manifest file. As usual, we’ll list TimelineActivitywithin the <activity> element in the file, just as we added thepreference activity to the manifest file in “Update the Manifest File” on page 88:<activity android:name=".TimelineActivity" />Now, in order to make TimelineActivity the main entry point into our application, weneed to register it to respond to certain intents. Basically, when the user clicks to startyour application, the system sends an intent. You have to define an activity to “listen”to this intent. The activity does that by filtering the intents with an IntentFilter. InXML, this is within the <intent-filter> element, and it usually contains at least an<action> element representing the actual intent action we’re interested might have noticed that StatusActivity had some extra XML compared toPrefsActivity. The extra code is the intent filter block, along with the action thatit’s filtering is a special action named that simply indicates thisis the main component that should be started when the user wants to start your appli-cation. Additionally, the <category> element tells the system that this applicationshould be added to the main Launcher application so that the user can see its app iconalong with all the other icons, click on it, and start it. This category is defined , to make TimelineActivity the main entry point, we simply list it and move the codefrom the StatusActivity declaration over to the TimelineActivity declaration, asshown in Example 10-9. <?xml version="" encoding="utf-8"?><manifest xmlns:android=" android:versionCode="1" android:versionName="" package=""> <application android:icon="@drawable/icon" android:label="@string/app_name" android:name=".YambaApplication"> <activity android:name=".TimelineActivity" android:label="@string/titleTimeline"> <intent-filter> <!-- --> <action android:name="" /> <!-- --> <category android:name="" /> <!-- --> </intent-filter> </activity> <activity android:name=".PrefsActivity" android:label="@string/titlePrefs" /> <activity android:name=".StatusActivity" android:label="@string/titleStatus" /> <!-- --> <service android:name=".UpdaterService" /> </application> <uses-sdk android:minSdkVersion="8" />Updating the Manifest File|151
<uses-permission android:name="" /></manifest><intent_filter> registers this particular activity with the system to respond to cer-tain the system that this is the main activity to start when users start category LAUNCHER tells the Home application to add this application into thelist displayed in the launcher no longer needs any intent App SetupNow when the user runs our application, the Timeline screen will show up first. Butunless the user knows she should set up the preferences and start the service, there willbe no data and very little hand-holding telling her what to solution is to check whether preferences exist, and if they do not, redirect the userto the Preference activity with a message telling her what to do next:...@Overrideprotected void onCreate(Bundle savedInstanceState) { ... // Check whether preferences have been set if (().getString("username", null) == null) { // startActivity(new Intent(this, )); // (this, , _LONG).show(); // } ...}...We check whether a particular preference has been set. In this case, I’ve chosen tocheck username because it’s likely to be set if any preferences at all are set. Since thepreferences do not exist the first time the user runs the application, this meansthe value of username (or any other preference item we choose) will be start the PrefsActivity. Note that startActivity() will dispatch an intent to thesystem, but the rest of onCreate() will execute as well. This is good because we’relikely going to come back to the Timeline activity once we’re done setting display a little pop-up message, ., a Toast, telling the user what to do. Thisassumes that you have created the appropriate msgSetupPrefs in your |Chapter 10: Lists and Adapters
Base ActivityNow that we have a Timeline activity, we need to give it an options menu, just as wedid for our Status activity in “The Options Menu” on page 89. This is especially im-portant because the Timeline activity is the entry point into our application, and with-out the menu, the user cannot easily get to any other activity or start and stop the one approach, we could copy and paste the code we already have from the Statusactivity, but that’s rarely a good strategy. Instead, we’ll do what we usually do: refactorthe code. In this case, we can take out the common functionality from the Status activityand place it in another activity that will serve as the base. See Figure 10-2. BaseActivity refactorTo do that, we’ll create a new class called BaseActivity and move the common func-tionality into it. For us, the common functionality includes getting the reference to theYambaApplication object, as well as the onCreateOptionsMenu() and onOptionsItemSelected() methods that support the options Activity|153
Toggle ServiceWhile we’re at it, instead of having Start Service and Stop Service menu buttons, itwould be nice to provide just one button that toggles between Start and Stop. To dothat, we’ll change our menu and add onMenuOpened() to the base activity to dynamicallyupdate the title and images for this toggle , we’ll update the file to include our new toggle menu item, as shown inExample 10-10. At the same time, we’ll remove the Start Service and Stop Service itemsbecause our toggle feature makes them 10-10. res/menu/[]<?xml version="" encoding="utf-8"?><menu xmlns:android=" <item android:id="@+id/itemStatus" android:title="@string/titleStatus" android:icon="@android:drawable/ic_menu_edit"></item> <item android:title="@string/titleTimeline" android:id="@+id/itemTimeline" android:icon="@android:drawable/ic_menu_sort_by_size"></item> <item android:id="@+id/itemPrefs" android:title="@string/titlePrefs" android:icon="@android:drawable/ic_menu_preferences"></item> <item android:icon="@android:drawable/ic_menu_delete" android:title="@string/titlePurge" android:id="@+id/itemPurge"></item> <!-- --> <item android:id="@+id/itemToggleService" android:title="@string/titleServiceStart" android:icon="@android:drawable/ic_media_play"></item></menu>This new itemToggleService now replaces both itemServiceStart and , we need to override onMenuOpened() in the base activity to change the menu itemdynamically, shown in Example 10-11. ;import ;import ;import ;import ;import ;import ;/** * The base activity with common features shared by TimelineActivity and * StatusActivity */public class BaseActivity extends Activity { // YambaApplication yamba; // 154|Chapter 10: Lists and Adapters
@Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); yamba = (YambaApplication) getApplication(); // } // Called only once first time menu is clicked on @Override public boolean onCreateOptionsMenu(Menu menu) { // getMenuInflater().inflate(, menu); return true; } // Called every time user clicks on a menu item @Override public boolean onOptionsItemSelected(MenuItem item) { // switch (()) { case : startActivity(new Intent(this, ) .addFlags(_ACTIVITY_REORDER_TO_FRONT)); break; case : if (()) { stopService(new Intent(this, )); } else { startService(new Intent(this, )); } break; case : ((YambaApplication) getApplication()).getStatusData().delete(); (this, , _LONG).show(); break; case : startActivity(new Intent(this, ).addFlags( _ACTIVITY_SINGLE_TOP).addFlags( _ACTIVITY_REORDER_TO_FRONT)); break; case : startActivity(new Intent(this, ) .addFlags(_ACTIVITY_REORDER_TO_FRONT)); break; } return true; } // Called every time menu is opened @Override public boolean onMenuOpened(int featureId, Menu menu) { // MenuItem toggleItem = (); // if (()) { // (); (_media_pause); } else { // Base Activity|155
(); (_media_play); } return true; }}BaseActivity is an declare the shared YambaApplication to make it accessible to all the onCreate(), we get the reference to () is moved here from () is also moved over from StatusActivity. Notice, however,that it now checks for itemToggleService instead of start and stop service on the state of the service, which we know from the flag in yamba, we requesteither to start or to stop the updater () is the new method called by the system when the options menu isopened. This is a good callback for us to implement the toggle functionality. We’regiven the menu object that represents the options the menu object, we find our new toggle item so that we can update it basedon the current state of the updater check whether the service is already running, and if it is, we set the appropriatetitle and icon for the toggle item. Notice that here we’re setting up the title and iconprogrammatically using the Java APIs instead of the XML, which we used initiallyto set up the menu in the service is stopped, we set the icon and title so that user can click on it and startthe service. This way our single toggle button communicates the service’s that we have a BaseActivity class, let’s update our Timeline activity to use 10-12 shows what the completed Timeline activity looks 10-12. , final versionpackage ;import ;import ;import ;import ;import ;import ;import ;import ;import ;156|Chapter 10: Lists and Adapters
import ;public class TimelineActivity extends BaseActivity { // Cursor cursor; ListView listTimeline; SimpleCursorAdapter adapter; static final String[] FROM = { _CREATED_AT, _USER, _TEXT }; static final int[] TO = { , , }; @Override protected void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Check if preferences have been set if (().getString("username", null) == null) { // startActivity(new Intent(this, )); (this, , _LONG).show(); } // Find your views listTimeline = (ListView) findViewById(); } @Override protected void onResume() { (); // Setup List (); // } @Override public void onDestroy() { (); // Close the database ().close(); // } // Responsible for fetching data and setting up the list and the adapter private void setupList() { // // Get the data cursor = ().getStatusUpdates(); startManagingCursor(cursor); // Setup Adapter adapter = new SimpleCursorAdapter(this, , cursor, FROM, TO); (VIEW_BINDER); // (adapter); } // View binder constant to inject business logic for timestamp to relative // time conversionBase Activity|157Download from Wow! eBook <>
static final ViewBinder VIEW_BINDER = new ViewBinder() { // public boolean setViewValue(View view, Cursor cursor, int columnIndex) { if (() != ) return false; // Update the created at text to relative time long timestamp = (columnIndex); CharSequence relTime = (view .getContext(), timestamp); ((TextView) view).setText(relTime); return true; } };}For starters, we now subclass our BaseActivity instead of just the system’sActivity. This way we inherit the yamba object as well as all the support for theoptions is where we check whether preferences are already set. If not, we’ll redirect theuser to the Preference activity resuming this activity, we set up the list. This is a private method, shown laterin the this activity is closed, we want to make sure we close the database to releasethis resource. The database is opened by the call to getStatusUpdates() in theyamba () is the convenience method that gets the data, sets up the adapter, andconnects it all to the list is where we attach the view binder to the list, as discussed earlier in “View-Binder: A Better Alternative to TimelineAdapter” on page is defined this point, we’ve done a lot of the refactoring work on our Timeline activity. We canalso simplify the Status activity by cutting out the code related to the options also helps separate functional concerns among BaseActivity, StatusDate, 10-3 shows what the final Timeline activity screen looks |Chapter 10: Lists and Adapters
Figure 10-3. TimelineActivitySummaryAt this point, Yamba can post a new status as well as list the statuses of our application is complete and 10-4 illustrates what we have done so far as part of the design outlined earlierin Figure |159
Figure 10-4. Yamba completion160|Chapter 10: Lists and Adapters
CHAPTER 11Broadcast ReceiversIn this chapter, you will learn about broadcast receivers and when to use them. We’llcreate a couple of different receivers that illustrate different usage scenarios. First, you’llcreate a broadcast receiver that will start up your update service at boot time, so thatusers always have their friends’ latest timelines the first time they check for them (as-suming their preferences are set). Next, you will create a receiver that will update thetimeline when it changes while the user is viewing it. This will illustrate the program-matic registration of receivers and introduce you to broadcasting intents. We’ll imple-ment a receiver that is trigged by changes in network availability. And finally, we’lllearn how to surround our app with some security by defining the end of this chapter, your app has most of the functionality that a user wouldneed. The app can send status updates, get friends’ timelines, update itself, and startautomatically. It works even when the user is not connected to the network (althoughof course it cannot send or receive new messages).About Broadcast ReceiversBroadcast receivers are Android’s implementation of the Publish/Subscribe messagingpattern, or more precisely, the Observer pattern. Applications (known as publishers)can generate broadcasts to simply send events without knowing who, if anyone, willget them. Receivers (known as subscribers) that want the information subscribe to spe-cific messages via filters. If the message matches a filter, the subscriber is activated (ifit’s not already running) and notified of the you may recall from Chapter 4, a BroadcastReceiver is a piece of code to which anapp subscribes in order to get notified when an action happens. That action is in theform of an intent broadcast. When the right intent is fired, the receiver wakes up andexecutes. The “wakeup” happens in the form of an onReceive() callback
BootReceiverIn our Yamba application, the UpdaterService is responsible for periodically updatingthe data from the online service. Currently, the user needs to start the service manually,which she does by starting the application and then clicking on the Start Service would be much cleaner and simpler if somehow the system automatically startedUpdaterService when the device powered up. To do this, we create BootReceiver, abroadcast receiver that the system will launch when the boot is complete, which in turnwill launch our TimelineActivity activity. Example 11-1 sets up our broadcast 11-1. ;import ;import ;import ;import ;public class BootReceiver extends BroadcastReceiver { // @Override public void onReceive(Context context, Intent intent) { // (new Intent(context, )); // ("BootReceiver", "onReceived"); }}We create BootReceiver by subclassing BroadcastReceiver, the base class for only method that we need to implement is onReceive(). This method gets calledwhen an intent matches this launch an intent to start our Updater service. The system passed us a Contextobject when it invoked our onReceive() method, and we are expected to pass it onto the Updater service. The service doesn’t happen to use the Context object foranything, but we’ll see an important use for it this point, we have our boot receiver. But in order for it to get called—in other words,in order for the activity to start at boot—we must register it with the the BootReceiver with the AndroidManifest FileTo register BootReceiver, we add it to the manifest file, shown in Example 11-2. Wealso add an intent filter to this file. This intent filter specifies which broadcasts triggerthe receiver to become |Chapter 11: Broadcast Receivers
Example 11-2. : <application> section...<receiver android:name=".BootReceiver"> <intent-filter> <action android:name="_COMPLETED" /> </intent-filter></receiver>...In order to get notifications for this particular intent filter, we must also specify thatwe’re using a specific permission it requires, in this case _BOOT_COMPLETED (see Example 11-3).Example 11-3. : <manifest> section...<uses-permission android:name="_BOOT_COMPLETED" />...If we don’t specify the permission we require, we simply won’t be no-tified when this event occurs, and we won’t have the chance to run ourstartup code. We won’t even know we aren’t getting notified, so this ispotentially a hard bug to the Boot ReceiverAt this point, you can reboot your device. Once it comes back up, yourUpdaterService should be up and running. You can verify this either by looking at theLogCat for our output or by using System Settings and checking that the service verify via System Settings, at the Home screen, click on the Menu button and chooseSettings→Applications→Running Services. You should see UpdaterService listed this point, you know the BootReceiver did indeed get the broadcast and has startedthe TimelineReceiverCurrently, if you view your Timeline activity while a new status update comes in, youwon’t know about it. That’s because the UpdaterService doesn’t have a way to notifyTimelineActivity to refresh address this, we create another broadcast receiver, this time as an inner class ofTimelineActivity, as shown in Example TimelineReceiver|163
Example 11-4. with TimelineReceiver inner class...class TimelineReceiver extends BroadcastReceiver { // @Override public void onReceive(Context context, Intent intent) { // (); // (); // ("TimelineReceiver", "onReceived"); }}...As before, to create a broadcast receiver, we subclass the BroadcastReceiver only method we need to override is onReceive(). This is where we put the workwe want done when this receiver is work we want done is simply to tell the cursor object to refresh itself. We dothis by invoking requery(), which executes the same query that was executed ini-tially to obtain this cursor the adapter that the underlying data has this point, our receiver is ready but not registered. Unlike BootReceiver, where weregistered our receiver with the system statically via the manifest file, we’ll registerTimelineReceiver programmatically, as shown in Example 11-5. This is because TimelineReceiver makes sense only within TimelineActivity because purpose is refreshingthe list when the user is looking at the Timeline 11-5. with TimelineReceiver...@Overrideprotected void onResume() { (); // Get the data from the database cursor = (, null, null, null, null, null, _CREATED_AT + " DESC"); startManagingCursor(cursor); // Create the adapter adapter = new TimelineAdapter(this, cursor); (adapter); // Register the receiver registerReceiver(receiver, filter); // }@Overrideprotected void onPause() { ();164|Chapter 11: Broadcast ReceiversDownload from Wow! eBook <>
// UNregister the receiver unregisterReceiver(receiver); // }...We register the receiver in onResume() so that it’s registered whenever theTimelineActivity is running. Recall that all paths to the running state go throughthe onResume() method, as described in “Running state” on page , we unregister the receiver on the way to the stopped state (recall “Stoppedstate” on page 30). onPause() is a good place to do ’s missing now is the explanation of filter. To specify what triggers the receiver,we need an instance of IntentFilter, which simply indicates which intent actions wewant to be notified about. In this case, we make up an action string through which wefilter intents, as shown in Example 11-6. with update onCreate()...filter = new IntentFilter("_STATUS"); // ...Create a new instance of IntentFilter to filter for the _STATUS intent action. Since this is a text constant, we’ll define it as such and refer to itas a constant later on. A good place to define it is the UpdaterService, because that’sthe code that generates the events we’re waiting IntentsFinally, to trigger the filter, we need to broadcast an intent that matches the action theintent filter is listening for. In the case of BootReceiver, earlier, we didn’t have to dothis, because the system was already broadcasting the appropriate intent. However, forTimelineReceiver, the broadcast is ours to make because the intent is specific to you recall from Chapter 8, our UpdaterService had an inner class called Updater (seeExample 11-7). This inner class was the separate thread that connected to the onlineservice and pulled down the data. Because this is where we know whether there are anynew statuses, this is a good choice for sending notifications as 11-7. with the Updater inner class...private class Updater extends Thread { Intent intent; public Updater() { super("UpdaterService-Updater"); }Broadcasting Intents|165
@Override public void run() { UpdaterService updaterService = ; while () { (TAG, "Running background thread"); try { YambaApplication yamba = (YambaApplication) (); // int newUpdates = (); // if (newUpdates > 0) { // (TAG, "We have a new status"); intent = new Intent(NEW_STATUS_INTENT); // (NEW_STATUS_EXTRA_COUNT, newUpdates); // (intent); // } (60000); // } catch (InterruptedException e) { = false; // } } }}...We get the application object to access our common application you recall, our application provides fetchStatusUpdates() to get all the latest statusupdates and populate the database. This method returns the number of new check whether there are any new is the intent we are about to broadcast. NEW_STATUS_INTENT is a constantthat represents an arbitrary action. In our case, we define it as _STATUS, but it could be any string without spaces. However, usingsomething that resembles your package name is a good ’s a way to add data to an intent. In our case, it would be useful to communicateto others as part of this broadcast how many new statuses there are. In this line, weuse Intent’s putExtra() method to add the number of new statuses under a keynamed NEW_STATUS_EXTRA_COUNT, which is just our arbitrary this point, we know there’s at least one new status. sendBroadcast() is part ofContext, which is a superclass of Service and therefore also a superclass of ourUpdaterService. Since we’re inside the Updater inner class, we have to refer to theparent’s updaterService instance in order to call sendBroadcast(). This method sim-ply takes the intent we just tell this thread to sleep for a minute so that it doesn’t overload the device’s CPUwhile checking regularly for case this thread is interrupted for some reason, we update this service’s runFlagso we know it’s not currently |Chapter 11: Broadcast Receivers
UpdaterService might send broadcasts even when the TimelineReceiver is not registered. That is perfectly fine. Those broadcasts willsimply be this point, a new status received by UpdaterService causes an intent to be broadcastover to the TimelineActivity, where the message is received by the TimelineReceiver,which in turn refreshes the ListView of Network ReceiverWith the current design, our service will start automatically at boot time and attemptto connect to the cloud and retrieve the latest updates approximately every problem with the current design is that the service will try to connect even whenthere’s no Internet connection available. This adds unnecessary attempts to wake upthe radio and connect to the server, all of which taxes the battery. Imagine how manywasteful attempts would be made while your phone is in flight mode on a cross-countryflight. This highlights some of the inherit constraints when programming for mobiledevices: we’re limited by the battery life and network better approach is to listen to network availability broadcasts and use that informa-tion to intelligently turn off the service when the Internet is unavailable and turn it backon when data connection comes back up. The system does send an intent wheneverconnection availability changes. Another system service allows us to find out whatchanged and act this case, we’re creating another receiver, NetworkReceiver, shown in Exam-ple 11-8. Just as before, we need to create a Java class that subclasses BroadcastReceiver, and then register it via the Android manifest 11-8. ;import ;import ;import ;import ;import ;public class NetworkReceiver extends BroadcastReceiver { // public static final String TAG = "NetworkReceiver"; @Override public void onReceive(Context context, Intent intent) { boolean isNetworkDown = ( _NO_CONNECTIVITY, false); // The Network Receiver|167
if (isNetworkDown) { (TAG, "onReceive: NOT connected, stopping UpdaterService"); (new Intent(context, )); // } else { (TAG, "onReceive: connected, starting UpdaterService"); (new Intent(context, )); // } }}As we said before, when you create a new broadcast receiver, you typically start bysubclassing Android’s own BroadcastReceiver the system broadcasts the particular intent action that this receiver subscribes,the intent will have an extra piece of information indicating whether the network isup or down. In this case, the variable is a Boolean value keyed to the _NO_CONNECTIVITY constant. In the previous section, we associated avalue to a string of our own invention; here we’re on the other end of the message,extracting a value from a Boolean. A value of true indicates that the network is the network is down, we simply send an intent to our UpdaterService. We nowhave a use for the Context object that the system passed to this method. We call its stopService() method, passing the the flag is false, we know that the network has changed and is now available. Sowe start our UpdaterService, the inverse of our previous stop an activity or a service, we simply used the methods startActivity(), startService(), stopService(), and so on. This is becauseactivities and services are subclasses of Context, and thus they inheritedthese methods. So, there’s an is-a relationship between them and Context. Broadcast receivers, on the other hand have a Context objectpassed into them, and thus have a has-a relationship with the that we have created this new receiver, we need to register it with the manifestfile, shown in Example 11-9. : <application> section...<receiver android:name=".NetworkReceiver"> <intent-filter> <action android:name="_CHANGE" /> </intent-filter></receiver>...168|Chapter 11: Broadcast Receivers
We also need to update our application’s permissions (Example 11-10) because theaction filter for a network change is protected and requires us to ask the user to grantus this particular 11-10. : <manifest> section...<uses-permission android:name="" /> <!-- --><uses-permission android:name="_BOOT_COMPLETED" /> <!-- --><uses-permission android:name="_NETWORK_STATE" /> <!-- -->...Used by our Twitter object to connect to the Internet to get and post status saw this permission already in Chapter 6. Not having this permission will causeour app to crash when it attempts to access the network (unless we catch and handlethat network exception).Required in order to receive broadcasts that the system has booted. As mentionedearlier, if we don’t have this permission, we will silently be ignored at boot time andour boot code won’t in order to receive network state updates. Just as with the boot receiver, ifwe don’t have this permission, we will be silently passed by when the network Custom Permissions to Send and Receive BroadcastsAs discussed in “Updating the Manifest File for Internet Permission” on page 61, anapplication must be granted permissions to access certain restricted features of thesystem, such as connecting to the Internet, sending SMS messages, making phone calls,reading the user’s contacts, taking photos, and so on. The user has to grant all or noneof the permissions to the application at installation time, and it is the job of the appli-cation developer to list all the permissions the app needs by adding the <uses-permission> element to the manifest file. So far, we’ve added permissions to Yamba in orderto access the Internet, kick off our boot-time service, and learn about network now that we have our Updater service sending a broadcast action to our Timelinereceiver, we might want to restrict permission to send and receive that broadcast to ourown app. Otherwise, another app, knowing what our action looks like, could send itand cause actions in our application that we didn’t fill up this security hole, we define our own permission and ask the user to grant itto the Yamba application. Next, we’ll enforce both sending and receiving Custom Permissions to Send and Receive Broadcasts|169
Declaring Permissions in the Manifest FileThe first step is to declare our permissions, explaining what they are, how they are tobe used, and setting their protection level, shown in Example 11-11. Adding permissions to manifest file<manifest> ... <!-- --> <permission android:name="_TIMELINE_NOTIFICATIONS" <!-- --> android:label="@string/send_timeline_notifications_permission_label" <!-- --> android:description="@string/send_timeline_notifications_permission_description" <!-- --> android:permissionGroup="_INFO" <!-- --> android:protectionLevel="normal" /> <!-- --> <permission android:name="_TIMELINE_NOTIFICATIONS" android:label="@string/receive_timeline_notifications_permission_label" android:description="@string/receive_timeline_notifications_permission_description" android:permissionGroup="_INFO" android:protectionLevel="normal" /> <!-- --> <uses-permission android:name="_TIMELINE_NOTIFICATIONS" /> <uses-permission android:name="_TIMELINE_NOTIFICATIONS" /></manifest>This is the name of our permission, which we refer to later both when we requestthe permission and when we enforce it. In our app, we’ll be using the permission tosecurely send timeline that will be displayed to the user when she is prompted to grant this permissionto our app at installation time. It should be relatively short. Note that we have definedthis label in our resource description should be provided to offer information about why this permission isneeded and how it’s going to be permission group is optional, but it helps the system group your permissionwith other common permissions in one of the system-defined permission groups You couldalso define your own group, but that is rarely permission level, a required value, specifies the severity or risk posed by grantingthe permission. A “normal” level is the lowest and most basic of the four standardpermission |Chapter 11: Broadcast Receivers
We do the same to define the other permission, which allows us to receive the time-line notifications we are our permissions are defined, we need to ask the user to grant them to theapplication. We do that via the <uses-permission> element, just as we did for theother system permissions we specified this point, we have defined our two custom permissions and have requested themfor our application. Next, we need to make sure the sender and receiver both play bythe the Services to Enforce PermissionsOur Updater service broadcasts the intent to the rest of the system once there’s a newstatus update. Because we do not want everyone to receive this intent, in Exam-ple 11-12 we ensure that the receiver won’t be allowed to receive it unless the receiverdefines the right 11-12. Updater in UpdaterService ... private class Updater extends Thread { static final String RECEIVE_TIMELINE_NOTIFICATIONS = "_TIMELINE_NOTIFICATIONS"; // Intent intent; public Updater() { super("UpdaterService-Updater"); } @Override public void run() { UpdaterService updaterService = ; while () { (TAG, "Running background thread"); try { YambaApplication yamba = (YambaApplication) updaterService .getApplication(); int newUpdates = (); if (newUpdates > 0) { (TAG, "We have a new status"); intent = new Intent(NEW_STATUS_INTENT); (NEW_STATUS_EXTRA_COUNT, newUpdates); (intent, RECEIVE_TIMELINE_NOTIFICATIONS); // } (DELAY); } catch (InterruptedException e) { = false; } } }Adding Custom Permissions to Send and Receive Broadcasts|171
} // Updater ...This is the name of the permission that the receiver must have. It needs to be thesame as the permission name in the manifest file that we specified enforce the permission on the receiver, we simply add it to the sendBroadcast() call as the optional second parameter. If the receiver doesn’t have this par-ticular permission granted to it by the user, the receiver won’t be notified and willnever know that our message just got complete the security in the sending direction, we don’t have to do anything to TimelineReceiver. It will be able to receive the permission because the user granted there is a corresponding responsibility on the TimelineReceiver side. It shouldcheck that the sender had permission to send the message it is TimelineReceiver to Enforce PermissionsNow we will check on the receiver side that the broadcaster is allowed to talk to we register our receiver, we add the broadcast permission that the sender shouldhave, as shown in Example 11-13. TimelineReceiver in ...public class TimelineActivity extends BaseActivity { static final String SEND_TIMELINE_NOTIFICATIONS = "_TIMELINE_NOTIFICATIONS"; // ... @Override protected void onResume() { (); ... // Register the receiver (receiver, filter, SEND_TIMELINE_NOTIFICATIONS, null); // } ...}We define the permission name as a constant. This needs to be the same name asthe one we declared for this permission in the manifest the onResume() method where we register our TimelineReceiver, we now add aparameter specifying this permission is a requirement for anyone who wants to sendus this type of now have a pair of custom permissions, and we are enforcing them in both thesender and the receiver of the broadcast. This illustrates some of the capabilities ofAndroid to fine-tune the permission |Chapter 11: Broadcast Receivers
SummaryYamba is now complete and ready for prime time. Our application can now send statusupdates to our online service, get the latest statuses from our friends, start automaticallyat boot time, and refresh the display when a new status is 11-1 illustrates what we have done so far as part of the design outlined earlierin Figure 11-1. Yamba completionSummary|173Download from Wow! eBook <>
CHAPTER 12Content ProvidersContent providers are Android building blocks that can expose data across the boun-daries between application sandboxes. As you recall, each application in Android runsin its own process with its own permissions. This means that an application cannot seeanother app’s data. But sometimes you want to share data across applications. This iswhere content providers become very your contacts, for example. You might have a large database of contacts on yourdevice, which you can view via the Contacts app as well as via the Dialer app. Somedevices, such as HTC Android models, might even have multiple versions of the Con-tacts and Dialer apps. It would not make a lot of sense to have similar data live inmultiple providers let you centralize content in one place and have many different ap-plications access it as needed. In the case of the contacts on your phone, there is actuallya ContactProvider application that contains a content provider, and other applicationsaccess the data via this interface. The interface itself is fairly simple: it has the sameinsert(), update(), delete(), and query() methods we saw in Chapter uses content providers quite a bit internally. In addition to contacts, yoursettings represent another example, as do all your bookmarks. All the media in thesystem is also registered with MediaStore, a content provider that dispenses images,music, and videos in your a Content ProviderTo create a content provider: a new Java class that subclasses the system’s ContentProvider your all the unimplemented methods, such as insert(), update(), delete(), query(), getID(), and getType(). your content provider in the
We are going to start by creating a brand-new Java class in the same package as all otherclasses. Its name will be StatusProvider. This class, like any of Android’s main buildingblocks, will subclass an Android framework class, in this case Eclipse, select your package, click on File→New→Java Class, and enter “StatusPro-vider”. Then, update the class to subclass ContentProvider, and organize the imports(Ctrl-Shift-O) to import the appropriate Java packages. The result should look like this:package ;import ;public class StatusProvider extends ContentProvider {}Of course, this code is now broken because we need to provide implementations formany of its methods. The easiest way to do that is to click on the class name and choose“Add unimplemented methods” from the list of quick fixes. Eclipse will then createstubs, or templates, of the missing the URIObjects within a single app share an address space, so they can refer to each othersimply by variable names. But objects in different apps don’t recognize the differentaddress spaces, so they need some other mechanism to find each other. Android usesa Uniform Resource Identifier, a string that identifies a specific resource, to locate acontent provider. A URI has three or four parts, shown in Example 12-1. Parts of a URIcontent:// A B C D•Part A, content://, is always set to this value. This is written in stone.•Part B, , is the so-called authority. It is typically thename of the class, all in lowercase. This authority must match the authority thatwe specify for this provider when we later declare it in the manifest file.•Part C, status, indicates the type of data that this particular provider provides. Itcould contain any number of segments separated with a slash, including none at all.•Part D, 47, is an optional ID for the specific item that we are referencing. If not set,the URI will represent the entire set. Number 47 is an arbitrary number picked forthis you need to refer to the content provider in its entirety, and sometimes toonly one of the items of data it contains. If you refer to it in its entirety, you leave offPart D; otherwise, you include that part to identify one item within the content pro-vider. Actually, since we have only one table, we do not need Part C of the |Chapter 12: Content Providers
One way to define the constants for our example is like this:public static final Uri CONTENT_URI = Uri .parse("content://");public static final String SINGLE_RECORD_MIME_TYPE = " static final String MULTIPLE_RECORDS_MIME_TYPE = " “Getting the Data Type” on page 180, we’ll explore the reason for two MIME are also going to define the status data object in a class-global variable so that wecan refer to it:StatusData statusData;We’ll be using the status data object all over our app because all our database connec-tivity is centralized in that class. So now the StatusProvider class has a reference to anobject of class DataTo insert a record into a database via the content provider interface, we need to overridethe insert() method. The caller provides the URI of this content provider (without anID) and the values to be inserted. A successful call to insert the new record returns theID for that record. We end by returning a new URI concatenating the provider’s URIwith the ID we just got back:@Overridepublic Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = (); // try { long id = (, null, values); // if (id == -1) { throw new RuntimeException(( "%s: Failed to insert [%s] to [%s] for unknown reasons.", TAG, values, uri)); // } else { return (uri, id); // } } finally { (); // }}We need to open the database for attempt to insert the values into the database and, upon a successful insert,receive the ID of the new record from the anything fails during the insert, the database will return -1. We can than throw aruntime exception because this is an error that should never have a Content Provider|177
If the insert was successful, we use the () helper methodto craft a new URI containing the ID of the new record appended to the standardprovider’s need to close the database no matter what, so a finally block is a good place todo DataTo update the data via the Content Provider API, we need:The URI of the providerThis may or may not contain an ID. If it does, the ID indicates the specific recordthat needs to be updated, and we can ignore the selection. If the ID is not specified,it means that we are updating many records and need the selection to indicatewhich are to be values to be updatedThe format of this parameter is a set of name/value pairs that represent columnnames and new selection and arguments that go with itThese together make up a WHERE clause in SQL, selecting the records that willchange. The selection and its arguments are omitted when there is an ID, becausethe ID is enough to select the record that is being code that handles both types of update—by ID and by selection—can be as follows:@Overridepublic int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { long id = (uri); // SQLiteDatabase db = (); // try { if (id < 0) { return (, values, selection, selectionArgs); // } else { return (, values, _ID + "=" + id, null); // } } finally { (); // }}We use the local helper method getId() to extract the ID from the URI. If no ID ispresent, this method returns -1. getId() will be defined later in this need to open the database for writing the there’s no ID, that means we’re simply updating all the database records that matchthe selection and selectionArgs |Chapter 12: Content Providers
If an ID is present, we are using that ID as the only part of the WHERE clause to limitthe single record that we’re ’t forget to close the DataDeleting data is similar to updating data. The URI may or may not contain the ID ofthe particular record to delete:@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) { long id = (uri); // SQLiteDatabase db = (); // try { if (id < 0) { return (, selection, selectionArgs); // } else { return (, _ID + "=" + id, null); // } } finally { (); // }}The getId() helper method extracts the ID from the URI that we get. If no ID ispresent, this method returns need to open the database for writing the there’s no ID, we simply delete all the database records that match the selection and selectionArgs an ID is present, we use that ID as the only part of the WHERE clause to limit theoperation to the single record the user wants to ’t forget to close the DataTo query the data via a content provider, we override the query() method. This methodhas a long list of parameters, but usually we just forward most of them to the databasecall with the same name:@Overridepublic Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { long id = (uri); // SQLiteDatabase db = (); // if (id < 0) { return (, projection, selection, selectionArgs, null, null, sortOrder); // Creating a Content Provider|179
} else { return (, projection, _ID + "=" + id, null, null, null, null); // }}The getId() helper method extracts the ID from the URI that we need to open the database, in this case just for there’s no ID, we simply forward what we got for the content provider to theequivalent database call. Note that the database call has two additional parametersthat correspond to the SQL GROUPING and HAVING components. Because content pro-viders do not support this feature, we simply pass in an ID is present, we use that ID as the WHERE clause to limit what record to do not close the database here, because closing the database willdestroy the cursor and we still need it on the receiving end to go overthe data returned by the query. One way to handle the cursor is to havethe receiver manage it. Activities have a simple startManagingCursor()method for this the Data TypeA content provider must return the MIME type of the data it is returning. The MIMEtype indicates either a single item or all the records for the given URI. Earlier in thischapter we defined the single-record MIME type as and the directory of all statuses as To let others retrieve the MIME type, we must define the call getType().The first part of the MIME type is either or , depending on whether the type represents a specific item or all items forthe given URI. The second part, or for our app, is a combination of the constant vnd followed by yourcompany or app name and the actual content you may recall, the URI can end with a number. If it does, that number is the ID ofthe specific record. If it doesn’t, the URI refers to the entire following source shows the implementation of getType() as well as the getId()helper method that we’ve already used several times:@Overridepublic String getType(Uri uri) { return (uri) < 0 ? MULTIPLE_RECORDS_MIME_TYPE : SINGLE_RECORD_MIME_TYPE; // }180|Chapter 12: Content Providers
private long getId(Uri uri) { String lastPathSegment = (); // if (lastPathSegment != null) { try { return (lastPathSegment); // } catch (NumberFormatException e) { // // at least we tried } } return -1; // }getType() uses the helper method getId() to determine whether the URI has an IDpart. If it does not—as indicated by a negative return value—we for the MIME type. Other-wise, we’re referring to a single record and the MIME type is Of course, we previously defined these valuesas class extract the ID in our implementation of getId(), we take the last part of the that last part is not null, we try to parse it as a long and return could be that the last part is not a number at all, in which case the parse will return -1 to indicate that the given URI doesn’t contain a valid the Android Manifest FileAs with any major building block, we want to define our content provider in the An-droid manifest XML file. Notice that in this case the android:authorities propertyspecifies the URI authority permitted to access this content provider. Typically, thisauthority would be your content provider class—which we use here—or your package:<application> ... <provider android:name=".StatusProvider" android:authorities="" /> ...</application>At this point our content provider is complete, and we are ready to use it in otherbuilding blocks of Yamba. But since our application already centralizes all data accessin a StatusData object that is readily accessible via YambaApplication, we don’t reallyhave a good use for this content provider within the same application. Besides, contentproviders mostly make sense when we want to expose the data to another Content Providers Through WidgetsAs mentioned before, content providers make the most sense when you want to exposethe data to other applications. It is a good practice to always think of your applicationUsing Content Providers Through Widgets|181Download from Wow! eBook <>
as part of a larger Android ecosystem and, as such, a potential provider of useful datato other demonstrate how content providers can be useful, we’ll create a Home screenwidget. We’re not using the term widget here as a synonym for Android’s View class,but as a useful embedded service offered by the Home typically ships with a few Home screen widgets. You can access them by goingto your Home screen, long-pressing on it to pull up an Add to Home Screen dialog,and choosing Widgets. Widgets that come with Android include Alarm Clock, PictureFrame, Power Controls, Music, and Search. Our goal is to create our own Yamba widgetthat the user will be able to add to the Home Yamba widget will be simple, displaying just the latest status update. To create it,we’ll make a new YambaWidget class that subclasses AppWidgetProviderInfo. We’ll alsohave to register the widget with the manifest the YambaWidget classYambaWidget is the main class for our widget. It is a subclass of AppWidgetProvider, aspecial system class that makes widgets. This class itself is a subclass of BroadcastReceiver, so our Yamba widget is a broadcast receiver automatically. Basically, when-ever our widget is updated, deleted, enabled, or disabled, we’ll get a broadcast intentwith that information. So this class inherits the onUpdate(), onDeleted(), onEnabled(),onDisabled(), and onReceive() callbacks. We can override any of these, but typicallywe care mostly about the updates and general broadcasts we that we understand the overall design of the widget framework, Example 12-2shows how we implement 12-2. ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;public class YambaWidget extends AppWidgetProvider { // private static final String TAG = (); @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // 182|Chapter 12: Content Providers
Cursor c = ().query(_URI, null, null, null, null); // try { if (()) { // CharSequence user = ((_USER)); // CharSequence createdAt = (context, c .getLong((_CREATED_AT))); CharSequence message = ((_TEXT)); // Loop through all instances of this widget for (int appWidgetId : appWidgetIds) { // (TAG, "Updating widget " + appWidgetId); RemoteViews views = new RemoteViews((), _widget); // (, user); // (, createdAt); (, message); (_icon, PendingIntent .getActivity(context, 0, new Intent(context, ), 0)); (appWidgetId, views); // } } else { (TAG, "No data to update"); } } finally { (); // } (TAG, "onUpdated"); } @Override public void onReceive(Context context, Intent intent) { // (context, intent); if (().equals(_STATUS_INTENT)) { // (TAG, "onReceived detected new status update"); AppWidgetManager appWidgetManager = (context); // (context, appWidgetManager, appWidgetManager .getAppWidgetIds(new ComponentName(context, ))); // } }}As mentioned before, our widget is a subclass of AppWidgetProvider, which itself isa method is called whenever our widget is to be updated, so it’s where we’llimplement the main functionality of the widget. When we register the widget withthe system in the manifest file later, we’ll specify the update frequency we’d like. Inour case, this method will be called about every 30 finally get to use our content provider. The whole purpose of this widget in thischapter is to illustrate how to use the StatusProvider that we created earlier. As yousaw earlier when we implemented the content provider, its API is quite similar toUsing Content Providers Through Widgets|183
the SQLite database API. The main difference is that instead of passing a table nameto a database object, we’re passing a content URI to the ContentResolver. We stillget back the very same Cursor object as we did with databases in Chapter this particular example, we care only about the very latest status update from theonline service. So we position the cursor to the first element. If one exists, it’s ourlatest status the next few of lines of code, we extract data from the cursor object and store itin local the user could have multiple Yamba widgets installed, we need to loopthrough them and update them all. We don’t particularly care about the specificappWidgetId because we’re doing identical work to update every instance of theYamba widget. The appWidgetId becomes an opaque handle we use to access eachwidget in actual view representing our widget is in another process. To be precise, ourwidget is running inside the Home application, which acts as its host and is theprocess we are updating. Hence the RemoteViews constructor. The RemoteViewsframework is a special shared memory system designed specifically for we have the reference to our widget views’ Java memory space in anotherprocess, we can update those views. In this case, we’re setting the status data in therow that represents our we update the remote views, the AppWidgetManager call to updateAppWidget()actually posts a message telling the system to update our widget. This will happenasynchronously, but shortly after onUpdate() of whether the StatusProvider found a new status, we release the datathat we might have gotten from the content provider. This is just a good call to onReceive() is not necessary in a typical widget. But since a widget is abroadcast receiver, and since our Updater service does send a broadcast when weget a new status update, this method is a good opportunity to invoke onUpdate()and get the latest status data updated on the check whether the intent was for the new status it was, we get the instance of AppWidgetManager for this then invoke onUpdate().At this point, we have coded the Yamba widget, and as a receiver, it will be notifiedperiodically or when there are new updates, and it will loop through all instances ofthis widget on the Home screen and update , we need to set up the layout for our |Chapter 12: Content Providers
Creating the XML LayoutThe layout for the widget is fairly straightforward. Note that we’re reusing our file that displays status data properly in the Timeline activity. In Exam-ple 12-3, we just include it along with a little title and an icon to make it look good onthe Home 12-3. res/layout/<?xml version="" encoding="utf-8"?> <!-- --><LinearLayout xmlns:android=" android:layout_height="wrap_content" android:layout_width="fill_parent" android:background="@color/edit_text_background" android:layout_margin="5dp" android:padding="5dp"> <!-- --> <ImageView android:layout_width="wrap_content" android:src="@drawable/icon" android:layout_height="fill_parent" android:id="@+id/yamba_icon" android:clickable="true" /> <!-- --> <include layout="@layout/row" /></LinearLayout>We’re using LinearLayout to hold our widget together. It runs horizontally, with theicon on the left and the status data on the is our standard Yamba the use of the <include> element. This is how we include our into this layout so we don’t have to duplicate the layout is simple enough, but it does the job for our particular needs. Next, weneed to define some basic information about this widget and its the AppWidgetProviderInfo FileThe XML file shown in Example 12-4 is responsible for describing the widget. It typi-cally specifies which layout this widget uses, how frequently it should be updated bythe system, and its 12-4. res/xml/<?xml version="" encoding="utf-8"?><appwidget-provider xmlns:android=" android:initialLayout="@layout/yamba_widget" android:minWidth="294dp" android:minHeight="72dp" android:label="@string/msgLastTimelineUpdate" android:updatePeriodMillis="1800000" />In this case we specify that we’d like to have our widget updated every 30 minutes orso (1,800,000 milliseconds). Here, we also specify the layout to use, the title of thiswidget, and its Content Providers Through Widgets|185
Updating the Manifest FileFinally, we need to update the manifest file and register the widget: ... <application .../> ... <receiver android:name=".YambaWidget" android:label="@string/msgLastTimelineUpdate"> <intent-filter> <action android:name="_UPDATE" /> </intent-filter> <intent-filter> <action android:name="_STATUS" /> </intent-filter> <meta-data android:name="" android:resource="@xml/yamba_widget_info" /> </receiver> ... </application> ...Notice that the widget is a receiver, as we mentioned before. So, just like other broadcastreceivers, we declare it within a <receiver> tag inside an <application> element. It isimportant to register this receiver to receive ACTION_APPWIDGET_UPDATE updates. We dothat via the <intent-filter>. The <meta-data> specifies the meta information for thiswidget in the yamba_widget_info XML file described in the previous ’s it. We now have the widget and are ready to test the WidgetTo test this widget, install your latest application on the device. Next, go to the Homescreen, long-press it, and click on the Widgets choice. You should be able to navigateto the Yamba widget at this point. After adding it to the Home screen, the widget shoulddisplay the latest status your Updater service is running, the latest updates should show up on the Homescreen. This means your widget is running this point, the Yamba app is complete. Congratulations! You are ready to fine-tuneit, customize it, and publish it to the 12-1 illustrates what we have done so far as part of the design outlined earlierin Figure |Chapter 12: Content Providers
Figure 12-1. Yamba completionSummary|187
CHAPTER 13System ServicesLike many modern operating systems, Android comes with a number of system servicesthat are always on, always running, and readily available for developers to tap system services include things like the Location service, Sensor service, WiFiservice, Alarm service, Telephony service, Bluetooth service, and so on. System servicesare started at boot time and are guaranteed to be running by the time your this chapter, we’ll see how we can use some of the system services to further expandthe Yamba application. First, we’ll take a look at the Sensor service in a small exampleto demonstrate some of the concepts that are common to most of the system , we’ll add support for location information to our status updates via the , we’re going to refactor the Yamba application to take advantage of IntentService support. This will demonstrate how to use the Alarm service and will make ourUpdater slightly simpler and more DemoTo start with system services, we are going to look at a simple, self-contained exampleof a compass application. This application uses the Sensor service to get updates fromthe orientation sensor and use its information to rotate a Rose, our custom UI compo-nent. The Sensor service is very typical of system services and a relatively easy one build this example, we’ll create an activity that will get the Sensor service and registerfor updates from a particular sensor. Next, we’ll build the Rose that will rotate on thescreen based on the sensor
Common Steps in Using System ServicesTo get any system service, issue the getSystemService() call. This returns a Managerobject representing that system service, which you then use to access the service. Mostsystem services work on some sort of publish/subscribe mechanism. In other words,you generally register your app for notifications from that service and provide your owncallback methods that the service will invoke when an event happens. To do this inJava, create a listener that implements an interface so that the service can call the call-back in mind that requesting notifications from a system service can be costly in termsof battery usage. For example, getting a GPS signal or processing sensor updates takesa lot of energy from the device. To preserve the battery, we typically want to be doingthe work of processing updates only when the user is looking at the activity itself. Interms of the activity life cycle (see “Activity Life Cycle” on page 28), this means we wantto get the notifications only while in the running state (see “Running state”on page 29).To ensure that you request service updates only while in the running state, register forupdates in onResume() and unregister in onPause(). This is because all roads into therunning state go via onResume() and all roads out of it go via onPause(). In certain othersituations, you may want to cast the net wider and register the activity betweenonStart() and onStop(), or even between onCreate() and onDestroy(). In our case, wedon’t want to register in onCreate(), because it would waste a lot of battery and pro-cessing time by making us listen and process sensor updates even when our activity isnot in the foreground. You can now see how understanding the activity life cycle playsan important role in optimizing the usage of system services for battery Updates from the CompassTo code our Compass demo application, we get SensorManager, the class that representsthe Sensor system service. We make our main activity implement SensorEventListener so that we can register it (., this) to get updates for a specific sensor. Weregister and unregister the listener in onResume() and onPause(), respectively. To im-plement the sensor listeners, our activity provides onAccuracyChanged() and onSensorChanged(). The former is a requirement, but we’ll leave it empty because the accuracy ofthe orientation sensor is not expected to change. The latter call is what’s really of in-terest to the orientation sensor changes, the Sensor service calls back our sensor listenervia onSensorChanged() and reports the new sensor data. The data always comes backas an array of float values that represent degrees and therefore range from 0 to 359. Inthe case of the orientation sensor, the elements represent the following dimensions,illustrated in Figure 13-1:190|Chapter 13: System ServicesDownload from Wow! eBook <>
Index [0], the azimuthThe amount of rotation around the Z axis from the vertical position around theback and then around the bottom toward the frontIndex [1], the pitchThe amount of rotation around the X axis from the front to the left and then aroundthe back toward the rightIndex [2], the rollThe amount of rotation around the Y axis from the vertical position to the left andthen the around the bottom toward the rightFor the Compass demo, we are interested only in the first element, ., the data returned by each sensor has a different meaning, and you should look up theparticulars in the documentation at 13-1. AxisCompass Main ActivityExample 13-1, the main Compass activity, sets the Rose as its only widget on the also registers with SensorManager to listen to sensor events and updates the Roseorientation Demo|191
Example 13-1. ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;// implement SensorListenerpublic class Compass extends Activity implements SensorEventListener { // SensorManager sensorManager; // Sensor sensor; Rose rose; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { // (savedInstanceState); // Set full screen view getWindow().setFlags(_FULLSCREEN, _FULLSCREEN); requestWindowFeature(_NO_TITLE); // Create new instance of custom Rose and set it on the screen rose = new Rose(this); // setContentView(rose); // // Get sensor and sensor manager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); // sensor = (_ORIENTATION); // ("Compass", "onCreated"); } // Register to listen to sensors @Override public void onResume() { (); (this, sensor, _DELAY_NORMAL); // } // Unregister the sensor listener @Override public void onPause() { (); (this); // }192|Chapter 13: System Services
// Ignore accuracy changes public void onAccuracyChanged(Sensor sensor, int accuracy) { // } // Listen to sensor and provide output public void onSensorChanged(SensorEvent event) { // int orientation = (int) [0]; // ("Compass", "Got sensor event: " + [0]); (orientation); // } @Override public void onConfigurationChanged(Configuration newConfig) { (newConfig); }}Since Compass listens to sensor events, it needs to implement the SensorEventListener define the local variable for the sensor, the sensor manager, and the accessing the sensor is a one-time activity, we do it when our app is window manager flags set the activity into full-screen create a new instance of the Rose widget, our custom compass this case, the activity content is the single Rose widget. This is unlike the usualreference to an XML layout get the sensor manager from the system the sensor manager, we can obtain the actual sensor object that we are inter-ested register to listen to sensor updates in the activity’s onResume() method, as de-scribed unregister from sensor updates in onPause(), the counterpart to onResume().onAccuracyChanged() is implemented because it is required by the SensorEventListener interface, but is left empty for the reasons explained () is called whenever the sensor changes, indicating a rotation ofthe device in some direction. The particular information about the change is storedin are interested in the first element of the array of new we have the new orientation, we update our Rose widget to rotate Demo|193
The way a device reports sensor data can be very erratic, coming atuneven intervals. There are ways to suggest to the system how frequentlywe’d like the sensor updates, but these are just suggestions and not aguarantee. Also, sensors are not supported by the emulator, so to reallytest your application, you’ll need a physical device with support for theorientation sensor. Most Android phones have that Rose WidgetShown in Example 13-2, Rose is our custom UI widget showing a compass rose thatcan be rotated like a real compass. Every UI widget in Android needs to be a subclassof View. But because this is an image, we’ll choose a higher starting point, in this casethe ImageView class, which is a View. By subclassing ImageView, our Rose inherits someuseful methods to load an image and draw it on the any custom UI widget, one of the most important methods is onDraw(), whichdraws the widget onto a Canvas that is provided to the method. In the case of our Rose,we rotate this canvas around its middle point for the same number of degrees as re-ported by the orientation sensor. Next, we draw the image onto this rotated sensor asit would normally be drawn by the super class. The result is a rotated compass roserepresenting the direction in which we are 13-2. ;import ;import ;import ;public class Rose extends ImageView { // int direction = 0; public Rose(Context context) { super(context); (); // } // Called when component is to be drawn @Override public void onDraw(Canvas canvas) { // int height = (); // int width = (); (direction, width / 2, height / 2); // (canvas); // } // Called by Compass to update the orientation public void setDirection(int direction) { // 194|Chapter 13: System Services
= direction; (); // request to be redrawn }}Our widget has to be a subclass of View, but since our widget is an image, we getmore functionality by starting from already knows how to set an image as its content. We just specify tosuper which image resource to use. Note that the file is inour /res/drawable () is the method that the layout manager calls to have each widget draw layout manager passes the Canvas to this method. This method is where youtypically do any custom drawing to the we have the canvas, we can figure out its simply rotate the entire canvas for some amount (in degrees) around tell super to draw the image on this rotated canvas. At this point we have ourrose drawn at the proper () is called by the Compass activity to update the direction of the rosebased on the values that the sensor manager invalidate() on a view marks it for redrawing, which happens later via acall to onDraw().At this point, your compass application is working. The compass rose should be pointingnorth, more or less, when the device is held upright as usual. Keep in mind that youshould run this application on a physical device because the emulator doesn’tsupport ServiceNow that you have seen how the sensor manager works, we can look at the LocationAPI, another system service provided by Android. Just like sensors, the Location APIis supported via the Location manager. And just like sensors, we get the Location man-ager via a getSystemService() we have access to the Location service, we need to register a Location listenerwith it so the service can call back when there’s a change in location. Again, we’ll dothis by implementing a Location listener you recall from “Common Steps in Using System Services” on page 190, processingGPS and other location updates can be very taxing for the battery. To minimize thebattery consumption, we want to listen to location updates only while in the runningLocation Service|195
state. To do that, we’ll register for the updates in onResume() and unregister inonPause(), taking advantage of the activity life Am I? DemoThis example illustrates how to use location-based services in Android. First, we useLocationManager to figure out our current location based on the resources in the envi-ronment available to the device, such as GPS or a wireless network. Second, we use Geocoder to convert this location to an layoutThe layout for this example is trivial, as you can see in Example 13-3. Our resource fileprovides a TextView widget for the title and another TextView widget for the the output could be longer than the screen size, we wrap the output in a ScrollView 13-3. res/layout/<?xml version="" encoding="utf-8"?><LinearLayout xmlns:android=" android:layout_height="fill_parent" android:layout_width="fill_parent" android:background="#fff" android:orientation="vertical"> <!-- --> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#333" android:textSize="30dp" android:text="@string/title"/> <!-- --> <ScrollView android:layout_height="fill_parent" android:layout_width="fill_parent"> <!-- --> <TextView android:textColor="#333" android:layout_gravity="center" android:layout_height="fill_parent" android:layout_width="fill_parent" android:gravity="center" android:textSize="25dp" android:text="Waiting..." android:id="@+id/textOut"></TextView> </ScrollView></LinearLayout>The title for our ScrollView to enable scrolling if the output grows beyond the size of the TextView to represent the output. It will be programmatically set from the Where-AmI activity for our Location listenerThe code in Example 13-4 is our main activity, which sets up the screen, connects toLocationManager, and uses the Geocoder to figure out our address. The LocationManager uses location providers, such as GPS or Network, to figure out our currentlocation. The location is expressed as latitude and longitude values. The Geocoder196|Chapter 13: System Services
searches an online database for known addresses in the vicinity of the location may come up with multiple results, some more specific than 13-4. ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;public class WhereAmI extends Activity implements LocationListener { // LocationManager locationManager; // Geocoder geocoder; // TextView textOut; // @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); textOut = (TextView) findViewById(); locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // geocoder = new Geocoder(this); // // Initialize with the last known location Location lastLocation = locationManager .getLastKnownLocation(_PROVIDER); // if (lastLocation != null) onLocationChanged(lastLocation); } @Override protected void onResume() { // (); (_PROVIDER, 1000, 10, this); } @Override protected void onPause() { // (); (this); }Location Service|197
// Called when location has changed public void onLocationChanged(Location location) { // String text = ( "Lat:\t %f\nLong:\t %f\nAlt:\t %f\nBearing:\t %f", location .getLatitude(), (), (), ()); // (text); // Perform geocoding for this location try { List<Address> addresses = ( (), (), 10); // for (Address address : addresses) { ("\n" + (0)); // } } catch (IOException e) { ("WhereAmI", "Couldn't get Geocoder data", e); } } // Methods required by LocationListener public void onProviderDisabled(String provider) { } public void onProviderEnabled(String provider) { } public void onStatusChanged(String provider, int status, Bundle extras) { }}Notice that WhereAmI implements LocationListener. This is the interface that LocationManager uses to notify us of changes to the reference to reference to is the text view to which we print our output so the user can see get the local reference to LocationManager, we ask the context to get thelocation manager system service. For more about context, see “Application Con-text” on page create a new instance of Geocoder and pass the current context to location manager memorizes its last known location. This is useful because itmight take a while until we get the location lock via either a network or a usual, we register in onResume(), since that is the method that is called en routeto the running state. We use the location manager’s requestLocationUpdates()method to register for |Chapter 13: System Services
We unregister in onPause(), which will be called just before the activity goes into thestopped () is the callback method called by the location manager when itdetects that the location has get the Location object, which contains a lot of useful information about thecurrent location. We create a human-readable string with this we have the location, we can try to “geocode” the location, a process of con-verting latitude and longitude to a known we do find known addresses for this location, we print them other callback methods are required to implement the LocationListener in-terface. We don’t use them for this manifest fileAs shown in Example 13-5, the manifest file for this app is fairly standard. Notice thatin order to register as a location listener, we have to hold the appropriate in mind that although we have GPS and Network as the two most commonlyused location providers, Android is built with extensibility in mind. In the future, wemight have other types of providers as well. For that reason, Android breaks down thelocation permissions into abstract fine location and coarse location 13-5. <?xml version="" encoding="utf-8"?><manifest xmlns:android=" package="" android:versionCode="1" android:versionName=""> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".WhereAmI" android:label="@string/app_name"> <intent-filter> <action android:name="" /> <category android:name="" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="4" /> <!-- --> <uses-permission android:name="_FINE_LOCATION" /> <uses-permission android:name="_COARSE_LOCATION" /></manifest>Declares that this app uses location providers. The location permissions could _FINE_LOCATION for a GPS provider or _COARSE_LOCATION for a wireless network this point, your WhereAmI application is complete. It illustrates how to useLocationManager to get the actual location via a specific location provider and how toLocation Service|199Download from Wow! eBook <>
convert that location into a known address via Geocoder. An example of the result isshown in Figure 13-2. WhereAmIUpdating Yamba to Use the Location ServiceThe WhereAmI application was a small standalone test to make sure we can get locationinformation. Now we’ll incorporate location information into our larger Yamba Our PreferencesFirst, the user might not want to broadcast her location to the world, so we should good place to ask would be the Preferences. This time around, we’ll use a ListPreference property. This is somewhat different from the EditTextPreferences we’veseen before in Chapter 7, in that it requires a list of items. In fact, it requires two lists:one to display and one to use for actual we’ll add a couple of strings to our file and create two new string re-sources: one to represent names of our location providers in a form friendly to humanreaders and the other to represent their values. To do that, we’ll add the following toour file:<?xml version="" encoding="utf-8"?><resources> ... <string-array name="providers"> <item>None, please</item> <item>GPS via satellites!</item> <item>Mobile Network will do</item> </string-array>200|Chapter 13: System Services
<string-array name="providerValues"> <item>NONE</item> <item>gps</item> <item>network</item> </string-array></resources>Notice that both string arrays have the same number of elements. They basically rep-resent name-value pairs and match each that we have the names and values for our location providers, we can with that information, as shown in Example 13-6. Updated res/xml/<?xml version="" encoding="utf-8"?><PreferenceScreen xmlns:android=" <EditTextPreference android:title="@string/titleUsername" android:summary="@string/summaryUsername" android:key="username"></EditTextPreference> <EditTextPreference android:title="@string/titlePassword" android:password="true" android:summary="@string/summaryPassword" android:key="password"></EditTextPreference> <EditTextPreference android:title="@string/titleApiRoot" android:summary="@string/summaryApiRoot" android:key="apiRoot"></EditTextPreference> <ListPreference android:title="@string/titleProvider" android:summary="@string/summaryProvider" android:key="provider" android:entryValues="@array/providerValues" android:entries="@array/providers" /> <!-- --> <ListPreference android:entryValues="@array/intervalValues" android:summary="@string/summaryUpdaterInterval" android:title="@string/titleUpdaterInterval" android:entries="@array/interval" android:key="interval"></ListPreference></PreferenceScreen>The new ListPreference displaying the names and values of various location pro-viders that we support: GPS, network, and none at the Yamba ApplicationNow that we have the location provider preferences, we have to expose those prefer-ences via YambaApplication to rest of the app, namely do that, add a getter method to (see Example 13-7).Example 13-7. class YambaApplication extends Application implements OnSharedPreferenceChangeListener { ... public static final String LOCATION_PROVIDER_NONE = "NONE"; ... public String getProvider() { return ("provider", LOCATION_PROVIDER_NONE);Updating Yamba to Use the Location Service|201
}}Now that we have support for providers in the preferences and in the Yamba app object,we’re ready to update the Status the Status ActivityThe Status activity is the main place where we use the location information. Just as inthe WhereAmI demo, we’re going to get the Location manager by calling getSystemService() and register for location updates. We’re also going to implement theLocationListener interface, which means adding a number of new callback methodsto this activity. When the location does change, we’ll update the location object, andnext time around when we update our status online, we’ll have the proper locationinformation. Example 13-8 shows the updated 13-8. ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;public class StatusActivity extends BaseActivity implements OnClickListener, TextWatcher, LocationListener { // private static final String TAG = "StatusActivity"; private static final long LOCATION_MIN_TIME = 3600000; // One hour private static final float LOCATION_MIN_DISTANCE = 1000; // One kilometer EditText editText; Button updateButton; TextView textCount; LocationManager locationManager; // Location location; String provider; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) {202|Chapter 13: System Services
(savedInstanceState); setContentView(); // Find views editText = (EditText) findViewById(); updateButton = (Button) findViewById(); (this); textCount = (TextView) findViewById(); ((140)); (); (this); } @Override protected void onResume() { (); // Setup location information provider = (); // if (!(provider)) { // locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // } if (locationManager != null) { location = (provider); // (provider, LOCATION_MIN_TIME, LOCATION_MIN_DISTANCE, this); // } } @Override protected void onPause() { (); if (locationManager != null) { (this); // } } // Called when button is clicked public void onClick(View v) { String status = ().toString(); new PostToTwitter().execute(status); (TAG, "onClicked"); } // Asynchronously posts to twitter class PostToTwitter extends AsyncTask<String, Integer, String> { // Called to initiate the background activity @Override protected String doInBackground(String... statuses) { try { // Check if we have the location if (location != null) { // Updating Yamba to Use the Location Service|203
double latlong[] = {(), ()}; ().setMyLocation(latlong); } status = ().updateStatus(statuses[0]); return ; } catch (RuntimeException e) { (TAG, "Failed to connect to twitter service", e); return "Failed to post"; } } // Called once the background activity has completed @Override protected void onPostExecute(String result) { (, result, _LONG).show(); } } // TextWatcher methods public void afterTextChanged(Editable statusText) { int count = 140 - (); ((count)); (); if (count < 10) (); if (count < 0) (); } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } // LocationListener methods public void onLocationChanged(Location location) { // = location; } public void onProviderDisabled(String provider) { // if ((provider)) (this); } public void onProviderEnabled(String provider) { // if ((provider)) (, LOCATION_MIN_TIME, LOCATION_MIN_DISTANCE, this); } public void onStatusChanged(String provider, int status, Bundle extras) { // }}204|Chapter 13: System Services
StatusActivity now implements LocationListener, the interface for callbacks fromthe location we define local variables for LocationManager, Location, and our get the provider from the Yamba application object, as we explained earlier. Andultimately, the user chooses the provider in the check whether the user wants us to provide her location information at we pass that test, we get the location information via getSystemService(). Thiscall is relatively inexpensive, even if it happens every time the method runs, becausewe’re just getting a reference to an already running system the cached location if the location manager has with the location manager to receive location updates. Here, we get tospecify how often we’d like to receive notifications and for what kind of change inlocation. In our example, we care only about the general vicinity at a city level, sowe set these values to 1,000 meters (one kilometer) and 3,600,000 milliseconds (onehour). Note that this is just a hint to the this activity is no longer visible, we unregister from the location manager andno longer receive any updates to help save battery the user is about to update her status, we check whether we have a we do, we pack it into the required double array and pass it on tosetMyLocation() in Yamba’s Twitter we implement the methods that the location manager calls. onLocationChanged() is called whenever there’s a change in location and provides us with theactual new Location method is called when the provider is no longer available. We can simply re-move any updates so that we don’t waste the the provider we care about becomes available, we can request location up-dates method is called when there’s a change with the provider in general. In thiscase, we ignore this point our Yamba application supports location updates. The user can set pref-erences to indicate what location provider to use, if , we’re going to see another system service, this time the Alarm service, which we’lluse to trigger an Intent Yamba to Use the Location Service|205
Intent ServiceNow that we understand how system services work, we can use another service conceptto substantially simplify our Updater service. If you recall, our Updater service is analways-on, always-running service that periodically goes to the cloud and pulls downthe latest timeline updates. Since by default a service runs in the same thread as theuser interface (., it runs on the UI thread), we had to create a separate thread calledUpdater within the Updater service that is responsible for the actual network connec-tion. We then started this thread in the service’s onCreate() and onStartCommand()methods. We ran it forever until onDestroy() got called. However, our Updater threadwould sleep between the updates for some amount of time. All this worked well inChapter 8, but there’s a simpler way to accomplish this task, shown in Example IntentService is a subclass of Service and is also activated by a startService()intent. Unlike a regular service, it runs on its own worker thread, so it doesn’t blockour precious UI thread. Also, once it’s done, it’s done. This means it runs only once,but we will use an Alarm later to run it periodically. Any call to the intent’s startService() will recreate a regular service, we don’t override onCreate(), onStartCommand(),onDestroy(), and onBind(), but rather a new onHandleIntent() method. This methodis where we want to put our code that goes online and handles the network , unlike a regular service, an IntentService has a default constructor that must short, instead of creating a separate thread and delaying network updates as in aregular service, we can simplify our code by using an IntentService to run status up-dates on its worker thread. Now we just need something to periodically wake up ourIntentService so it knows it needs to handle the updating job. For that, we’ll use theAlarm manager, another system key to Intent services is the onHandleIntent() method, a block of code that will runon a separate 13-9. based on IntentServicepackage ;import ;import ;import ;public class UpdaterService1 extends IntentService { // private static final String TAG = "UpdaterService"; public static final String NEW_STATUS_INTENT = "_STATUS"; public static final String NEW_STATUS_EXTRA_COUNT = "NEW_STATUS_EXTRA_COUNT"; public static final String RECEIVE_TIMELINE_NOTIFICATIONS = "_TIMELINE_NOTIFICATIONS";206|Chapter 13: System ServicesDownload from Wow! eBook <>
public UpdaterService1() { // super(TAG); (TAG, "UpdaterService constructed"); } @Override protected void onHandleIntent(Intent inIntent) { // Intent intent; (TAG, "onHandleIntent'ing"); YambaApplication yamba = (YambaApplication) getApplication(); int newUpdates = (); if (newUpdates > 0) { // (TAG, "We have a new status"); intent = new Intent(NEW_STATUS_INTENT); (NEW_STATUS_EXTRA_COUNT, newUpdates); sendBroadcast(intent, RECEIVE_TIMELINE_NOTIFICATIONS); } }}We now subclass IntentService instead of its parent, default constructor is needed. This is a good place to give your service a name,which can be useful in TraceView, for example, to help identify various is the key method. The work inside of it takes place on a separate worker threadand doesn’t interfere with the main UI rest of the code in this section broadcasts the change, as described in “Broad-casting Intents” on page this point, our service is updated. An easy way to test it would be to change theStart/Stop Service menu item to a Refresh button. To do that, update your to include the new item shown in Example 13-10, and change its handling in ourBaseActivity 13-10. res/xml/<?xml version="" encoding="utf-8"?><menu xmlns:android=" ... <item android:title="@string/titleRefresh" android:id="@+id/itemRefresh" android:icon="@android:drawable/ic_menu_rotate"></item></menu>I’ve replaced itemToggle with itemRefresh so that the names make more sense. Wemust also add the appropriate string to the we need to update our file to handle this new Refresh button(see Example 13-11). To do that, we change the appropriate case statement inonOptionsItemSelected(). Additionally, we can now remove onMenuOpened() altogetherIntent Service|207
because we no longer need to change the state of that toggle button—it doesn’t existany 13-11. with support for the Refresh buttonpublic class BaseActivity extends Activity { ... @Override public boolean onOptionsItemSelected(MenuItem item) { switch (()) { ... case : startService(new Intent(this, )); // break; ... } return true; } ...}We simply fire off an intent to start our Updater our options menu now has a Refresh button that will start a service and have itupdate the status data in the background. We can use this button to test whether thisnew feature works way to add the same functionality would have been to use an AsyncTask. Infact, AsyncTask would probably be slightly more appropriate in this case from a designpoint of view, to keep all the functionality at the UI level, but we’ve already discussedit in “Threading in Android” on page 65. Here we wanted to demonstrate quickly howan IntentService is started, and as you can see, it works just like any other , we want to have our Updater service triggered periodically. To do that, we’ll usethe Alarm previous incarnation of our Updater service had a regular service that was alwaysrunning in a loop, pulling network updates, then sleeping for some amount of time,and then looping again. With IntentService, we turned the process around. Our Up-dater service now runs only once when fired up by the startService() intent. Now weneed a way to have something fire these intents every so comes with yet another system service just for that. The Alarm service, rep-resented by the AlarmManager class, lets you schedule certain things to happen at certaintimes. The time can be recurring, which makes it easy to start our service every so the event that happens is an intent, or more precisely, a |Chapter 13: System Services
Pending intentsA PendingIntent is a combination of an intent and an action to be executed on this is used for future intents that you are passing to someone else. Create apending intent via one of the static methods in the PendingIntent class. Since there areonly a handful of ways to send an intent, there are only a handful of static methods tocreate pending intents along with their actions. If you recall, you typically use an intentto start an activity via startActivity(), start a service via startService(), or send abroadcast via sendBroadcast(). So, to create a pending intent that will execute startService() with our intent in the future, we call the getService() static an Interval to PreferencesNow that we know how to leave an intent for someone to execute later and how to tellan Alarm service to repeat that periodically, we need to choose where to implementthis feature. One good place is our existing BootReceiver, but before we do that, we’lladd another option to our preferences, shown in Example 13-12. with arrays for interval options<?xml version="" encoding="utf-8"?><resources> ... <!-- --> <string-array name="interval"> <item>Never</item> <item>Fifteen minutes</item> <item>Half hour</item> <item>An hour</item> <item>Half day</item> <item>Day</item> </string-array> <!-- --> <string-array name="intervalValues"> <item>0</item> <item>900000</item> <item>1800000</item> <item>3600000</item> <item>43200000</item> <item>86400000</item> </string-array></resources>These will be the names of options that show up in the will be their corresponding that we have these arrays, we can update as shown in Example 13-13to add to our list of Service|209
Example 13-13. with support for interval preference setting<?xml version="" encoding="utf-8"?><PreferenceScreen xmlns:android=" ... <!-- --> <ListPreference android:entryValues="@array/intervalValues" android:summary="@string/summaryUpdaterInterval" android:title="@string/titleUpdaterInterval" android:entries="@array/interval" android:key="interval" /></PreferenceScreen>This is the list preference. It shows a list of entities, as represented by android:entities. The value associated with it comes from android: we are ready to update BootReceiver and add the Alarm service BootReceiverIf you recall from “BootReceiver” on page 162, a BootReceiver wakes up every time thedevice is booted up. So far, our BootReceiver just starts our Updater service. That wasfine when the Updater service was always on and running, but now it would cause onlya one-time execution of the can use the Alarm service instead to periodically fire intents that start our Updaterservice, as shown in Example 13-14. To do that, we’ll get the reference to the Alarmmanager, create a pending intent to be started each time, and set up the interval atwhich to start the updates. Because our pending intent is meant to start a service, we’lluse the () call, as described in “Pending intents”on page 13-14. updated with Alarm service calls to periodically start the Updaterservicepackage ;import ;import ;import ;import ;import ;import ;public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent callingIntent) { // Check if we should do anything at boot at all long interval = ((YambaApplication) ()) .getInterval(); // if (interval == _NEVER) // 210|Chapter 13: System Services
return; // Create the pending intent Intent intent = new Intent(context, ); // PendingIntent pendingIntent = (context, -1, intent, _UPDATE_CURRENT); // // Setup alarm service to wake up and start service periodically AlarmManager alarmManager = (AlarmManager) context .getSystemService(_SERVICE); // (_REALTIME, System .currentTimeMillis(), interval, pendingIntent); // ("BootReceiver", "onReceived"); }}The previous code assumes that the phone is awake and will not workwhen the device is asleep. When your device is asleep, a different ap-proach is needed (not discussed in this book).Our Yamba application has a simple getter method to return the value of the check the user’s preference to set the frequency of checks for network value of INTERVAL_NEVER (zero) means not to check for updates at is the intent that will run to start our Updater we wrap that intent with the action to start a service and get a new pendingintent. The value -1 is for a request code that is currently not being used. The flagin the final argument indicates whether this intent already exists. We need just toupdate it and not recreate get the reference to AlarmManager via the usual getSystemService() () specifies that we’d like this pending intent to be sent repeat-edly, but we’re not concerned with being exactly on time. The ELAPSED_REALTIME flagwill keep the alarm from waking up the phone just to run the updates. The otherparameters are the current time as the start time for this alarm, our desired interval,and the actual pending intent to execute when the alarm can now install this application on a device (and thus install the updatedBootReceiver), and then reboot the device. Once the device starts, the LogCat shouldindicate that the BootReceiver ran and started the Updater service by posting a pendingintent to the Alarm Service|211
Sending NotificationsHere’s an opportunity to introduce yet another system service—this time the Notifi-cation service. We worked hard to have our Updater service run in the background andget the latest status updates, but what’s the point of all this work if the user is not madeaware that there’s something new to look at? A standard Android UI approach to thiswould be to post a notification to the notification bar up at the top of the screen. Todo that, we use the Notification system ’re going to make the Updater service responsible for posting the notifications, sinceit is the part of the app that knows of new statuses in the first place. To do that, we’llget the reference to the system Notification service, create a new Notification object,and update it with the latest information. The notification itself will contain a pendingintent so that when the user clicks on it, it takes the user to Timeline activity to viewthe latest status updates. Example 13-15 shows the new 13-15. with Notificationspackage ;import ;import ;import ;import ;import ;import ;public class UpdaterService extends IntentService { private static final String TAG = "UpdaterService"; public static final String NEW_STATUS_INTENT = "_STATUS"; public static final String NEW_STATUS_EXTRA_COUNT = "NEW_STATUS_EXTRA_COUNT"; public static final String RECEIVE_TIMELINE_NOTIFICATIONS = ". RECEIVE_TIMELINE_NOTIFICATIONS"; private NotificationManager notificationManager; // private Notification notification; // public UpdaterService() { super(TAG); (TAG, "UpdaterService constructed"); } @Override protected void onHandleIntent(Intent inIntent) { Intent intent; = (NotificationManager) getSystemService(NOTIFICATION_ SERVICE); // = new Notification(_notify_chat, "", 0); // 212|Chapter 13: System ServicesDownload from Wow! eBook <>
(TAG, "onHandleIntent'ing"); YambaApplication yamba = (YambaApplication) getApplication(); int newUpdates = (); if (newUpdates > 0) { (TAG, "We have a new status"); intent = new Intent(NEW_STATUS_INTENT); (NEW_STATUS_EXTRA_COUNT, newUpdates); sendBroadcast(intent, RECEIVE_TIMELINE_NOTIFICATIONS); sendTimelineNotification(newUpdates); // } } /** * Creates a notification in the notification bar telling user there are new * messages * * @param timelineUpdateCount * Number of new statuses */ private void sendTimelineNotification(int timelineUpdateCount) { (TAG, "sendTimelineNotification'ing"); PendingIntent pendingIntent = (this, -1, new Intent(this, ), _UPDATE_CURRENT); // = (); // |= _AUTO_CANCEL; // CharSequence notificationTitle = this .getText(); // CharSequence notificationSummary = ( , timelineUpdateCount); (this, notificationTitle, notificationSummary, pendingIntent); // (0, ); (TAG, "sendTimelineNotificationed"); }}This is just our local reference to the NotificationManager class, which is our accessto the Notification system create a class-global Notification object and update it each time there’s a newnotification for our obtain the reference to the Notification service by using the usual getSystemService() create the notification object that we’ll reuse later. For now, we just specify thestandard icon to use with our notification, and leave the text and timestamp to beupdated later when we are about to post this call our private sendTimelineNotification() method once we know there arenew statuses for the Notifications|213
This pending intent will be kicked off when the user checks the notification in thenotification bar and clicks on the actual item. In this case, we want to take the userto the Timeline activity, so we create an intent for ’re now updating the data for the most recent notification. This is the timestampthat indicates when it flag tells the Notification manager to cancel this notification as soon as the userclicks on it. The notification will be removed from the notification bar at that we get the notification’s title and summary from our file. Noticethat the summary has parameters, so we can use () to update theactual number of new , we tell the Notification manager to post this notification. In this case, wedo not need the ID, so we specify zero. An ID can be used to refer to a notificationlater, usually in order to cancel this point our application is yet again complete. We now have a way to notify theuser of any new status updates so he can stay on top of what is going on in the this point you have seen a few system services—Sensor, Location, Alarm, andNotification—and Android provides a few more services in addition to these. Youmight have noticed that most of them have a lot of similarities, and hopefully you havestarted extrapolating certain patterns. We have also used this chapter to somewhatsimplify our Updater service and introduce Intent services and pending |Chapter 13: System Services
CHAPTER 14The Android InterfaceDefinition LanguageEach application in Android runs in its own process. For security reasons, an applica-tion cannot directly access the data of another application. However, a couple of mech-anisms allow communication between applications. One such mechanism that you’veseen throughout this book is Intents. Intents are asynchronous, meaning that you canpost a message for someone to receive at some future point in time and just continuewith your once in a while we need a more direct, synchronous access to another are many ways to implement this across process boundaries, and collectivelythey are called Interprocess Communication, or IPC for allow cross-application communication, Android provides its own version of anIPC protocol. One of the biggest challenges in IPC is passing data around, such as when passing parameters to method calls on the remote systems. IPC protocols tend to getcomplicated because they have to convert data from its in-memory format to a formatthat’s convenient for sending to another process. This is called marshaling, and theunpacking at the receiver is called help with this, Android provides the Android Interface Definition Language, orAIDL. This lightweight implementation of IPC uses a syntax that is very familiar to Javadevelopers, and there is a tool that automatically creates the hidden code required toconnect a client and a remote illustrate how to use AIDL to create an interprocess communication, we’ll createtwo applications: a remote service called LogService and a client called LogClient thatwill bind to that remote the Remote ServiceOur remote service, LogService, will simply allow remote clients to log a message to
We are going to start by creating the interface for the remote service. This interfacerepresents the API, or set of capabilities that the service provides. We write this interfacein the AIDL language and save it in the same directory as our Java code with an . AIDL syntax is very similar to a regular Java interface. You simply define themethod signature. The datatypes supported by AIDL are somewhat different from reg-ular Java interfaces. However, all Java primitive datatypes are supported, and so arethe String, List, Map, and CharSequence you have a custom complex data type, such as a class, you need to make itParcelable so that the Android runtime can marshal and unmarshal it. In this example,we’ll create a Message as a custom the AIDLWe start by defining the interface for our service. As you can see in Example 14-1, theinterface very much resembles a typical Java interface. For readers who might haveworked with CORBA in the past, AIDL has its roots in CORBA’s 14-1. ; // import ; // interface ILogService { // void log_d(String tag, String message); // void log(in Message msg); // }Just as in Java, our AIDL code specifies what package it’s part , unlike Java, we have to explicitly import other AIDL definitions, even ifthey are in the same specify the name of our interface. Interface names conventionally start with Ifor method is simple because it doesn’t return anything and takes only primitivesas inputs. Note that the String class is not a Java primitive, but AIDL considers itto be method takes our custom Message parcel as its input. We’ll define Message , we’ll look at the implementation of the Message AIDL, shown in Example 14-2. ; // 216|Chapter 14: The Android Interface Definition Language
/* */parcelable Message;Specifies the package it’s that Message is a parcelable object. We will define this object later in this point, we are done with the AIDL. As you save your files, Eclipse automaticallybuilds the code to which the client will connect, called the stub because it looks like acomplete method to the client but actually just passes on the client request to yourremote service. The new Java file is located in the gen folder under /gen/com/marakana/logservice/. Because this file is derived from your AIDL, you shouldnever modify it. The aidl tool that comes with the Android SDK will regenerate itwhenever you make changes to your AIDL that we have the AIDL and the generated Java stub, we are ready to implementthe the ServiceJust like any Android service, we implement LogService in a Java class that subclassesthe system Service class. But unlike our earlier Service implementations, where weignored onBind() but implemented onCreate(), onStartCommand(), and onDestroy(),here we’re going to do the opposite. A method in a remote service starts when the clientmakes its request, which is called binding to the service, and therefore the client requesttriggers the service’s onBind() implement our remote service, we’ll return an IBinder object from the onBind()method in our service class. IBinder represents the implementation of the remoteservice. To implement IBinder, we subclass the class from the auto-generated Java code, and provide the implementation for our AIDL-defined methods,in this case various log() methods. Example 14-3 shows the 14-3. ;import ;import ;import ;import ;import ;public class LogService extends Service { // @Override public IBinder onBind(Intent intent) { // final String version = ().getString("version"); return new () { // Implementing the Remote Service|217
public void log_d(String tag, String message) throws RemoteException { // (tag, message + " version: " + version); } public void log(Message msg) throws RemoteException { // ((), ()); } }; }}LogService is an Android class derived from Service. We’ve seen many services, butthis time around, it’s a bound service, as opposed to UpdaterService, which this is a bound service, we must implement onBind() and have it return a correctinstance of IBinder class. The client passes us an Intent, from which we extract somestring data. During the client implementation, we’ll see how it sets this, and thushow we can pass small amounts of data into the remote service as part of the instance of IBinder is represented by (), a helper method thatis generated for us in the Java stub file created by the aidl tool when we saved ourAIDL interface. This code is part of /gen/com/marakana/logservice/_d() is the simple method that takes two strings and logs them. Our implemen-tation simply invokes the system’s ().We also provide a log() method that gets our Message parcel as its input of this object we extract the tag and the message. Again, for this trivial imple-mentation, we just invoke Android’s logging that we have implemented the service in Java, we have to provide the Java im-plementation of the Message parcel as a ParcelSince Message is a Java object that we’re passing across processes, we need a way toencode and decode this object—marshal and unmarshal it—so that it can be Android, the object that can do that is called a Parcel and implements theParcelable be a parcel, this object must know how to write itself to a stream and how to recreateitself. Example 14-4 shows the 14-4. ;import ;218|Chapter 14: The Android Interface Definition Language
import ;public class Message implements Parcelable { // private String tag; private String text; public Message(Parcel in) { // tag = (); text = (); } public void writeToParcel(Parcel out, int flags) { // (tag); (text); } public int describeContents() { // return 0; } public static final <Message> CREATOR = new <Message>() { // public Message createFromParcel(Parcel source) { return new Message(source); } public Message[] newArray(int size) { return new Message[size]; } }; // Setters and Getters public String getTag() { return tag; } public void setTag(String tag) { = tag; } public String getText() { return text; } public void setText(String text) { = text; }}As we said before, Message implements the Parcelable the Remote Service|219
To be parcelable, this object must provide a constructor that takes in a Parcel andrecreates the object. Here we read the data from the parcel into our local order in which we read in data is important: it must correspond to the order inwhich the data was written () is the counterpart to the constructor. This method is responsiblefor taking the current state of this object and writing it out into a parcel. Again, theorder in which variables are written out must match the order in which they are readin by the constructor that gets this parcel as its ’re not using this method, because we have no special objects within our parcelable object must provide a Creator. This Creator is responsible for creatingthe object from a parcel. It simply calls our other are just various setter and getter methods for our private this point, we have implemented the required Java code. We now need to registerour service with the manifest with the Manifest FileAs always, whenever we provide one of the new main building blocks for an application,we must register it with the system. The most common way to do that is to define it inthe manifest as we registered UpdaterService earlier, we provide a <service> element specifyingour service. The difference this time around is that this service is going to be invokedremotely, so we should specify what action this service responds to. To do that, wespecify the action and the intent filter as part of this service registration:<?xml version="" encoding="utf-8"?><manifest xmlns:android=" package="" android:versionCode="1" android:versionName=""> <application android:icon="@drawable/icon" android:label="@string/app_name"> <!-- --> <service android:name=".LogService"> <!-- --> <intent-filter> <action android:name="" /> </intent-filter> </service> </application> <uses-sdk android:minSdkVersion="4" /></manifest>This is where we define our service. It is a <service> element within the |Chapter 14: The Android Interface Definition Language
The difference between this service and our UpdaterService is that this service isgoing to be remote to the client. Therefore, calling it by an explicit class namewouldn’t work well, because the client might not have access to the same set ofclasses. So instead, we provide the intent filter and action to which this service isregistered to this point, our service is complete. We can now move on to the the Remote ClientNow that we have the remote service, we are going to create a client that connects tothat service to test that it all works well. Note that in this example we purposely sepa-rated the client and the server into two separate projects with different Java packagesaltogether, in order to demonstrate how they are separate we’re going to create a new Android project in Eclipse for this client, just as we’vedone before for various other applications. However, this time around we are also goingto make this project depend on the LogService project. This is important becauseLogClient has to find the AIDL files we created as part of LogService in order to knowwhat that remote interface looks like. To do this in Eclipse: you have created your LogClient project, right-click on your project in Pack-age Explorer and choose the “Properties for LogClient” dialog box, choose Java Build Path, and then clickon the Projects this tab, click on “Add…”, and point to your LogService procedure will add LogService as a dependent project for to the Remote ServiceOur client is going to be an activity so that we can see it working graphically. In thisactivity, we’re going to bind to the remote service, and from that point on, use it as ifit were just like any other local class. Behind the scenes, the Android binder will marshaland unmarshal the calls to the binding process is asynchronous, meaning we request it and it happens at somelater point in time. To handle that, we need a callback mechanism to handle remoteservice connections and we have the service connected, we can make calls to it as if it were any other localobject. However, if we want to pass any complex data types, such as a custom Javaobject, we have to create a parcel for it first. In our case, we have Message as a customtype, and we have already made it parcelable. Example 14-5 shows the the Remote Client|221Download from Wow! eBook <>
Example 14-5. ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;import ;public class LogActivity extends Activity implements OnClickListener { private static final String TAG = "LogActivity"; ILogService logService; LogConnection conn; @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Request bind to the service conn = new LogConnection(); // Intent intent = new Intent(""); // ("version", ""); // bindService(intent, conn, _AUTO_CREATE); // // Attach listener to button ((Button) findViewById()).setOnClickListener(this); } class LogConnection implements ServiceConnection { // public void onServiceConnected(ComponentName name, IBinder service) { // logService = (service); // (TAG, "connected"); } public void onServiceDisconnected(ComponentName name) { // logService = null; (TAG, "disconnected"); } } public void onClick(View button) {222|Chapter 14: The Android Interface Definition Language
try { _d("LogClient", "Hello from onClick()"); // Message msg = new Message(()); // ("LogClient"); ("Hello from inClick() version "); (msg); // } catch (RemoteException e) { // (TAG, "onClick failed", e); } } @Override protected void onDestroy() { (); (TAG, "onDestroyed"); unbindService(conn); // logService = null; }}LogConnection is our class that both connects to and handles disconnections fromthe remote service. The class is explained is the action intent that we’re using to connect to the remote service. It mustmatch the action that LogService specified in the manifest file as part of its is where we add the data to the intent, to be extracted by the remote bindService() method asks the Android runtime to bind this activity to theremote service specified by the intent action. In addition to the intent, we passon the Service Connection class to handle the actual connection. The BIND_AUTO_CREATE flag indicates that if the service we’re trying to connect to doesn’t alreadyexist, it should be is the class that will be called back upon successful connectionto the remote service and whenever the service disconnects. This class needsto subclass ServiceConnection and implement onServiceConnected() and onServiceDisconnected().onServiceConnected() is called once the bind succeeds. At this point, the IBinderinstance represents our remote now need to cast the bound service into our LogService instance. To do that,we use a helper method named (), provided by thatJava stub that was created automatically by the aidl tool when we saved our the Remote Client|223
onServiceDisconnected() is called once the remote service is no longer available. Itis an opportunity to handle any necessary cleanup. In this case, we just set logService to null to help with the garbage that we have successfully bound to the remote service, we can now makecalls to it as if it were a local call. _d() simply passes two strings tothe log_d() method that we saw defined in mentioned earlier, if we want to pass a Message to the remote method, we haveto create a parcel for it first. This is possible because Message is a parcelable then set its properties using appropriate we have the parcel, we simply call () and pass it to LogService, where it gets we make a remote call, it could fail for a variety of reasons outside of ourcontrol. Because of that, it is a good practice to handle a possible this activity is about to be destroyed, we ask to unbind the service and freethose this point our client is complete. There’s a simple UI with a single button that triggersan onClick() call. Once the user clicks the button, our client should invoke the remotecall in the That It All WorksTry to run the client from within Eclipse. Since Eclipse knows that LogClient is de-pendent on LogService, it should install both packages onto your device. Once theclient starts, it should bind to the service. Try clicking on the button and check thatLogService is indeed logging. Your adb logcat call should give you something like this:...I/LogActivity( 613): connected...D/LogClient( 554): Hello from onClick() version: 554): Hello from inClick() version ...The first line is from the LogConnection in the client, indicating that we’ve successfullybound to the service. The other two lines are from the remote service, one for _d() and the other one for (), where we passed in theMessage |Chapter 14: The Android Interface Definition Language
If you run adb shell ps to see the running processes on your device, you’ll notice twoseparate line items for the client and the server:app_43 554 33 130684 12748 ffffffff afd0eb08 S _42 613 33 132576 16552 ffffffff afd0eb08 S indicates that indeed the client and server are two separate provides an interprocess communication mechanism based on its binder, ahigh-performance, shared-memory system. To create a remote service, we define itusing the Android Interface Definition Language (AIDL), in a way similar to Java in-terfaces. We then implement the remote interface and connect to it via the IBinderobject. This allows us to connect our client to a remote service in a different |225
CHAPTER 15The Native Development Kit (NDK)The Native Development Kit, or NDK, is an add-on to SDK that helps you integratenative code—code that uses platform-specific features, generally exposed through Cor C++ language APIs—within your Android application. The NDK allows your An-droid application to call some native code and even include some native the Gingerbread release of Android, NDK takes support for native code even furtherwith the introduction of the NativeActivity class. You can now write your entire ac-tivity in C or C++. However, NativeActivity is not the subject of this chapter. Here,we’ll look at integrating native C code within your Java Android Is and Isn’t the NDK For?The main motivation for developing parts of your app in native code is you can see, the NDK supports math and graphics libraries well, as well as somesupporting system libraries. So graphically and computationally intensive applicationsare the best candidates for NDK. One could argue that the recent boom in the popu-larity of mobile games is driving this development as that any native code accessible from your app via the Java Native Interface (JNI)still runs inside your application’s Dalvik VM. So it’s subject to the same security sand-boxing rules that an Android application lives by. Writing parts of your application inC or C++ just so you can do something that might not be possible in Java usually isnot a good reason for NDK. Keep in mind that most of the low-level hardware featuresare already elegantly exposed via the Android framework in Java and are usually whatyou want to use Solved by the NDKThe NDK addresses several of the major issues you’d have to deal with if you weredoing native development
The ToolchainJava offers access to native code via the Java Native Interface (JNI). To make it work,you would typically have to compile everything on your host computer for the targetarchitecture, which would require you to have the entire tool chain on your develop-ment machine. Setting up the proper cross-compiler and other tools is not provides the complete toolchain you need to compile and build your native codeso it can run on your target platform. The build system makes it very easy to set upyour environment and integrate your native code into your Your LibsIf you had a native library and wanted it to be available to your application, you’d haveto make sure it is part of the library path where the system searches for libraries to is typically LD_LIBRARY_PATH on Linux. On an Android device, only the /system/lib directory is part of this path. This is a problem because the entire /system partitionis read-only and thus unavailable for installing solves this problem by providing for a mechanism to ship your native library aspart of your Application Package (APK) file. Basically, when the user installs an APKthat contains a native library, the system creates a directory named /data/data/ If you recall from “The Filesystem Explained” on page 95, this par-tition is private just to your application and thus is a safe place to keep your librariesfor the user, while blocking other applications from loading and using your packaging mechanism is a dramatic change to the rules for distributing applica-tions on Android devices, and is a big deal because it brings the huge range of legacyand new native code into the and Standardized HeadersThe NDK comes with helpful documentation and a sample application explaining howto get things done in native code. It also standardizes on certain guaranteed C andC++ headers, such as:•libc (C library) headers•libm (math library) headers•JNI interface headers•libz (Zlib compression) headers•liblog (Android logging) header•OpenGL ES and OpenGL ES (3D graphics libraries) headers•libjnigraphics (Pixel buffer access) header (for Android and above)•A minimal set of headers for C++ support228|Chapter 15: The Native Development Kit (NDK)
•OpenSL ES native audio libraries•Android native application APIsGiven this set of standard headers, you might have extrapolated what NDK is wellsuited for. We’ll examine that in the next NDK Example: FibonacciBecause the NDK is well-suited for computationally intensive applications, I wantedto find an example where we can implement a relatively simple algorithm in both nativecode and Java to compare their relative I picked a Fibonacci algorithm as the example. It’s a fairly simple algorithm that canbe implemented easily in both C and Java. Additionally, we can implement it recursivelyas well as a quick refresher, the Fibonacci series is defined as:fib(0)=0fib(1)=1fib(n)=fib(n-1)+fib(n-2)So the Fibonacci sequence looks like this: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, andso this example, we are going to:•Create the Java class representing the Fibonacci library.•Create the native code header file.•Implement the native code by writing C code.•Compile everything and build a shared library.•Use this native code inside an Android is where we declare our algorithms for computing the Fibonacci sequence. Wehave a total of four versions of the Fibonacci algorithm:•Java recursive version•Java iterative version•Native recursive version•Native iterative versionWe’ll write the Java implementation in Example 15-1 and do the native ones in C NDK Example: Fibonacci|229Download from Wow! eBook <>
Example 15-1. ;public class FibLib { // Java implementation - recursive public static long fibJ(long n) { // if (n <= 0) return 0; if (n == 1) return 1; return fibJ(n - 1) + fibJ(n - 2); } // Java implementation - iterative public static long fibJI(long n) { // long previous = -1; long result = 1; for (long i = 0; i <= n; i++) { long sum = result + previous; previous = result; result = sum; } return result; } // Native implementation static { ("fib"); // } // Native implementation - recursive public static native long fibN(int n); // // Native implementation - iterative public static native long fibNI(int n); // }This is the Java recursive version of the Fibonacci recursive iterative version of the same Java recursive algorithm. Everything that can beimplemented recursively can be reduced to an iterative algorithm as native version will be implemented in a shared library. Here, we tell the Javavirtual machine to load that library so the function can be found when declare the native Fibonacci method, but don’t implement it. Notice the use ofthe native keyword here. It tells the Java VM that the implementation of this methodis in a shared library. The library should be loaded prior to this method previous declaration is for the recursive native implementation. This one is forthe iterative |Chapter 15: The Native Development Kit (NDK)
At this point, our FibLib is complete, but we still need to back the native methods withtheir C implementations. To do that, first we need to create the appropriate JNI JNI Header FileThe next step is to create the C header file based on our FibLib Java file. To do that,we use Java’s standard javah tool. Note that you must have the Java Development Kit(JDK) installed in order to find this tool in the JDK/bin , to create the C header, go to your project’s bin directory and execute:[Fibonacci/bin]> javah -jni -jni takes a Java class as the parameter. Not all the classes are in the Java class-path by default, so it is easiest to just change directory to your project’s bin , we assume that the current working directory is part of your Java classpath andthus that javah -jni at this location will result should be a new file named . This is the C headerfile that we need to implement implementing our native files, let’s organize our project a little bit. AlthoughEclipse did a lot to set up our Android application directories in a meaningful way thusfar, it doesn’t yet offer that level of support and automation for NDK development. Weare going to do a couple of steps manually one, create a directory named jni inside your Eclipse Fibonacci project. This willbe the place where you’ll store all your native code and related files. You can create thisdirectory from within Eclipse by selecting the Fibonacci project in Package Explorer,right-clicking on it, and choosing New→, move this new header file into that folder:[Fibonacci/bin]> mv ../jni/You can look into this file:/* DO NOT EDIT THIS FILE - it is machine generated */#include <>/* Header for class com_marakana_FibLib */#ifndef _Included_com_marakana_FibLib#define _Included_com_marakana_FibLib#ifdef __cplusplusextern "C" {#endif/* * Class: com_marakana_FibLib * Method: fibN * Signature: (I)J */An NDK Example: Fibonacci|231
JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN (JNIEnv *, jclass, jint);/* * Class: com_marakana_FibLib * Method: fibNI * Signature: (I)J */JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI (JNIEnv *, jclass, jint);#ifdef __cplusplus}#endif#endifAs you can see, this file is automatically generated and is not to be modified by theprogrammer directly. You may observe signatures for two of our native functions thatwe’re yet to implement:...JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN (JNIEnv *, jclass, jlong);...JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI (JNIEnv *, jclass, jlong);...These are standard JNI signatures. They are generated by a naming convention indi-cating that the function contains code defined in Java as part of the class for the native methods fibN and fibNI. You can also see that bothmethods return jlong, a JNI-standardized integer input parameters are also interesting: JNIEnv, jclass, and jlong. The first two arealways part of a Java class, created to interface with native code. The JNIEnv points backto the virtual machine environment, and the next parameter points back to the class orobject where this method is from; the parameter is jclass for a class method orjobject for an instance method. The third parameter, jlong, is just our input into theFibonacci algorithm, or our that we have this header file, it is time to provide its implementation in ImplementationWe are going to create a C file that will implement our native algorithms. For simplic-ity’s sake, we’ll call it . Like the header file we looked at earlier, this file will residein the jni folder. To create it, right-click on the jni folder and choose New→File. Saveit as |Chapter 15: The Native Development Kit (NDK)
When you open the C file, it might open up in another editor outsideof Eclipse. That’s because the Java version of Eclipse typically doesn’thave support for C development. You could extend your Eclipse withC development tools by opening Eclipse and going to Help→Install NewSoftware. Alternatively, you can just open the file with the standardEclipse text editor by selecting the file and choosing Open With→, we provide the implementation of the Fibonacci algorithm in C in this file,as shown in Example 15-2. The C versions of our algorithms are almost identical to theJava 15-2. jni/ "" /* *//* Recursive Fibonacci Algorithm */long fibN(long n) { if(n<=0) return 0; if(n==1) return 1; return fibN(n-1) + fibN(n-2);}/* Iterative Fibonacci Algorithm */long fibNI(long n) { long previous = -1; long result = 1; long i=0; int sum=0; for (i = 0; i <= n; i++) { sum = result + previous; previous = result; result = sum; } return result;}/* Signature of the JNI method as generated in header file */JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibN (JNIEnv *env, jclass obj, jlong n) { return fibN(n);}/* Signature of the JNI method as generated in header file */JNIEXPORT jlong JNICALL Java_com_marakana_FibLib_fibNI (JNIEnv *env, jclass obj, jlong n) { return fibNI(n);}We import , the header file that was produced when wecalled javah -jni actual recursive Fibonacci algorithm. This is fairly similar to the Java NDK Example: Fibonacci|233
An iterative version of Fibonacci. Again, very similar to the Java provides this function to us. Copy and paste the prototype from , add variable names, and call the appropriate C function toproduce the for the iterative signature of the that we have implemented C versions of Fibonacci, we want to build the sharedlibrary. To do that, we need an appropriate MakefileTo build the native library, the makefile must describe our files. The file isshown in Example 15-3. jni/_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := fibLOCAL_SRC_FILES := $(BUILD_SHARED_LIBRARY)The makefile is a part of the standard Android make system. All we are adding here isour specific input () and our specific output (the fib module). The name of themodule we specify is important and will determine the name of the library based onthe operating system convention. For example, on ARM-based systems, the output willbe a we have this makefile, we’re ready to initiate the the Shared LibraryAssuming you have the NDK installed properly, you can now build the native sharedlibrary by running ndk-build in your project directory. Here, ndk-build is a tool in thedirectory where your NDK is installed. We assume you put this directory into yourenvironment this point, you should have a subdirectory named lib containing your shared you deploy the Fibonacci application in the next section, this library is packagedas part of the shared library is compiled to run on the emulator by default, so it’sbased on ARM |Chapter 15: The Native Development Kit (NDK)
Finally, we need an application to put this library to good Fibonacci ActivityThe Fibonacci Activity asks the user to input a number. Then, it runs the four algo-rithms to compute the Fibonacci value of that number. It also times the computationand prints the results to the screen. This activity basically uses the FibLib class that inturn uses for its native part. Example 15-4 shows the 15-4. ;import ;import ;import ;import ;import ;import ;import ;public class Fibonacci extends Activity implements OnClickListener { TextView textResult; Button buttonGo; EditText editInput; @Override public void onCreate(Bundle savedInstanceState) { (savedInstanceState); setContentView(); // Find UI views editInput = (EditText) findViewById(); textResult = (TextView) findViewById(); buttonGo = (Button) findViewById(); (this); } public void onClick(View view) { int input = (().toString()); // long start, stop; long result; String out = ""; // Dalvik - Recursive start = (); // result = (input); // stop = (); // out += ("Dalvik recur sive: %d (%d msec)", result, stop - start); // Dalvik - IterativeAn NDK Example: Fibonacci|235
start = (); result = (input); // stop = (); out += ("\nDalvik iterative: %d (%d msec)", result, stop - start); // Native - Recursive start = (); result = (input); // stop = (); out += ("\nNative recursive: %d (%d msec)", result, stop - start); // Native - Iterative start = (); result = (input); // stop = (); out += ("\nNative iterative: %d (%d msec)", result, stop - start); (out); // }}We convert the string we get from the user into a we start the calculation, we take the current perform the actual Fibonacci calculation by invoking the appropriate staticmethod in FibLib. In this case, it’s the Java recursive take another timestamp and subtract the previous one. The delta is the lengthof the computation, in do the same for the iterative Java implementation of we use the native recursive finally, we use the native iterative format the output and print out the results on the That It All WorksAt this point, we can fire up the Fibonacci application and run some tests on it. Keepin mind that larger values for n take quite a bit longer to process, especially using therecursive algorithms. One suggestion would be to keep n in the 25–30 range. Also keepin mind that we are doing all this processing on Activity’s main UI thread, and blockingthat thread for a long period of time will lead to the Application Not Responding (ANR)error we showed in Figure 6-9. As an exercise, you might want to move the actualcalculation into an AsyncTask, as described in “AsyncTask” on page 67, to preventblocking the main |Chapter 15: The Native Development Kit (NDK)
As you run some tests, you might notice that the native version of the algorithm runsabout one order of magnitude faster than the Java implementation (see Figure 15-1).Figure 15-1. Fibonacci of 33These results alone should provide enough motivation to consider moving some of yourcomputationally intensive code into native code. NDK makes the job of integratingnative code into your app much with the Gingerbread version of Android, NDK also supports Native activities,a way to create an entire activity in C and still have it adhere to the activity life cyclerules, as discussed in “Activity Life Cycle” on page 28. This makes game developmentin Android even |237Download from Wow! eBook <>
IndexAapplication objects, 102–105, 113, 133application signing, 12AbsoluteLayout, 50AppWidgetProvider class, 182activities, 28, 39ARGB color set, 77adb shell, 98asInterface() method, 223addPreferencesFromResource() method, 87afterTextChanged() method, 73AsyncTask, 67AVD (Android Virtual Device), 23, 96AIDL (Android Interface Definition Language),215Alarm service, 205, 208–211BAlpha channel, 77background, running services in, 31Android components (see activities) (seebase activity example, 153broadcast receivers) (see contentbattery consumption, 29, 34, 50, 167, 190, 195,providers) (see system services)205Android Device Dashboard, 5bindService() method, 223Android Interface Definition Language (AIDL),bindView() method, 146, 149215Bionic, 9Android Project, 18BootReceiver, 162, 210–211Android UI (see UI (user interface))Bornstein, Dan, 9Android Virtual Device (AVD), 23, 96broadcast receivers, 34, 39, 161–173Android, history of, 3BootReceiver, 162, 210– examples, 61, 163network receivers, 167–169Apache Harmony, 9timeline receivers, 163–165, 172APIbuild target, 19, 51and AIDL interface, 216building blocks, overview, 27Content Provider, 178, 183buttons, 48, 52–59, 91, 108, 154, 207levels, 4, 19, 51Location, 195root, 83, 85, 95, 98CTwitter-compatible, 58, 59, 113, 127C implementation, 12, 227, 228, 231, 232APK (Application Package) file, 12, 228, 234Canvas, 194app resources, 90classpaths, 60Application Context, 34close() method, 133application framework, 11coarse location permissions, 199application initial setup, 152color, adding, 74–78Compass demo, 189–195We’d like to hear your suggestions for improving our indexes. Send email to index@
compiling code, 59–61format() method, 141comprehensive platform, 1, 37FrameLayout, 50content providerscreating, 175–181overview, 32, 39Gusing through widgets, 181–186garbage collector, 141CRUD principle, 32Geocoder, 196getApplication() mcursors, 122, 141, 180ethod, 35, 105, 116getApplicationContext() method, 35custom permissions, 169–171getColumnIndex() method, 141getDefaultSharedPreferences() method, 94DgetFilesDir() method, () severity level, 62getFriendsTimeline() method, 113, 116, 127,Dalvik, 9129databasesgetID() method, 175, 178constraints on, 129getLatestStatusCreatedAtTime() method, 133overview, 119getReadableDatabase() method, 140schema, creating, 120getRelativeTimeSpanString() method, 148,working with, 139150DbHelper, 120getService() method, 209DDMS, 17, 63, 95, 98, 127getStatusTextById() method, 133debugging, 41getStatusUpdates() method, 158declarative user interface, 47getString() method, 94delete() method, 121, 175getSystemService() method, 190, 195, 202,design philosophy, 39205Destroyed State, 30getTwitter() method, 95, 102, 105, 116, 127development tools, 17getType() method, 175, 180distribution of applications, 12getWriteableDatabase() method, 140doInBackground() method, 67GNU libc, 9Google, 3Egraphics, adding, 74–78gravity property, () severity level, 62Eclipse Android Development Tools (ADT),47HEclipse IDEheader files, creating, 231editing code in, 87, 88Hello, World example, 18–22installing, 16hexadecimal, 77Organize Imports tool, 62Hierarchy Viewer, 81WYSIWYG Editor, 75HTC Sense, 34emulators, 23events, 70–73execSQL() method, 122, () severity level, 62id property, 55FIDE (Integrated Development Environment),fetchStatusUpdates() method, 134, 16616Fibonacci demo, 229–237images, adding, 74file system, 42, 95–99insert() method, 121, 127, 129, 175, 177files, saving, 59insertOrThrow() method, 129fine location permissions, 199insertWithOnConflict() method, 133240|Index
Integrated Development Environment (IDE),, 19416source code, , 56, 58, 67, 71, 109,broadcasts, 161, 165202filter, , 130and menus, 83, 92synchronized method, 104overview, 31, , 139, 144–150, 156,services, 206–208164, 172interfaces, , 106, 111, 114, 124,Internet access, 167, 169134, 165, 206, 212Internet permissions, , 197Interprocess Communication (IPC), 215widget id's, 55interrupt() method, , 102, 133, 201invalidate() method, , 182IPC (Interprocess Communication), 215Java Native Interface (JNI), 227javah tool, 231JNI (Java Native Interface), library, 59Java, 16(see also Eclipse IDE)and AIDL, 216, , 154, 208layout file, , 162, 210layouts and views, 48classes, 51layout_gravity, 55classpaths, 60layout_height, 54compared to native code, 229, 237layout_weight, , 192layout_width, 54Dalvik compiler, 10, , 123building shared, 234errors, 59native, 9–12, , 235packaging, , 230SQLite as set of, 119file naming conventions, 51, 53using third-party, 60gen/com/marakana/, 22licensing, 2, , 22LinearLayout, 49inflated from XML, 48, 56Linux, 7libraries for, 60Lists and Adapters, 43libraries for Android, 11Location Service, 195–, 222LocationListener, , 217Log class, 62loops, 116, 127log() method, , 218LogCat, 62, 113multithreaded execution, 66, 111LogClient demo, , 167logging, 62notifications, 190, 196LogService demo, 215–224packages, 19, 51, 97looping in services, 110parcels, 218, , 87programmatic user interface with, 48MR file and, 22, 90make system, 234Index|241
makeText() method, 69onCreate() method, 56, 87, 93, 106, 107, 121,malware, 13123, 127, 139managers, 11onCreateOptions() method, 156manifest fileonCreateOptionsMenu() method, 89, 91declaring permissions in, 61, 170onDeleted() method, 182LocationListener example, 199onDestroy() method, 106, 107, 112, 139, 141overview, 20onDisabled() method, 182refactoring via, 104onDraw(), 194registering activities via, 88, 150onEnabled() method, 182registering BootReceiver via, 162onHandleIntent() method, 206registering content providers via, 181onLocationChanged() method, 199, 205registering network receivers via, 167onMenuOpened() method, 154, 156, 207registering services via, 107, 220onOptionsItemSelected() method, 89, 91, 109,registering widgets via, 186156, 207marshaling, 215onPause() method, 165, 190, 193, 199Media Store, 33onPostExecute() method, 67, 69menusonProgressUpdate() method, 67, 68adding items to, 108onReceive() method, 161, 182, 184events, 92onResume() method, 139, 165, 172, 190, 193loading, 91onSensorChanged() method, 190, 193resources, 89onServiceConnected() method, 223moveToNext() method, 141onServiceDisconnected() method, 223multithreaded execution, 66onSharedPreferenceChanged() method, 94multithreading, 41onStart() method, 190onStartCommand() method, 106, 107, 109,N112onStop() method, 190name-value pairs, 83naming conventionsonTerminate() method, 104CamelCase, 19onUpdate() method, 182, 184onUpgrade() method, 121, 123Java classes, 19, 51Open Handset Alliance, 1, 4Java packages, 51open source platform, 2JNI signatures, 232OpenGL, 9resources, 56, 79widgets, 55OpenJDK, 10Native Development Kit (NDK), 227–237OpenSSL, 9options menus, 89, 153native libraries, 9, 12, 227NDK (Native Development Kit), 227–237Organize Imports tool, 62Override/Implement Methods, 92, 106network connectivity, 61, 167, 169network latency, 41network receivers, 167–169Pnotification service, 212packages, 19packaging libraries, 228Oparameters, passing, 215Observer pattern, 34, 161Parcel, implementing, 218partitions, 96onAccuracyChanged() method, 190, 193onBind() method, 107, 217password, 98PATH variable, 16onClick() method, 95Paused State, 30PendingIntent, 209242|Index
permissionscustom, 169–171Sdeclaring in manifest file, 61, 170schema, database, 120Schmidt, Eric, 3fine and coarse, 199Internet, 61SDCard partition, 96SDK (Software Development Kit), 15, 19, 23,to send/receive broadcasts, 170for status updates, 17191phishing attacks, 13security, 8, 61, 98, 120, 122, 169, 227portability, 2, 7sendBroadcast() method, 166, 172, 209preferencessendTimelineNotification() method, 213SensorManager, 190accessing in file system, 95–99directing user to, 158services (see system services)setContentView() method, 53, 57initial setup, 152location, 200setDirection() method, 195menu system, 89–92setInexactRepeating() method, 211overview, 83setMyLocation() method, 205prefs resource, 84–87Settings Provider, 33PrefsActivity class, 87–89setupList() method, 158setViewBinder() method, 149security, filesystem, 98shared, 93setViewValue() method, 149prepared statement approach, 127severity levels, log, 62programmatic user interface, 48shared preferences, 93project design, 39signing, application, 12Publish/Subscribe pattern, 161simulators, 23publishers, 161single thread, 65putExtra() method, 166Software Development Kit (SDK) (see SDK(Software Development Kit))spyware, 13QSQL injection, 122QEMU, 23SQLite, 9, 42, 119query() method, 121, 141, 175, 179sqlite3 tool, 128stack, 7–13RstartActivity() method, 92, 152, 209R file, 22Starting State, 29startManagingCursor() method, 141, 180refactoring, 39, 94, 104, 130–135, 189startService() method, 109, 209registering, 94, 95, 150, 162–169, 190–194,Status activity, 202220RelativeLayout, 50status data, 110, 127, 130–135, 177, 184status updatesremote client, 221remote services, 215checking for new, 110using IntentService to run, 206requery() method, 164notification of, 163, 212–214requestLocationUpdates() method, 198permissions for, 171res/layout folder, 53screen, 53resources, 12, 79, 90RGB color set, 77sending, 161storing locally, 119rose widget, 191–195run() method, 113, 134widget for displaying, 181–186Running State, 29, , 52StatusActivity, 52, 56, 67, 91, 105Stopped State, 30Index|243Download from Wow! eBook <>
stopService() method, 109, 168UpdaterService, 105, 114, 124–127, 162, 163,strings resource, 55165, 171Stub() method, 218, 223updateStatus() method, 65subscribers, 161URI (Uniform Resource Identifier), 176system servicesuser data partition, 96, 97adding and handling menus, 108user interface (see UI)Android Services vs. native services, 32user preferences (see preferences)common steps in using, 190username, 98Compass demo, 189–195creating, 105defining in manifest file, 107VViewBinder, 149intents, 206–208views and layouts, 48location service, 195–205looping in, 110viruses, 13notification service, 212overview, 101, 189Wand project design, () severity level, 62testing, 109, 113wakeups, 161Webkit, 9Where Am I? demo, 196–200TwidgetsTableLayout, 50Android UI widgets vs. App Widgets, 49TableRow, 50App Widgets, 181–186testing, 109, 113, 117, 224Compass Rose example, 191–195text property, 55content providers through, 181–186TextWatcher, 70important properties of, () method, 110, 113selection/viewing, 137, 196threading, 65timeline activity example, 137–146, 156, 163and UI sluggishness, 80withAppendedID() method, 178timeline adapter example, 146–150writeToParcel() method, 220timeline receivers, 163–165, () severity level, 62toggle service, 154WYSIWYG Editor, 75Twitter140-character counter, 70–73creating compatible apps, 56–61, 67, 71, 94,X137XMLexample of app, 27, 34android: keyword, 90pulling data from, 105, 113–116, 120, , 21, 61, 88, 151, 163,and Yamba, 37168, 199declarative user interface with, 47developer-friendly view, 89UEclipse file naming/renaming, 53, 79, 84UI (user interface), 39editing options, 88Android objects, 57inflated into Java, 48, 56optimizing, 80intent filter, 151two ways to create, 47layout code, 21, 143, 185Uniform Resource Identifier (URI), , 52unmarshaling, 215manifest file, 88, 107, 181, 186update() method, 121, 175updateAppWidget() method, 184menu resource, , 108244|Index
for preference resource, , 210res/layout/, 21, 196res/layout/, 143res/layout/, 78res/layout/, 70res/layout/, 142res/layout/, 138res/layout/, 185res/menu/, 91, 154res/values/, 21, 56, 92res/xml/, 207res/xml/, 86, 201res/xml/, 185strings, , 152, 209updating directly in, 76viewing, 53, 61YYambaapplication object, 102opening a database, 122overview, 37–45starting, 51updating to use location service, 200–202YambaWidget class, 182Zzero-configuration database, 119Index|245
About the AuthorMarko Gargenta is the founder and chief Android expert at Marakana, a trainingcompany in San Francisco. Marko has developed Android Bootcamp and Android In-ternals courses, and has trained over 1,000 developers on four continents. His clientsinclude Qualcomm, Sony-Ericsson, Motorola, Sharp, Cisco, the US Department ofDefense, and many more. Marko frequently speaks on Android at technical conferencesand events, and is the founder of the San Francisco Android Users animal on the cover of Learning Android is a Little Little Owl is part of the taxonomic family Strigdae, which is informally known as“typical owl” or “true owl” (the other taxonomic family includes barn owls). True toits name, the Little Owl is small, measuring between 23 and centimeters in is native to the warmer areas of east Asia (particularly Korea), Europe, and NorthAfrica and has been introduced and naturalized in Great Britain and the South Islandof New Little Owl is characterized by long legs and a round head with yellow eyes andwhite eyebrows; the eyebrows are said to give the owl a serious expression. The mostwidespread species, Athene noctua, is white and speckled brown on top and white-and-brown streaked on bottom. A species commonly found in the Middle East, A. , or the Syrian Little Owl, is a pale sedentary Little Owl typically makes its home in open country, such as parklandand farmland. It preys on amphibians, earthworms, insects, and even smaller mammalsand birds; despite its diminutive stature, the Little Owl is able to attack many gamebirds. Unlike many of its true owl family members, the Little Owl is diurnal, or activeduring the day, during which it often perches openly. Depending on the habitat, theLittle Owl builds nests in cliffs, rocks, holes in trees, river banks, and buildings. LittleOwls that live in areas with human activity tend to get used to people and may perchin full view when humans are cover image is from Cassell’s Natural History. The cover font is Adobe ITC Gara-mond. The text font is Linotype Birka; the heading font is Adobe Myriad Condensed;and the code font is LucasFont’s TheSansMonoCondensed.
Download from Wow! eBook <>