Running Android Apps Using External Storage (SD Card)

Hidden in the depths of the Android docs is an important chapter about info you need to know for running your apps off an external storage card. Here’s the link if you are a developer where you can use Manifest file settings so that anytime your app is installed it will be enabled for use on your SD Card: https://developer.android.com/guide/appendix/install-location.html . If you aren’t a developer then I can’t help you much because these settings require access to either the application source code, or the Android Debug Bridge.

You can also enable your apps for external storage from the adb command line using adb shell pm setInstallLocation 2. See the screenshot below. Once you’ve done that then use the adb install /mydir/some.apk to push the app to the phone. If the app isn’t automatically pushed to the card, then you’ll have to go into your droids application manager, select your application name, and then select “Move to SD”.

If you move the app to the external card you can find it using the built-in Android file browser under /mnt/asec/…

Here’s a quick link to Android dev docs on writing to internal and external storage. And, should you so desire to create a database on an external card you would point the SQLiteDatabase.openDatabase method to the appropriate directory path. I’ll point out that external storage is inherently insecure and you wouldn’t want to store passwords or anything important on it.

I’ll also add that not all storage cards are equal and some are much, much slower than others. Do your research, especially if you have a large application and you want snappy startup or read/write performance.

Presenting at the Esri Developer Summit

I’ll be presenting at the Esri Developer Summit this week (March 7 – 9, 2011) . So, if you are at the conference in Palm Springs, California stop by and say “hi”. If you aren’t familiar with this conference, it is the largest geo-developer conference in North America with over 1200 geo-geeks basking in all manner of technical geographic goodness. There will be 63 technical sessions and around 29 sessions presented by non-Esri, ArcGIS developers. What better way to learn than to hear it straight from the developers on the front lines…right??!

I have three sessions: a pre-conference session on Getting Started with the ArcGIS API for Android (beta), Localizing the ArcGIS Viewer for Flex, and integrated Volunteered Geographic Information and Social Media into your GIS. A GIS, is a Geographic Information System, for all you non-geo-geeks. Hope to see you there!

Check Network Connectivity in Your Android App

Our team does alot of application demos, and a vast majority of them require an internet connection. My challenge is that most of my development droid phones only have wireless (e.g. 802.11g), and I need a graceful way to detect if an internet connection exists. Without some sort of connection-based error detection, my mapping app would simply have a few buttons and a grey screen with no clue as to what the problem might be. So, here’s one way to to alert your users that the network connection is down. This was built using Android 2.2 on Eclipse 3.5.

Step 1 – Create a new Class that takes advantages of the capabilities in the ConnectivityManager class. That way you can reuse this across multiple projects. Be sure to import the appropriate references, and change the package name to the correct path in your project:

  
package com.esri.arcgis.android.samples;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

public class CheckConnectivity{
	ConnectivityManager connectivityManager;
	NetworkInfo wifiInfo, mobileInfo;

	/**
	 * Check for <code>TYPE_WIFI</code> and <code>TYPE_MOBILE</code> connection using <code>isConnected()</code>
     * Checks for generic Exceptions and writes them to logcat as <code>CheckConnectivity Exception</code>. 
     * Make sure AndroidManifest.xml has appropriate permissions.
	 * @param con Application context
	 * @return Boolean
	 */
	public Boolean checkNow(Context con){
		
		try{
			connectivityManager = (ConnectivityManager) con.getSystemService(Context.CONNECTIVITY_SERVICE);
			wifiInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
			mobileInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);	
			
			if(wifiInfo.isConnected() || mobileInfo.isConnected())
			{
				return true;
			}
		}
		catch(Exception e){
			System.out.println("CheckConnectivity Exception: " + e.getMessage());
		}
		
		return false;
	}	
}

Step 2 – Create an instance of that Class, and import the new Class you just created. Be sure to place your check for connectivity before any code that requires an internet connection. You can now use the Boolean result to decide what actions you want to take. I added the System.out.println to print the results into the logcat file. You can access logcat by installing Android Debug Bridge (ADB) and then typing “adb logcat” at a DOS command prompt. On a related note, you should also install Android Debug Tools (ADT) Eclipse Plug-in.

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

     CheckConnectivity check = new CheckConnectivity();
     Boolean conn = check.checkNow(this.getApplicationContext());
     if(conn == true){
          //run your normal code path here
     }
     else{
          //Send a warning message to the user
          connectivityMessage("Check Network Connection."); }
}

public void connectivityMessage(String msg){
     Context context = getApplicationContext();
     Toast toast = Toast.makeText(context, "", Toast.LENGTH_LONG);
     toast.setGravity(Gravity.CENTER, 0, 0);
     toast.setText(msg);
     toast.show();
}

Step 3 – I did initially run into a problem where the application threw a strange “Source not found” error on a system thread. It say strange because there was absolutely no useful information. I knew the application ran just fine without my ConnectionCheck Class, so I used the step-thru debugger to narrow things down to my checkNow() method. I placed that in a basic try/catch block and walla there was the error with the solution:

I/System.out(  269): CheckConnectivity: ConnectivityService: Neither user 10032 nor current process has
 android.permission.ACCESS_NETWORK_STATE.
I/System.out(  269): CONNECTION CHECK: false

So, I went and added that permission line into the AndroidManifest.xml file and my app worked:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0" package="com.esri.arcgis.android.samples">
  	<uses-permission android:name="android.permission.INTERNET"/>
  	<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application android:icon="@drawable/icon" android:label="@string/app_name" android:debuggable ="true">
        <activity android:name=".DrawGraphicElements"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-sdk android:minSdkVersion="8" />
</manifest>

Here’s what the warning message will look like if you don’t have an internet connection: