Android Pt.3 Snags in the Emulator and Bluetooth Explorations
Continuing my work with Android – I seem to have hit a snag. When I start emulators the process hangs and the emulator never loads properly. I can see adb.exe and the emulator-arm.exe running in the process list, but nothing ever appears on screen. Attempting to kill it from the task list does not work either. The only solution is to reboot my laptop. Not sure what the problem is.
Emulators – What are they?
An emulator is a shell for running an android OS. If you look in your android SDK’s tools folder you can see the binary for the shell. The OS images are configured and stored (by default) under your user’s profile, on windows C:\Users\beren.android\avd. The shell program requires an AVD (Android Virtual Device) file to startup. – or all the options look here. When you run from within Eclipse there are probably ant scripts or something like that to automate the launching of the binaries. I mention all this in case you get the same problem I am currently having – the Run As function in Eclipse all of a sudden stopped working and I can’t launch the emulator – no clue why.
After a bit of poking around I determined that somehow the saved data parts of my emulator configuration had become corrupted. I compared my directory structure to the directory structure of a new emulator runtime I created in eclipse. I just deleted all the “extra” files in my original one and tried again and everything was back to normal.
Bluetooth
There’s a pretty decent set of materials o-line that talk to how to create bluetooth clients and servers for Java. Here’s a fairly comprehensive overview:
- http://developers.sun.com/mobility/apis/articles/bluetoothintro/index.html
You’ll notice something straight off – the article seems to mix J2SE and J2ME and not really clearly state what bluetooth support is in which platforms, how do you get working code, and how would you run these things. The answer is that these articles are generally talking about J2ME techniques and not J2SE. As far as I can tell there still is no true bluetooth support in J2SE java. You need a 3rd party library – I’ve used bluecove for this, however since I am running on Win64 and this library only runs on a 32-bit JVM I will need to use -d32 option i the VM arguments for the program to run in 32bit mode.
Bluecove has some decent example code that works, for example here is an example to find a device:
import java.io.IOException; import java.util.Vector; import javax.bluetooth.*; /** * Minimal Device Discovery example. */ public class RemoteDeviceDiscovery { public static final Vector/**/ devicesDiscovered = new Vector(); public static void main(String[] args) throws IOException, InterruptedException { final Object inquiryCompletedEvent = new Object(); devicesDiscovered.clear(); DiscoveryListener listener = new DiscoveryListener() { public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) { System.out.println("Device " + btDevice.getBluetoothAddress() + " found"); devicesDiscovered.addElement(btDevice); try { System.out.println(" name " + btDevice.getFriendlyName(false)); } catch (IOException cantGetDeviceName) { } } public void inquiryCompleted(int discType) { System.out.println("Device Inquiry completed!"); synchronized(inquiryCompletedEvent){ inquiryCompletedEvent.notifyAll(); } } public void serviceSearchCompleted(int transID, int respCode) { } public void servicesDiscovered(int transID, ServiceRecord[] servRecord) { } }; synchronized(inquiryCompletedEvent) { boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, listener); if (started) { System.out.println("wait for device inquiry to complete..."); inquiryCompletedEvent.wait(); System.out.println(devicesDiscovered.size() + " device(s) found"); } } } }
Let’s take a look at something similar in Android’s bluetooth APIs:
package com.example.android.BluetoothChat; import java.util.Set; import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.Window; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import android.widget.AdapterView.OnItemClickListener; /** * This Activity appears as a dialog. It lists any paired devices and * devices detected in the area after discovery. When a device is chosen * by the user, the MAC address of the device is sent back to the parent * Activity in the result Intent. */ public class DeviceListActivity extends Activity { // Debugging private static final String TAG = "DeviceListActivity"; private static final boolean D = true; // Return Intent extra public static String EXTRA_DEVICE_ADDRESS = "device_address"; // Member fields private BluetoothAdapter mBtAdapter; private ArrayAdapter mPairedDevicesArrayAdapter; private ArrayAdapter mNewDevicesArrayAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Setup the window requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.device_list); // Set result CANCELED incase the user backs out setResult(Activity.RESULT_CANCELED); // Initialize the button to perform device discovery Button scanButton = (Button) findViewById(R.id.button_scan); scanButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { doDiscovery(); v.setVisibility(View.GONE); } }); // Initialize array adapters. One for already paired devices and // one for newly discovered devices mPairedDevicesArrayAdapter = new ArrayAdapter(this, R.layout.device_name); mNewDevicesArrayAdapter = new ArrayAdapter(this, R.layout.device_name); // Find and set up the ListView for paired devices ListView pairedListView = (ListView) findViewById(R.id.paired_devices); pairedListView.setAdapter(mPairedDevicesArrayAdapter); pairedListView.setOnItemClickListener(mDeviceClickListener); // Find and set up the ListView for newly discovered devices ListView newDevicesListView = (ListView) findViewById(R.id.new_devices); newDevicesListView.setAdapter(mNewDevicesArrayAdapter); newDevicesListView.setOnItemClickListener(mDeviceClickListener); // Register for broadcasts when a device is discovered IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver, filter); // Register for broadcasts when discovery has finished filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); this.registerReceiver(mReceiver, filter); // Get the local Bluetooth adapter mBtAdapter = BluetoothAdapter.getDefaultAdapter(); // Get a set of currently paired devices Set pairedDevices = mBtAdapter.getBondedDevices(); // If there are paired devices, add each one to the ArrayAdapter if (pairedDevices.size() > 0) { findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); for (BluetoothDevice device : pairedDevices) { mPairedDevicesArrayAdapter.add(device.getName() + "n" + device.getAddress()); } } else { String noDevices = getResources().getText(R.string.none_paired).toString(); mPairedDevicesArrayAdapter.add(noDevices); } } @Override protected void onDestroy() { super.onDestroy(); // Make sure we're not doing discovery anymore if (mBtAdapter != null) { mBtAdapter.cancelDiscovery(); } // Unregister broadcast listeners this.unregisterReceiver(mReceiver); } /** * Start device discover with the BluetoothAdapter */ private void doDiscovery() { if (D) Log.d(TAG, "doDiscovery()"); // Indicate scanning in the title setProgressBarIndeterminateVisibility(true); setTitle(R.string.scanning); // Turn on sub-title for new devices findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE); // If we're already discovering, stop it if (mBtAdapter.isDiscovering()) { mBtAdapter.cancelDiscovery(); } // Request discover from BluetoothAdapter mBtAdapter.startDiscovery(); } // The on-click listener for all devices in the ListViews private OnItemClickListener mDeviceClickListener = new OnItemClickListener() { public void onItemClick(AdapterView av, View v, int arg2, long arg3) { // Cancel discovery because it's costly and we're about to connect mBtAdapter.cancelDiscovery(); // Get the device MAC address, which is the last 17 chars in the View String info = ((TextView) v).getText().toString(); String address = info.substring(info.length() - 17); // Create the result Intent and include the MAC address Intent intent = new Intent(); intent.putExtra(EXTRA_DEVICE_ADDRESS, address); // Set result and finish this Activity setResult(Activity.RESULT_OK, intent); finish(); } }; // The BroadcastReceiver that listens for discovered devices and // changes the title when discovery is finished private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // When discovery finds a device if (BluetoothDevice.ACTION_FOUND.equals(action)) { // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); // If it's already paired, skip it, because it's been listed already if (device.getBondState() != BluetoothDevice.BOND_BONDED) { mNewDevicesArrayAdapter.add(device.getName() + "n" + device.getAddress()); } // When discovery is finished, change the Activity title } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { setProgressBarIndeterminateVisibility(false); setTitle(R.string.select_device); if (mNewDevicesArrayAdapter.getCount() == 0) { String noDevices = getResources().getText(R.string.none_found).toString(); mNewDevicesArrayAdapter.add(noDevices); } } } }; }
Well – the first thing you’ll notice is that the code samples are pretty darn different and the first reaction might be “oh come on Google why couldn’t you use the J2ME and J2SE APIs. Well once you get beyond that first reaction the main difference I can see is that the Andorid APIs are a higher level APi that hides a lot of the underlying grunt code necessary for J2ME and J2SE bluetooth connections. The Android above is from the Chat sample – a comprehensive example of bluetooth coding.
I’m going to try a couple of things next:
- Get the chat sample working between two Android devices
- Try to get some connectivity and things working between a J2SE client and an Android client. I’ll switch to a Fedora linux client for that one I think.