Google I/O talk notes.
Dealing with various APIs and Android versions.
Coding patterns.
How to deal with various device capabilities.
private static boolean isNewAPI =
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent;
if (isNewAPI) {
intent = new Intent(this, ModernActivity.class);
} else {
intent = new Intent(this, LegacyActivity.class);
}
startActivity(intent);
finish();
}
Sensor registration example (Orientation Sensor).
private static boolean isNewSensorAPI =
android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.CUPCAKE;
boolean gyroExists = getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_GYROSCOPE);
IOrientationSensorListener myListener;
if (gyroExists) {
myListener = new GyroOrientetationSensorListener();
} else if (isNewSensorAPI) {
myListener = new AccOrientationSensorListener();
} else {
myListener = newAccOldOrientationSensorListener();
}
myListener.setOrientationChangeListener(myOCListener);
Getting users feedback
try {
} catch (Exception ex) {
String exText = "something went wrong";
MyApplication.getInstance().tracker().trackPageView(exText);
}
A – B user testing on Beta deployment. Getting real users feedback on different possible scenarios/visuals/etc.
private static final boolean isA =
UUID.randomUUID().getLeastSiginifcantBits() % 2 == 0;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(isA) {
setContentView(R.layout.mainA);
MyApp.getInstance().tracker().trackPageView("/AUser");
} else {
setContentView(R.layout.mainB);
MyApp.getInstance().tracker().trackPageView("/BUser");
}
}
Using Android Market for beta testing:
- private betas that require login or passcode
- public betas with disguised or obscured listing
- update listing or create new on launch
- point beta users to official app at launch
- be clear that this is beta. venues for feedback
- make sure you translated your app name and description to local languages
Other hints:
- upload your package before distributing it to anyone
- backup your keystore
- use generic gmail account for app releases (sharing the responsibilities)
Sensor orientation tip
int x = AXIS_X;
int y = AXIS_Y;
case (Display.getRotation()):
Surface.ROTATION_0: break;
Surface.ROTATION_90: x = AXIS_Y; y = AXIS_MINUS_X; break;
Surface.ROTATION_180: y = AXIS_MINUS_Y; break;
Surface.ROTATION_270: x = AXIS_MINUS_Y; y = AXIS_X; break;
default: break;
}
SensorManager.remapCoordinateSystem(inR, x_axis, y_axis, outR);
Generating unique identifier (per user/device). Use the following method of generating the unique id and store it in users’s preferences.
UUID. randomUUID().toString().
How to get the up-to-date location without draining the battery?
Use passive location provider, or, alternatively, use Location change intent.
final int resultCode = 0;
final String locAction = "com.ioApp.LOCATION_UPDATE_RECEIVED";
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
Intent intent = new Intent(locAction);
PendingIntent pi = PendingIntent.getBroadcast(this, resultCode, intent, flags);
locationManager.requestLocationUpdates(provider, minTime, minDistance, pi);
....
BroadcastReceiver locReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent intent) {
String key = LocationManager.KEY_LOCATION_CHANGED;
Location location = (Location)intent.getExtras().get(key);
// process new location
}
};
IntentFilter locIntentFilter = new IntentFilter(locAction);
registerReceiver(locReceiver, locIntentFilter);
To use intents to passively detect location changes start a service to refresh the data without updating a UI.
receiver android:name=".locReceiver" android:enabled="true"
intent-filter>
action android:name="com.ioApp.LOCATION_UPDATE_RECEIVED"/>
/intent-filter>
/receiver
Wake alarms and non-waking alarms example:
int wake = AlarmManager.ELAPSED_REALTIME_WAKEUP;
int sleep = AlarmManager.ELAPSED_REALTIME;
long minInt = AlarmManager.INTERVAL_HALF_DAY;
long bestInt = AlarmManager.INTERVAL_HALF_HOUR;
long trigger = SystemClock.elapsedRealtime() + bestInt;
alarms.setInexactRepeating(wake, trigger, minInt, alarmIntent);
alarms.setInexactRepeating(sleep, trigger, bestInt, alarmIntent);
Make your data updates smart by monitoring number of elements. For example:
monitor connectivity
Connectivity Manager cm = (ConnectivityManager)ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNet = cm.getActiveNetworkiInfo();
boolean isConnected = activeNet.isConnectedOrConnecting();
boolean isMobile = activeNet.getType() ==
ConnectivityManager.TYPE_MOBILE;
monitor power and battery
IntentFilter bf = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent bat = ctx.registerReceiver(null, bF);
int bstat = bat.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean power = bstat == BatteryManager.BATTERY_STATUS_CHARGING ||
bstat == BatteryManager.BATTERY_STATUS_FULL;
monitor docking status and other possible state-change broadcasts to monitor:
ACTION_DOCK_EVENT
ACTION_BATTERY_LOW
ACTION_POWER_CONNECTED
ACTION_POWER_DISCONNECTED
CONNECTIVITY_CHANGED
Cloud-based Android Backup service.
Backing up shared preferences
public class MyPrefsBackupAgent extends BackupAgentHelper {
static final String PREFS = "user_prefs";
static final String PREFS_BACKUP_KEY = "prefs";
@Override
public void onCreate() {
SharedPreferencesBackupHelper helper =
new SharedPreferencesBackupHelper(this, PREFS);
addHelper(PREFS_BACKUP_KEY, helper);
}
}
in Manifest
application android:label="MyApp" android:backupAgent="MyPrefsBackupAgent">
meta-data android:name="com.google.android.backup.api_key"
android:value="MyAPIKey" />
/application>
Making your apps more adaptive. Use appropriate keyboard for EditText entry UI.
eg, in EditText ....
android:inputType="phone"
android:imeOptions="actionSend | flagNoEnterAction"
/>
Listen for those special action keys in your OnEditorActionListener:
EditText.OnEditorActionListener myActionListener =
new () {
@Override
public boolean onEditorAction(EditText v,
int actionId,
KeyEvent event) {
if (actionId == EditorInfor.IME_ACTION_SEND) {
// handle the SEND action
return true;
}
return false;
}
};
editText.setOnEditorActionListener(myActionListener);
Handle Audio nicely (interoperate nicely with audio focus).
Make everything asynchronous and make use of background threads (Handler, AsyncTask, IntentService, AsyncQueryHandler, Loader and CursorLoader).
CursorLoader example
// within onCreate...
getLoaderManager(),initLoader(0, null, null);
// Callbacks
public Loader onCreateLoader(int id, Bundle args) {
Uri baseUri = MyContentProvider.CONTENT_URI;
return new CursorLoader(getActivity(), baseUri, null, null, null, null);
}
public void onLoadFinished(Loader loader, Cursor data) {
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader loader) {
mAdapter.swapCursor(null);
}
Testing/Debugging. Use Strict Mode:
public void onCreate() {
if (DEVELOPER_MODE) {
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyFlashScreen()
.build());
}
super.onCreate();
}