The Android | What is Android | Androids | Android Market | App for Android | Android Apps | Android Application | Download Android | Tablet Android | Tablet PC | Phone Android | Android 2.2 | Android 2.3 | Android Free | Games for Android | Android Samsung | Galaxy Android | Android Google | Games Android | アンドロイド | Android | Android 2 | Android PC | Google Android | Android Galaxy | Phones with Android | Which Android Phone | What is an Android Tablet | What is an Android Phone
ADW Theme HD for Android
JNI Local Reference Changes in ICS
[This post is by Elliott Hughes, a Software Engineer on the Dalvik team. — Tim Bray]
If you don’t write native code that uses JNI, you can stop reading now. If you do write native code that uses JNI, you really need to read this.
What’s changing, and why?
Every developer wants a good garbage collector. The best garbage collectors move objects around. This lets them offer very cheap allocation and bulk deallocation, avoids heap fragmentation, and may improve locality. Moving objects around is a problem if you’ve handed out pointers to them to native code. JNI uses types such as jobject
to solve this problem: rather than handing out direct pointers, you’re given an opaque handle that can be traded in for a pointer when necessary. By using handles, when the garbage collector moves an object, it just has to update the handle table to point to the object’s new location. This means that native code won’t be left holding dangling pointers every time the garbage collector runs.
In previous releases of Android, we didn’t use indirect handles; we used direct pointers. This didn’t seem like a problem as long as we didn’t have a garbage collector that moves objects, but it let you write buggy code that still seemed to work. In Ice Cream Sandwich, even though we haven't yet implemented such a garbage collector, we've moved to indirect references so you can start detecting bugs in your native code.
Ice Cream Sandwich features a JNI bug compatibility mode so that as long as your AndroidManifest.xml’s targetSdkVersion is less than Ice Cream Sandwich, your code is exempt. But as soon as you update your targetSdkVersion, your code needs to be correct.
CheckJNI has been updated to detect and report these errors, and in Ice Cream Sandwich, CheckJNI is on by default if debuggable="true"
in your manifest.
A quick primer on JNI references
In JNI, there are several kinds of reference. The two most important kinds are local references and global references. Any given jobject
can be either local or global. (There are weak globals too, but they have a separate type, jweak
, and aren’t interesting here.)
The global/local distinction affects both lifetime and scope. A global is usable from any thread, using that thread’s JNIEnv*
, and is valid until an explicit call to DeleteGlobalRef()
. A local is only usable from the thread it was originally handed to, and is valid until either an explicit call to DeleteLocalRef()
or, more commonly, until you return from your native method. When a native method returns, all local references are automatically deleted.
In the old system, where local references were direct pointers, local references were never really invalidated. That meant you could use a local reference indefinitely, even if you’d explicitly called DeleteLocalRef()
on it, or implicitly deleted it with PopLocalFrame()
!
Although any given JNIEnv*
is only valid for use on one thread, because Android never had any per-thread state in a JNIEnv*
, it used to be possible to get away with using a JNIEnv*
on the wrong thread. Now there’s a per-thread local reference table, it’s vital that you only use a JNIEnv*
on the right thread.
Those are the bugs that ICS will detect. I’ll go through a few common cases to illustrate these problems, how to spot them, and how to fix them. It’s important that you do fix them, because it’s likely that future Android releases will utilize moving collectors. It will not be possible to offer a bug-compatibility mode indefinitely.
Common JNI reference bugs
Bug: Forgetting to call NewGlobalRef()
when stashing a jobject
in a native peer
If you have a native peer (a long-lived native object corresponding to a Java object, usually created when the Java object is created and destroyed when the Java object’s finalizer runs), you must not stash a jobject
in that native object, because it won’t be valid next time you try to use it. (Similar is true of JNIEnv*
s. They might be valid if the next native call happens on the same thread, but they won’t be valid otherwise.)
class MyPeer {
public:
MyPeer(jstring s) {
str_ = s; // Error: stashing a reference without ensuring it’s global.
}
jstring str_;
};
static jlong MyClass_newPeer(JNIEnv* env, jclass) {
jstring local_ref = env->NewStringUTF("hello, world!");
MyPeer* peer = new MyPeer(local_ref);
return static_cast<jlong>(reinterpret_cast<uintptr_t>(peer));
// Error: local_ref is no longer valid when we return, but we've stored it in 'peer'.
}
static void MyClass_printString(JNIEnv* env, jclass, jlong peerAddress) {
MyPeer* peer = reinterpret_cast<MyPeer*>(static_cast<uintptr_t>(peerAddress));
// Error: peer->str_ is invalid!
ScopedUtfChars s(env, peer->str_);
std::cout << s.c_str() << std::endl;
}
The fix for this is to only store JNI global references. Because there’s never any automatic cleanup of JNI global references, it’s critically important that you clean them up yourself. This is made slightly awkward by the fact that your destructor won’t have a JNIEnv*
. The easiest fix is usually to have an explicit ‘destroy‘ function for your native peer, called from the Java peer’s finalizer:
class MyPeer {
public:
MyPeer(JNIEnv* env, jstring s) {
this->s = env->NewGlobalRef(s);
}
~MyPeer() {
assert(s == NULL);
}
void destroy(JNIEnv* env) {
env->DeleteGlobalRef(s);
s = NULL;
}
jstring s;
};
You should always have matching calls to NewGlobalRef()
/DeleteGlobalRef()
. CheckJNI will catch global reference leaks, but the limit is quite high (2000 by default), so watch out.
If you do have this class of error in your code, the crash will look something like this:
JNI ERROR (app bug): accessed stale local reference 0x5900021 (index 8 in a table of size 8)
JNI WARNING: jstring is an invalid local reference (0x5900021)
in LMyClass;.printString:(J)V (GetStringUTFChars)
"main" prio=5 tid=1 RUNNABLE
| group="main" sCount=0 dsCount=0 obj=0xf5e96410 self=0x8215888
| sysTid=11044 nice=0 sched=0/0 cgrp=[n/a] handle=-152574256
| schedstat=( 156038824 600810 47 ) utm=14 stm=2 core=0
at MyClass.printString(Native Method)
at MyClass.main(MyClass.java:13)
If you’re using another thread’s JNIEnv*
, the crash will look something like this:
JNI WARNING: threadid=8 using env from threadid=1
in LMyClass;.printString:(J)V (GetStringUTFChars)
"Thread-10" prio=5 tid=8 NATIVE
| group="main" sCount=0 dsCount=0 obj=0xf5f77d60 self=0x9f8f248
| sysTid=22299 nice=0 sched=0/0 cgrp=[n/a] handle=-256476304
| schedstat=( 153358572 709218 48 ) utm=12 stm=4 core=8
at MyClass.printString(Native Method)
at MyClass$1.run(MyClass.java:15)
Bug: Mistakenly assuming FindClass()
returns global references
FindClass()
returns local references. Many people assume otherwise. In a system without class unloading (like Android), you can treat jfieldID and jmethodID as if they were global. (They’re not actually references, but in a system with class unloading there are similar lifetime issues.) But jclass is a reference, and FindClass()
returns local references. A common bug pattern is “static jclass”. Unless you’re manually turning your local references into global references, your code is broken. Here’s what correct code should look like:
static jclass gMyClass;
static jclass gSomeClass;
static void MyClass_nativeInit(JNIEnv* env, jclass myClass) {
// ‘myClass’ (and any other non-primitive arguments) are only local references.
gMyClass = env->NewGlobalRef(myClass);
// FindClass only returns local references.
jclass someClass = env->FindClass("SomeClass");
if (someClass == NULL) {
return; // FindClass already threw an exception such as NoClassDefFoundError.
}
gSomeClass = env->NewGlobalRef(someClass);
}
If you do have this class of error in your code, the crash will look something like this:
JNI ERROR (app bug): attempt to use stale local reference 0x4200001d (should be 0x4210001d)
JNI WARNING: 0x4200001d is not a valid JNI reference
in LMyClass;.useStashedClass:()V (IsSameObject)
Bug: Calling DeleteLocalRef()
and continuing to use the deleted reference
It shouldn’t need to be said that it’s illegal to continue to use a reference after calling DeleteLocalRef()
on it, but because it used to work, so you may have made this mistake and not realized. The usual pattern seems to be where native code has a long-running loop, and developers try to clean up every single local reference as they go to avoid hitting the local reference limit, but they accidentally also delete the reference they want to use as a return value!
The fix is trivial: don’t call DeleteLocalRef()
on a reference you’re going to use (where “use” includes “return”).
Bug: Calling PopLocalFrame()
and continuing to use a popped reference
This is a more subtle variant of the previous bug. The PushLocalFrame()
and PopLocalFrame()
calls let you bulk-delete local references. When you call PopLocalFrame()
, you pass in the one reference from the frame that you’d like to keep (typically for use as a return value), or NULL. In the past, you’d get away with incorrect code like the following:
static jobjectArray MyClass_returnArray(JNIEnv* env, jclass) {
env->PushLocalFrame(256);
jobjectArray array = env->NewObjectArray(128, gMyClass, NULL);
for (int i = 0; i < 128; ++i) {
env->SetObjectArrayElement(array, i, newMyClass(i));
}
env->PopLocalFrame(NULL); // Error: should pass 'array'.
return array; // Error: array is no longer valid.
}
The fix is generally to pass the reference to PopLocalFrame()
. Note in the above example that you don’t need to keep references to the individual array elements; as long as the GC knows about the array itself, it’ll take care of the elements (and any objects they point to in turn) itself.
If you do have this class of error in your code, the crash will look something like this:
JNI ERROR (app bug): accessed stale local reference 0x2d00025 (index 9 in a table of size 8)
JNI WARNING: invalid reference returned from native code
in LMyClass;.returnArray:()[Ljava/lang/Object;
Wrapping up
Yes, we asking for a bit more attention to detail in your JNI coding, which is extra work. But we think that you’ll come out ahead on the deal as we roll in better and more sophisticated memory management code.
A new Android app for Google Offers
Check out today’s deal from REI on the Google Offers app: $15 for $25 to spend on gear at the national outdoor retailer.
Visit Android Market to download the app and check out www.google.com/offers to learn more about Google Offers.
The Google Offers mobile app is only available in the US.
By Eric Rosenblum, Director of Product Management, Google Offers
Introducing the Gmail app for iPhone, iPad and iPod touch
Waiting. Walking. Watching TV. Working out. Winding down. Waking up. We check email pretty much everywhere these days. And when we do, we want easy access to our important messages so we can respond quickly and get back to life -- or slinging birds at thieving green pigs.
With that in mind, we’ve created a new Gmail app for iPhone, iPad, and iPod touch. We’ve combined your favorite features from the Gmail mobile web app and iOS into one app so you can be more productive on the go. It’s designed to be fast, efficient and take full advantage of the touchscreen and notification capabilities of your device. And it’s one more reason to switch to Gmail.
Speed
We want to give you the information you need quickly, with minimal effort and distraction. So we’ve included some time-saving features:
- Get alerted to new messages with push notifications and sounds
- Find an email in seconds with search across your entire inbox
- Autocomplete email addresses from your Gmail contacts or select from your device’s address book
- Upload photos with a click using the new attachment button in compose view
- On iPad, navigate your inbox and read your mail simultaneously with split view
![]() | |
Efficiency
Our inboxes overflow with dozens and even hundreds of messages a day—and this can be even more challenging on a smaller screen. The new Gmail app helps organize your mail so you don’t have to go wading through your inbox to find that key message from your boss or loved one:
- Focus on your important messages first with Priority Inbox
- Quickly scan countless emails on the same subject with threaded conversations
- Organize your mail by archiving, labelling, starring, deleting and reporting spam

Touch
We’ve also optimized the interface so you can perform common actions in Gmail with the lightest touch:
- Pull down your inbox to rapidly refresh if you’re eager for new mail
- Swipe right to view your labels without ever leaving your inbox
- Swiftly scroll through dozens of emails just by sliding your finger
![]() | ![]() |
We hope the Gmail app makes checking email on the go a little easier. It is available in the App Store today and works on all devices running iOS 4+. For more information, check out our help center.
Android 4.0 Graphics and Animations
[This post is by Romain Guy and Chet Haase, Android engineers who have been known to collaborate on the subject of graphics, UIs, and animation. You can read more from them on their blogs at curious-creature.org and graphics-geek.blogspot.com. — Tim Bray]
Earlier this year, Android 3.0 launched with a new 2D rendering pipeline designed to support hardware acceleration on tablets. With this new pipeline, all drawing operations performed by the UI toolkit are carried out using the GPU.
You’ll be happy to hear that Android 4.0, Ice Cream Sandwich, brings an improved version of the hardware-accelerated 2D rendering pipeline to phones, starting with Galaxy Nexus.
Enabling hardware acceleration
In Android 4.0 (API level 14), hardware acceleration, for the first time, is on by default for all applications. For applications at lower API levels, you can turn it on by adding android:hardwareAccelerated="true"
to the <application>
tag in your AndroidManifest.xml.
To learn more about the hardware accelerated 2D rendering pipeline visit the official Android developer guide. This guide explains how to control hardware acceleration at various levels, offers several performance tips and tricks and describes in details the new drawing model.
I also encourage you to watch the Android Hardware Accelerated Rendering talk that we gave at Google I/O 2011.
Introducing TextureView
Applications that need to display OpenGL or video content rely today on a special type of UI element called SurfaceView. This widget works by creating a new window placed behind your application’s window. It then punches a hole through your application’s window to reveal the new window. While this approach is very efficient, since the content of the new window can be refreshed without redrawing the application’s window, it suffers from several important limitations.
Because a SurfaceView’s content does not live in the application’s window, it cannot be transformed (moved, scaled, rotated) efficiently. This makes it difficult to use a SurfaceView inside a ListView or a ScrollView. SurfaceView also cannot interact properly with some features of the UI toolkit such as fading edges or View.setAlpha()
.
To solve these problems, Android 4.0 introduces a new widget called TextureView that relies on the hardware accelerated 2D rendering pipeline and SurfaceTexture. TextureView offers the same capabilities as SurfaceView but, unlike SurfaceView, behaves as a regular view. You can for instance use a TextureView to display an OpenGL scene or a video stream. The TextureView itself can be animated, scrolled, etc.
The following piece of code creates a TextureView to display the video preview from the default camera. The TextureView itself is rotated 45 degrees and semi-transparent.
public class TextureViewActivity extends Activity implements TextureView.SurfaceTextureListener {
private Camera mCamera;
private TextureView mTextureView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
setContentView(mTextureView);
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mCamera = Camera.open();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
try {
mCamera.setPreviewTexture(surface);
} catch (IOException t) {
}
mCamera.startPreview();
mTextureView.setAlpha(0.5f);
mTextureView.setRotation(45.0f);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Ignored, the Camera does all the work for us
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mCamera.stopPreview();
mCamera.release();
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Called whenever a new frame is available and displayed in the TextureView
}
}
A TextureView can just as easily be used to embed an OpenGL scene in your application. As of Android 4.0, eglCreateWindowSurface() can be used to render into a SurfaceTexture object.
Animation
There were minor improvements in Android 4.0 to some of the new animation facilities that were added in the 3.x releases.
First, if you're new to the new android.animation package and classes added in 3.0 and 3.1, you might want to read Animation in Honeycomb and Introducing ViewPropertyAnimator. These articles discuss the new APIs added in 3.0 that make animation in Android easier, more powerful, and more flexible. The Android 4.0 improvements discussed below are small additions to these core facilities.
Properties
One of the constraints of the Java programming language is the lack of “properties”. There is no way to refer generically to a field or a setter/getter of an object. As long as you know what kind of object you have, this is not a problem, because you then know the set of fields and methods that you can call. But if someone passes you some subclass of Object and you'd like to get or set some value “foo” on it, you're out of luck. You can use reflection or JNI to get access to the foo field/methods, but there is no way to refer directly to a field or a method unless you know the instance type of the object that has that field/method on it.
This is a constraint that the new animation system works within. Its whole job, you might say, is to get and set values on generic objects that it's been told about. If someone wants to animate the alpha property on a View, they tell the system the target object and the name of that field (“alpha”), and the animation system uses JNI to figure out which method(s) act on a field of that name. Basically, it looks for setAlpha()
and, sometimes, getAlpha()
methods. Then when an animation frame occurs, it calculates the new value and passes it into the setter method that it found.
This seems like a lot of work for what it does, but there's really no way around it. Unless the animation system were specific to View objects, there's no way that it could know that the target object has appropriate setter/getter methods. And even if it did, there's still no way for callers that construct the animations to tell the animator about the property named “alpha”; there are no function handles like there are in other languages, and there's no way to reference a public field either. (I'm ignoring Reflection here, which has Method and Field objects, because this approach requires much more overhead and processing than the simple function/field references of other languages).
If only there were a way to refer to these properties and to get/set them on generic objects...
Now there is. There is a new Property object in the android.util package that does exactly this. This class offers a set()
and a get()
method. So if someone hands you a Property object, you can safely call the set()
and get()
methods without knowing anything about the target object and it will do the job. Moreover, it will do it much more efficiently than the current JNI or reflection approaches because it can, depending on the implementation of it, set a field or call a method on the target object directly. For example, the new ALPHA property on View calls setAlpha() on the View object.
The Property class is a generic utility that can be used anywhere, not just in animations. But it's animations that benefit enormously from it, in their ability to handle properties in a type-safe and efficient manner.
For example prior to Android 4.0, you might construct a fade-out animation on some object myView like this:
ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "alpha", 0);
In Android 4.0, you can use the new ALPHA object on View to construct a Property-based animator instead:
ObjectAnimator anim = ObjectAnimator.ofFloat(myView, View.ALPHA, 0);
ViewPropertyAnimator Additions
There were minor API additions to the ViewPropertyAnimator class (introduced in Android 3.1) which make this class more flexible and powerful:
cancel()
: You can now cancel() a running ViewPropertyAnimator.setStartDelay()
: You can now set a startDelay on the ViewPropertyAnimator, just like the startDelay of the other Animator classes.start()
: ViewPropertyAnimators start automatically, but they do so on the next animation frame handled. This allows them to collect several requests and bundle them together, which is much more efficient and makes it easier to synchronize multiple animations together. However, if you just want to run a single animation, or want to make sure it starts immediately, at the time of construction, you can callstart()
to avoid that intervening delay.
LayoutTransition
LayoutTransition (introduced in Android 3.0) continues to provide functionality that makes some kinds of animations easier, specifically when adding, removing, hiding, and showing views. For example, either this snippet in a layout file:
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:id="@+id/container">
Or this code added at runtime:
container.setLayoutTransition(new LayoutTransition());
will result in a container that animates the visibility of its children views. So when a view is added to the container, the other children will animate out of the way and then the new one will fade in. Similarly, removing a child from the container will fade it out and then animate the other children around to their final places and sizes.
You can customize the animations and timing behavior of a LayoutTransition object, but the code above allows a very easy way to get reasonable default animation behavior.
One of the constraints in the pre-4.0 version of the class was that it did not account for changes in the bounds of the parent hierarchy. So, for example, if a LinearLayout with horizontal orientation has a layout_width of wrap_content and you want to run a transition that removes an item from that layout, then you might notice the parent snapping to the end size and clipping its children instead of animating all of them into their new positions. The new approach (enabled by default, but disabled by a call to setAnimateParentHierarchy(false))
also animates the layout bounds and scrolling values of the parent layout and its parents, all the way up the view hierarchy. This allows LayoutTransition to account for all layout-related changes due to that view being added or removed from its transitioning container.
Conclusion
The Android 3.0 release saw huge improvements in the visual capabilities of the platform, as we started enabling hardware acceleration and providing new, more flexible animation capabilities. Android 4.0 continues this trend as we continue to work hard on improving the performance, features, and usability of the Android APIs. We’re not done yet, but the enhancements in this release should help you create more exciting Android experiences and applications.
From the desert to the sea: Google Voice Search experiments
Meanwhile, our fellow Google engineers Noel and Alice went up north to the beautiful Great Barrier Reef, to test Google Voice Search underwater.
Now that you’ve seen what Google Voice Search can do, we hope these experiments will inspire you to try it yourself in a more everyday setting, and with your broadest Aussie accent!
You can use Google Voice Search on Android phones by pressing the microphone button on the homescreen or downloading the app. You can also download the app for the iPhone here.
Posted by Mike Lawther, Software Engineer.
Introducing Android WebDriver

[This post is by Dounia Berrada, an engineer on the EngTools team. — Tim Bray]
Selenium WebDriver is a browser automation tool which provides a lightweight and elegant way for testing web apps. Selenium WebDriver is now available as an SDK extra in the Android SDK, and supports 2.3 (Gingerbread) and onwards!
Whether or not your site is optimized for mobile browsers, you can be sure that users will be accessing it from their phones and tablets. WebDriver makes it easy to write automated tests that ensure your site works correctly when viewed from the Android browser. We’ll walk you through some basics about WebDriver and look at it in action.
WebDriver Basics
WebDriver tests are end-to-end tests that exercise the web application just like a real user would. WebDriver models user interactions with a web page such as finger flicks, finger scrolls and long presses. It can rotate the display and interact with HTML5 features such as local storage, session storage and the application cache. Those tests run as part of an Android tests project and are based on Junit. They can be launched from Eclipse or the command line. WebDriver tests can be wired with a continuous integration system and can run on phone and tablet emulators or real devices. Once the test starts, WebDriver opens a WebView configured like the Android browser and runs the tests against it.
WebDriver is an Android SDK extra and can be installed following these instructions. Once you’ve done that you’ll be ready to write tests! There is a comprehensive WebDriver user guide on the Selenium site, but let’s start with a basic example using www.google.com to give you a taste of what’s possible.
Getting Started
First, create an Android project containing an empty activity with no layout.
public class SimpleAppActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
Then create the Android test project that will contain the tests. WebDriver will create the WebView and set the layout automatically in the main Activity.
Let’s write a test that opens the Google home page on Android and issues a query for “weather in San Francisco”. The test will verify that Google returns search results, and that the first result returned is giving the weather in San Francisco.
public class SimpleGoogleTest extends ActivityInstrumentationTestCase2<SimpleAppActivity> {
public void testGoogleShouldWork() {
// Create a WebDriver instance with the activity in which we want the test to run
WebDriver driver = new AndroidDriver(getActivity());
// Let’s open a web page
driver.get("http://www.google.com");
// Lookup for the search box by its name
WebElement searchBox = driver.findElement(By.name("q"));
// Enter a search query and submit
searchBox.sendKeys("weather in san francisco");
searchBox.submit();
// Making sure that Google shows 11 results
WebElement resultSection = driver.findElement(By.id("ires"));
List<WebElement> searchResults = resultSection.findElements(By.tagName("li"));
assertEquals(11, searchResults.size());
// Let’s ensure that the first result shown is the weather widget
WebElement weatherWidget = searchResults.get(0);
assertTrue(weatherWidget.getText().contains("Weather for San Francisco, CA"));
}
}
Now let’s see our test in action! WebDriver will create a WebView with the same configuration as the Android browser in the main UI thread, i.e. the activity thread. The activity will display the WebView on the screen, allowing you to see your web application as the test code is executing.
Interaction Testing
We’ve mentioned that WebDriver supports creating advanced gestures to interact with the device. Let’s use WebDriver to throw an image across the screen by flicking horizontally, and ensure that the next image in the gallery is displayed.
WebElement toFlick = driver.findElement(By.id("image"));
// 400 pixels left at normal speed
Action flick = getBuilder(driver).flick(toFlick, 0, -400, FlickAction.SPEED_NORMAL)
.build();
flick.perform();
WebElement secondImage = driver.findElement(“secondImage”);
assertTrue(secondImage.isDisplayed());
Now, let’s rotate the screen and ensure that the image displayed on screen is resized.
assertEquals(landscapeSize, secondImage.getSize())
((Rotatable) driver).rotate(ScreenOrientation.PORTRAIT);
assertEquals(portraitSize, secondImage.getSize());
What if your test reveals a bug? You can easily take a screenshot for help in future debugging:
File tempFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Find Out More
If this has whetted your appetite and you’d like to know more, go ahead and install the Android WebDriver, take a look at the documentation on the Selenium project’s wiki, or just browse the javadocs. For questions and feedback not only of the Android WebDriver but also its desktop brethren, please join webdriver@googlegroups.com. For announcements keep an eye on http://seleniumhq.wordpress.com/.