Using adb over Wireless / Wifi

Physical cables are rapidly becoming obsolete, and they’re annoying to use for Android development and debugging.

Luckily, adb can easily be configured to work wirelessly and you can free yourself from micro-USB cables (almost) entirely.

To do this, plug a phone into a computer as you would to normally debug over adb. (This is the almost part.) Make sure your phone and computer are both connected to the same wireless network.

Open a terminal, and type:

adb tcpip 5555

This command prepares adb to communicate over tcpip on port 5555. The port number is arbitrary, but you should avoid network ports that are currently in use.

Next, open your device’s settings and find your IP address. Usually this is found in Settings -> Wireless -> Menu -> Advanced. Once you’ve found the device IP, type the following into the terminal:

adb connect <ip>

And that’s it! You’ll now be able to unplug the phone and debug via Eclipse or adb as you would normally.

To switch back to antique usb debugging, just type,

adb usb

while the device is still unplugged from USB.

Note: This might not work with all devices. YMMV.

Standard

Copy Android LogCat / Log Contents From a Device With adb

It’s a bummer when your Android application crashes while you’re away from a computer.

Luckily, there’s a simple adb command that will dump the contents of logcat’s ring buffer to your local disk, so you can examine the cause of the crash:

adb logcat -d -v time > logcat.txt

This will copy the phone’s log to a local file called logcat.txt and add time-stamps to the log entries. On my Galaxy Nexus the ring buffer is 256 Kb, which translates to around 12 hours of log history.

Standard

LightBox Effect for Android Dialogs

The stock black rectangular progress dialogs on Android are ugly, so I created a simple little open-source alternative.

It works by placing a translucent overlay on the Android UI with a ProgressBar and a dialog message. There’s also an alpha animation in there, to really class it up.

Lightbox

To use it, just add the lightbox_dialog.xml file to your res/layout folder. Then add the styles.xml and colors.xml to your res/values folder.

Then, just define these methods and dialog somewhere in an Activity base class:

private Dialog busyDialog;

public void showBusyDialog(String message) {
    busyDialog = new Dialog(this, R.style.lightbox_dialog);
    busyDialog.setContentView(R.layout.lightbox_dialog);
    ((TextView)busyDialog.findViewById(R.id.dialogText)).setText(message);

    busyDialog.show();
}

public void dismissBusyDialog() {
    if (busyDialog != null)
        busyDialog.dismiss();

    busyDialog = null;
}

And that’s it! Source code lives on GitHub here.

Standard

Example Robolectric and Ant Eclipse Project

Until recently, I’ve been unable to configure a Robolectric project that can easily be shared across a team and integrated with Jenkins CI without the use of Maven. I mentioned this in a previous post, and have asked for assistance on Pivotal’s Robolectric Google Group a handful of times. There are several jar files that need to be referenced, and dependencies between the test and main Android projects and (without the use of Maven) this is difficult to configure and move around between teammates.

Thankfully, a brilliant Android engineer named Michael Portuesi came to my aid and helped me sort out the missing pieces, and I decided to create an example Robolectric project to share his wisdom and help other people get started. This project also includes an Ant script that you can easily plug into Jenkins or any other Ant-based CI process.

How does it work?

A Robolectric test project requires dependencies on:

  • android.jar
  • maps.jar
  • junit.jar
  • hamcrest.jar
  • robolectric-with-dependencies.jar
  • The Android project being tested

There are several ways you can set this up, but for simplicity I’ve pinned these jar files into the test project’s libs folder. Some people prefer linking the Robolectric source into their test projects, so they can modify the Robolectric code as necessary. You can also use environment variables in place of the Maps and Android jars, and add references to JUnit and Hamcrest to your test project’s build path via Eclipse.

After cloning this repository, add the android-project and android-project-test projects into an Eclipse workspace. The android-project is an Android project (obviously), and the android-project-test project is a plain old Java project. You should be able to right-click on the android-project-test project, and choose “Run As -> JUnit Test”, and the single test should run via Robolectric and JUnit. Alternatively, you should be able to right-click on the build.xml file and choose “Run As -> Ant Build”. This ant script can be used to run your Robolectric tests via a CI server like Jenkins.

Take note of SampleTestRunner.java. This class inherits from RobolectricTestRunner, and tells Robolectric how to resolve references to your Android project’s manifest and resources:

package com.justinschultz.tests.runner;

import java.io.File;

import org.junit.runners.model.InitializationError;

import com.xtremelabs.robolectric.RobolectricConfig;
import com.xtremelabs.robolectric.RobolectricTestRunner;

public class SampleTestRunner extends RobolectricTestRunner {
	/**
	 * Call this constructor to specify the location of resources and AndroidManifest.xml.
	 *
	 * @param testClass
	 * @throws InitializationError
	 */
	public SampleTestRunner(@SuppressWarnings("rawtypes") Class testClass) throws InitializationError {
		super(testClass, new RobolectricConfig(new File("../android-project/AndroidManifest.xml"), new File("../android-project/res")));
	}
}

When authoring tests, make sure your class names end with the word “Test”, as the ant script requires this. Also make sure that their @RunWith annotations use the SampleTestRunner.class, and NOT the RobolectricTestRunner.class:

@RunWith(SampleTestRunner.class)
public class SampleTest {
    @Test
    public void testBasicResourceValue() throws Exception {
        String helloFromActivity = new MainActivity().getResources().getString(R.string.hello);
        assertThat(helloFromActivity, equalTo("Hello World, MainActivity!"));
    }
}

Finally, make sure junit.jar is defined in your Ant script’s classpath BEFORE android.jar, otherwise Ant will throw an error:

	<path id="junit_classpath">
		<pathelement path="${build.dir}"/>
		<pathelement path="${android.project.classpath}"/>
		<!-- NOTE: junit.jar must come before android.jar! -->
		<filelist refid="libs_jars"/>
		<filelist refid="android_jars"/>
	</path>

And that’s it. You should be able to clone the Android Eclipse Robolectric Example from GitHub and immediately run the sample test via Ant or JUnit in Eclipse.

Standard

Double-Tap to Zoom in Android Maps

Double-Tap to zoom is a typical feature for mapped interfaces on Android. Sometimes it’s just annoying trying to reverse-pinch to zoom with one hand while holding a device.

This is something that’s not technically difficult to do, but it’s not documented very well.

Here’s how to do it:

  1. Write a class that inherits from MapView, and add a GestureDetector to the child class. Then, override the onTouchEvent(MotionEvent event) method and pass the MotionEvent along to your GestureDetector:
package com.justinschultz.android.controls;

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.MotionEvent;
import android.view.GestureDetector.OnGestureListener;
import com.google.android.maps.MapView;

public class MyMapView extends MapView
{
	private GestureDetector gestureDetector;

	public MyMapView(Context context, AttributeSet attrs)
	{
		super(context, attrs);

		gestureDetector = new GestureDetector((OnGestureListener) context);
		gestureDetector.setOnDoubleTapListener((OnDoubleTapListener) context);
	}

    @Override
	public boolean onTouchEvent(MotionEvent ev)
	{
		return this.gestureDetector.onTouchEvent(ev);
	}
}
  1. Refer to this child class in your layout XML file, instead of the regular MapView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="fill_parent">
	<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	    android:orientation="vertical"
	    android:layout_width="fill_parent"
	    android:layout_height="wrap_content">

		<com.justinschultz.android.controls.MyMapView
	    xmlns:android="http://schemas.android.com/apk/res/android"
	    android:id="@+id/mapview"
	    android:layout_width="fill_parent"
	    android:layout_height="wrap_content"
	    android:clickable="true"
	    android:apiKey="YOUR_API_KEY" />

	</RelativeLayout>
</LinearLayout>
  1. Implement the OnGestureListener and OnDoubleTapListener interfaces in your MapActivity class, and write some code to zoom-in the map in the onDoubleTap(MotionEvent e) method:
public class Map extends MapActivity
    implements OnGestureListener, OnDoubleTapListener {
 // More map stuff here, and additional forced overrides from the interfaces
 @Override
 public boolean onDoubleTap(MotionEvent e) {
      // Assuming your MapView is called mapView
     mapView.getController().zoomIn();
     return true;
 }

And that’s it! Your map will now zoom-in when the user double-taps.

Standard

Inspecting and Improving UI Layouts With HierarchyViewer

HierarchyViewer is a visual tool that can be used to inspect your Android user interfaces and help you improve your layout designs and performance. It’s packaged with the Android SDK, and lives in the <android-sdk>/tools folder.

To launch hierarchyviewer, simply open a terminal or command window and type hierarchyviewer. To open hierarchyviewer via Eclipse, select Window -> Open Perspective -> Hierarchy View.

For security reasons you can’t run it on-device, so you’ll need to have an application running on an emulator. Also, make sure Eclipse isn’t trying to debug your application when using HierarchyViewer or it won’t work.

As its name suggests, HierarchyViewer will present you with a hierarchy of the view objects for the layout you select. After you’ve launched the tool, select the Activity you’d like to profile and click the button labeled “Load View Hierarchy.” For my simple demo, the hierarchy looks like this:

Hierarchy Viewer

UI Performance

In addition to providing information about the view hierarchy, the HierarchyViewer tool also provides 3 important metrics about the rendering performance for each View object on the selected Layout’s UI:

  • The time it takes to Measure
  • The time it takes to Layout
  • The time it takes to Draw

Here’s a great Google IO presentation on using hierarchyviewer, and understanding these metrics:

Standard

Unit Testing Android Applications With Robolectric

Running unit tests on an Android emulator or device is slow! Building, deploying, and launching the application in an emulator often takes several minutes, and that’s painful. It’s downright unacceptable if you’re planning on running your unit-tests as part of a continuous-integration build process, because the build server will need to launch an emulator every time you build and test.

To make matters worse, Google prevents you from using the Android SDK if it’s not running on a device or emulator. If you do, you’ll be thwarted by a RuntimeException and message:

java.lang.RuntimeException: Stub!

Pivotal Labs’ Robolectric is a unit test framework that de-couples the Android SDK jar so you can test-drive the development of your Android app. Tests run inside the JVM on your workstation in seconds.

With Robolectric, you can write tests like this:

// Test class for MyActivity
@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {
    private Activity activity;
    private Button pressMeButton;
    private TextView results;

    @Before
    public void setUp() throws Exception {
        activity = new MyActivity();
        activity.onCreate(null);
        pressMeButton = (Button) activity.findViewById(R.id.press_me_button);
        results = (TextView) activity.findViewById(R.id.results_text_view);
    }

    @Test
    public void shouldUpdateResultsWhenButtonIsClicked() throws Exception {
        pressMeButton.performClick();
        String resultsText = results.getText().toString();
        assertThat(resultsText, equalTo("Robolectric Rules."));
    }
}

Robolectric makes this possible by intercepting the loading of the Android classes and rewriting the method bodies. Robolectric re-defines Android methods so they return null (or 0, false, etc.), or if provided Robolectric will forward method calls to shadow Android objects giving the Android SDK behavior. Robolectric provides a large number of shadow objects covering much of what a typical application would need to test-drive the business logic and functionality of your application. Coverage of the SDK is improving every day.

Resource Support

Robolectric handles inflation of views, string resource lookups, etc. Some view attributes (id, visibility enabled, text, checked, and src) are parsed and applied to inflated views. Activity and View #findViewById() methods return Android view objects. Support exists for include and merge tags. These features allow tests access resources and to assert on view state.

Run Tests Outside of the Emulator

Run your tests on your workstation, or on your Continuous Integration environment. Because tests run on your workstation in a JVM and not in the emulator Android runtime, the dexing, packaging, and installation on the emulator steps are not necessary, allowing you to iterate quickly and refactor your code with confidence.

Here is a link with step-by-step instructions on getting Robolectric set up in Eclipse.

Potential Robolectric Pitfalls

I ran into a few issues using Robolectric at Breezy:

  • Robolectric doesn’t support all Android SDK APIs
  • Running Robolectric in a team environment is difficult without something like Maven. Eclipse doesn’t allow you to set relative directory paths for libraries by default, so if your Robolectric, Android SDK, and JUnit libraries live in different paths on different developer’s machines, you’ll have a tough time sharing a configured test project.
  • Robolectric caches its shadow classes, which can produce unexpected test results. Here’s a blog post about mitigating this.

Despite these few nuances, I love Robolectric. Here’s a great how-to video on Robolectric by Pivotal Labs, and here’s a link to the Robolectric Google Group if you run into problems.

Standard

Android Threading: Part 1

The UI layer of all Android applications runs on a single thread called the UI thread.

Performing long-running operations such as network access or database queries on this thread will block the user interface, and eventually crash your application with an Application Not Responding (ANR) error.

In this tutorial I’ll show you a simple way to perform a long-running task in the background using the AsyncTask class to download an image.

To start, create a new application and update the main.xml layout file with this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btnDownloadImages"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/helloText"
        android:onClick="downloadImage"
        android:text="Tap to Download Image" />

    <ImageView
        android:id="@+id/imgDownload"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/helloText" />

</LinearLayout>

When a user taps the button, we’ll:

  1. Display a busy dialog
  2. Invoke an AsyncTask to download an image from the internet
  3. Display the downloaded image in the ImageView control
  4. Hide the busy dialog

Here’s the Activity class to achieve this (note the comments):

package com.justinschultz.asynctaskexample;

import java.io.InputStream;
import java.net.URL;

import android.app.Activity;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

public class AsyncTaskExampleActivity extends Activity {
	ImageView _imgView = null;
	ProgressDialog _busyDialog = null;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		// Need an Activity-level reference to the ImageView
		_imgView = (ImageView) findViewById(R.id.imgDownload);
	}

	public void downloadImage(View v) {
		// Need to show the busy dialog here, since we can't do it in
		// DownloadImageTask's doInBackground() method
		showBusyDialog();
		new DownloadImageTask()
				.execute("http://code.google.com/android/goodies/wallpaper/android-wallpaper5_1024x768.jpg");
	}

	private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
		@Override
		protected Bitmap doInBackground(String... url) {
			// You might be tempted to display the busy dialog here, but you
			// CAN'T update the UI from this method!
			return loadImageFromNetwork(url[0]);
		}

		@Override
		protected void onPostExecute(Bitmap result) {
			// Our long-running process is done, and we can safely access the UI thread
			dismissBusyDialog();
			_imgView.setImageBitmap(result);
		}
	}

	private Bitmap loadImageFromNetwork(String url) {
		Bitmap bitmap = null;

		try {
			bitmap = BitmapFactory.decodeStream((InputStream) new URL(url).getContent());
		} catch (Exception e) {
			e.printStackTrace();
		}

		return bitmap;
	}

	public void showBusyDialog() {
		_busyDialog = ProgressDialog.show(this, "", "Downloading Image...", true);
	}

	public void dismissBusyDialog() {
		if (_busyDialog != null) {
			_busyDialog.dismiss();
		}
	}
}

The AsyncTask class must be subclassed, and it’s 3 generic types are:

  1. Params: the type of the parameters sent to the task upon execution.
  2. Progress: the type of the progress units published during the background computation.
  3. Result: the type of the result of the background computation.

Not all types are always used by an asynchronous task. To mark a type as unused, simply use the type Void:

private class MyTask extends AsyncTask<Void, Void, Void> { ... }

I don’t recommend using AsyncTask for things like long-running API calls, but it’s fine for short tasks within the activity life (between onResume and onPause). I’ll cover approaches to long-running API calls in a future post.

Download the complete code for this example here.

Standard

Real-Time Android Messaging With Pusher

Pusher is a hosted API for real-time JSON messaging in web and mobile applications via WebSockets.

Examples Pusher applications:

I recently used Pusher at Breezy to notify user’s printers when documents sent from their mobile devices are ready to be printed. It’s very fast (real-time!) and powerful stuff, and I immediately started thinking about ways to implement it in Android applications.

Surprisingly, nobody had built a full-featured Java / Android client for Pusher, so I decided to build one.

Pusher is event-driven, so the first thing to do is create an event listener:

PusherListener eventListener = new PusherListener() {
	@Override
	public void onConnect(String socketId) {
		System.out.println("Pusher connected. Socket Id is: " + socketId);
	}

	@Override
	public void onMessage(String message) {
		System.out.println("Received message from Pusher: " + message);
	}

	@Override
	public void onDisconnect() {
		System.out.println("Pusher disconnected.");
	}
};

Next, create an instance of the Pusher class and pass it the API key you were assigned while signing up for Pusher. Then set the event listener from the previous step and connect:

Pusher pusher = new Pusher(YOUR_API_KEY);
pusher.setPusherListener(eventListener);
pusher.connect();

Pusher’s communication happens over public, private or presence Channels, and you can bind to or trigger events on any of these channel types:

// Public Channel
channel = pusher.subscribe(PUSHER_CHANNEL);
// Private Channel
channel = pusher.subscribe(PUSHER_CHANNEL, AUTH_TOKEN);
// Presence Channel
channel = pusher.subscribe(PUSHER_CHANNEL, AUTH_TOKEN, USER_ID);
// Trigger an event
channel.send("trigger-event", new JSONObject());
// Bind to the "price-updated" event with a ChannelListener
channel.bind("price-updated", new ChannelListener() {
  @Override
  public void onMessage(String message) {
    System.out.println("Received bound channel message: " + message);
  }
});

The Java / Android Pusher library can be found on GitHub here.

Please let me know if you have any feedback or questions.

Standard

Avoiding Android Memory Leaks: Part 1

I recently discovered how easy it is to accidentally “leak” memory and resources in an Android application.

In this tutorial, I’ll illustrate a memory leak with a simple dialog box example, and then show you how to fix it.

Let’s start by creating a simple Android application that displays a dialog in its onCreate event:

package com.justinschultz.android;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;

public class LeakedDialogActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setIcon(android.R.drawable.ic_dialog_alert);
		builder.setMessage("This dialog leaks!").setTitle("Leaky Dialog").setCancelable(false).setPositiveButton("Ok", new DialogInterface.OnClickListener()
		{
			public void onClick(DialogInterface dialog, int which) {}
		});

		AlertDialog alert = builder.create();
		alert.show();
    }
}

If you’ve copied this correctly, the app should look like this:

Leaky Android Dialog

While this application is running, take a look in LogCat. (In Eclipse, LogCat can be displayed via Window -> Show View -> LogCat.)

While the dialog is displayed, rotate the device (or the emulator by pressing CTRL+F11).

You should see a lot of bright red error text in LogCat that looks something like this:

LogCat Leaked Window Error


Even though it looks like everything is working correctly on the device, LogCat displays this error message:

Activity com.justinschultz.android.LeakedDialogActivity has leaked
window com.android.internal.policy.impl.PhoneWindow$DecorView@40543ec0
that was originally added here

So… what happened?

When the device rotates, the onPause and onStop Activity events are invoked. Then a new landscape Activity is created, and its onCreate method is invoked.

Since the dialog is displayed and has a reference to the Activity, it can’t be garbage collected, and subsequently is leaked. That is, you’ve created an object that can’t be garbage collected.

To fix this, let’s make the dialog an Activity member variable and override the onPause method to dismiss it:

  AlertDialog _alert;

    @Override
    public void onPause() {
    	super.onPause();

    	if(_alert != null)
    		_alert.dismiss();
    }

This will dismiss the dialog each time the Activity is destroyed and recreated and the error will magically disappear from LogCat.

Here’s the source for this example.

Standard