android in action (part iii)
TRANSCRIPT
Android in Action (part III)
Alexey Golubev, Dmitry Lukashev
What is the plan?
Part I Part II Part III
Android UI
Layouts, ListView,
Menu, Dialog, Widgets, Tips & tricks, etc.
Android in Action
Screen rotation, Memory analyze, AIDL, SAX, Debug,
Wakelock, etc.
Java +Android (basics)
JVM, GC, Threads, SDK, NDK, Activity,
Code style, etc.
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Debugging tools
• Android Debug Bridge (ADB)
• Dalvik Debug Monitor Server (DDMS)
• Traceview
• logcat
• ADT plug-in
• DevTools
• SpareParts
• AXMLPrinter2
ADB
• Install/Uninstall apps, port forwarding, scripting, files management
• Shell
• adb shell ls /system/bin
• adb kill-server
$ adb -s emulator-5554 shell # sqlite3 /data/data/com.example.google.rss.rssexample/databases/rssitems.db SQLite version 3.3.12 Enter ".help" for instructions .... enter commands, then quit... sqlite> .exit
Monkey
• Stress-test your application: generate pseudo-random streams of user events such as clicks, touches, or gestures, as well as a number of system-level events
• You can write your own script and execute thru telnet
• You can even take screenshots thru command line
$ adb shell monkey -p your.package.name -v 500
press DPAD_CENTER
sleep 4000
tap 290 40
sleep 1000
tap 290 40
sleep 500
Monkey Demo
logcat
The priority is one of the following character values, ordered from lowest to highest priority:
V — Verbose (lowest priority)
D — Debug
I — Info
W — Warning
E — Error
F — Fatal
S — Silent (highest priority, on which nothing is ever printed)
adb logcat -b radio
DDMS
• Threads info
• VM Heap info
• Allocation Tracker
• System info
• Emulator control
• Screen capture
• File Explorer
Demo
Performance Analysis: Traceview
• In code: – Debug.startMethodTracing(“path”) // at /sdcard
– Debug.stopMethodTracing()
• Using adb: – adb shell am profile com.gtug.project start /sdcard/filename
– adb shell am profile com.gtug.project stop
• Impact performance! <!– AndroidManifest.xml //-->
<application
android:debuggable="true" ... />
Dev Tools
• Account Tester
• Development Setting – Immediately destroy activities (test
onSaveInstanceState())
– Screen updates
– Running processes
• Package Browser
• SyncTester
adb -e pull /system/app/Development.apk ./Development.apk adb -d install Development.apk
Spare Parts
• Battery history
• Usage statistics
AXMLPrinter2
• Converts Android binary XML to human-readable XML http://code.google.com/p/android4me/downloads/list
java -jar AXMLPrinter2.jar AndroidManifest.xml >> 1.txt
€ Д t 4 @ N Z x Љ в ж ш , 6 P d | љ Є в ц : v e r s i o n C o d e v e r s i o n N a m e i c o n l a b e l n a m e m i n S d k V e r s i o n a n d r o i d * h t t p : / / s c h e m a s . a n d r o i d . c o m / a p k / r e s / a n d r o i d p a c k a g e m a n i f e s t c o m . y o t a . m e r g e 1 . 0 a p p l i c a t i o n a c t i v i t y . M e r g e T e s t i n t e n t - f i l t e r a c t i o n a n d r o i d . i n t e n t . a c t i o n . M A I N c a t e g o r y a n d r o i d . i n t e n t . c a t e g o r y . L A U N C H E R u s e s - s d k Ђ яяяя ` яяяяяяяя яяяя яяяя L яяяяяяяя
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.gtug.merge"> <application android:label="@7F040001" android:icon="@7F020000"> <activity android:label="@7F040001" android:name=".MergeTest"> <intent-filter> <action android:name="android.intent.action.MAIN"> </action> <category android:name="android.intent.category.LAUNCHER"> </category> …
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Screen Orientation: Default Behavior - Recreate
Start
Stop
onCreate(Bundle) onStart()
onResume()
onRestart()
onDestroy()
onPause()
onStop()
Kill process
Running
partly visible
foreground
no longer visible
foreground
onSaveInstanceState(Bundle)
Object onRetainNonConfigurationInstance()
Screen Orientation: Dealing with recreate
• Saving state variables:
– onSaveInstanceState(Bundle)
– onRestoreInstanceState( Bundle)
• Saving Activity data
– Object onRetainNonConfigurationInstance()
– Object getLastNonConfigurationInstance()
Never save UI-dependent objects (even Adapters) using onRetainNonCofigurationInstance()!
Screen Orientation: Custom Behavior
• onConfigurationChanged(Configuration)
• android.view. OrientationEventListener
• setContentView()
<activity
android:configChanges=["mcc", "mnc",
"locale", "touchscreen", "keyboard",
"keyboardHidden", "navigation",
"orientation", "fontScale"]
android:screenOrientation=["unspecified" |
"user" | "behind" | "landscape" |
"portrait" | "sensor" | "nosensor"]
...</activity>
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Screen Orientation – The Fastest Way to Memory Leak
• References to Activity/Service/Context – Avoid final, static and long-live references – Use Application Context where possible
• References to View, Drawable, Handlers – Any kind of View from findViewById stores whole ViewRoot – Drawable callbacks
• Register/unregister BroadcastReceivers • Bind/unbind from Service
– ServiceConnection – AIDL Callbacks, RemoteCallbackList
• Inner classes – Anonymous inner classes as class fields (ClickListeners)
• Google issue #2423 (http://code.google.com/p/android/issues/detail?id=2423)
Memory Leaks demo
Memory Leaks Analysis: Debug Class
• Activity.getInstanceCount()
• Debug
– Binder transactions, dead objects, etc.
– Number of loaded classes
– setAllocationLimit(int)
– start/stopAllocationCounting()
• Debug.getMemoryInfo(MemoryInfo)
– Dalvik dirty pages
– Native dirty pages
– Other dirty pages
SysInfo -> Memory Usage
Memory Leaks Analysis: DDMS
• Stand-alone DDMS in <android-sdk>/tools – NB: Only one DDMS instance should be launched – Eclipse or stand-alone
VM Heap
Allocation Tracker
Memory Leaks Analysis: MAT
• Stand-alone Memory Analyzer (MAT)
– http://www.eclipse.org/mat/
• Get memory dump using adb shell
$ adb shell chmod 777 /data/misc $ adb shell ps $ adb shell kill -10 [pid] $ adb pull /data/misc/heap-dump-tmXXX-pidXXX.hprof name.hprof
• Convert hprof to Sun format
• Open new hprof dump in stand-alone Eclipse MAT
$ <android-sdk>/tools/hprof-conv name.hprof name4mat.hprof
Memory Analyzer demo
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Interactions with Android Service
• Context.start/stopService(Intent)
– stopSelf()
• Context.bind/unbindService()
– Interaction AIDL interface
• Permissions
– checkCallingPermission(String)
<service
android:exported=["true", "false"]
android:permission="string"
android:process="name".../>
Android Service Lifecycle
Started by startService()
Shut
down
onStart()
onDestroy()
Kill process
Running
onCreate()
stopSelf() or no
callback
onLowMemory()
Started by bindService()
onBind()
Running with
interactions
onUnbind()
onRebind()
Android Service Lifecycle Extras (SDK 5)
• onStartCommand() – Set:
• START_FLAG_REDELIVERED
• START_FLAG_RETRY
– Return: • START_STICKY_COMPATIBILITY
• START_STICKY
• START_NOT_STICKY
• START_REDELIVER_INTENT
• START_CONTINUATION_MASK
• start/stopForeground() – With Notification
Since platform 2.0!
Android IPC, RPC & AIDL
• IPC – InterProcess Communication
• RPC – Remote Procedure Calls
• AIDL – Android Interface Definition Language
Used locally (by clients of the Service)
Used remotely (by the Service)
Interface generated by the AIDL tool
Defined by the application
Defined by Android
IBinder interface
Binder class
Stub inner class
inner class used by Android
Class that implements the interface
AIDL definition
Communicate with Android Service using IPC (1)
Activity Service
ServiceConnection
onServiceConnected()
onServiceDisconnected()
bindService() onBind()
IMyInterface.aidl
IMyInterface.Stub
onDestroy()
mMyInterface = IMyInterface.Stub.asInterface(service); mMyInterface.registerCallback(cb);
RemoteCallbackList<IMyCallback> IMyCallback.aidl
mMyInterface.unregisterCallback(cb);
unbindService()
mMyInterface.unregisterCallback(cb); onUnbind()
Communicate with Android Service using IPC (2)
• AIDL types restrictions: – Primitives – String, CharSequence – List, Map – Other AIDL interfaces (import needed) – Parcelable classes (import needed)
• No Exceptions will be send back except RemoteException • All IPC calls are synchronous • Only methods, no fields
public void onCreate() { mCallbacks = new RemoteCallbackList<IMyCallback>(); } public void onDestroy() { mCallbacks.kill(); } IMyInterface.Stub { public void registerCallback(IMyCallback cb) { mCallbacks.register(cb); } public void unregisterCallback(IMyCallback cb) { mCallbacks.unregister(cb); } } private void notifyCallbacks(int data) { final int N = mCallbacks.beginBroadcast(); for (int i = 0; i < N; i++) { try { mCallbacks.getBroadcastItem(i).notify(data); } catch (RemoteException e) {} } mCallbacks.finishBroadcast(); }
Bundle, Parcel, Parcelable
Parcel
Bundle
key1 Value<int>
key2 Value<String>
key3 Value<ArrayList>
key4 Value<Parcelable>
key5 Value<Serializable>
...
Primitives
Primitive Arrays[]
Untyped Containers
writeValue(Object)
Object readValue()
Active Objects
IBinder IInterface
ParcelFileDescriptor
Parcelable
public class MyParcelable implements Parcelable {
int mData;
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() { public MyParcelable createFromParcel(Parcel in) {return new MyParcelable(in);} public MyParcelable[] newArray(int size) {return new MyParcelable[size];} };
private MyParcelable(Parcel in) {mData = in.readInt();}
@Override
public int describeContents() {return 0;} @Override
public void writeToParcel(Parcel out, int flags) {out.writeInt(mData);} }
parcelable MyParcelable;
Java
AIDL
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Databases (1)
• Create database easily with SQLiteOpenHelper
private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper() { super(VKApplication.getAppContext(), DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS " + DATABASE_TABLE_FRIENDS + " (" + KEY_FRIEND_ID + " TEXT PRIMARY KEY," + KEY_FRIEND_NAME + " TEXT, “ + KEY_FRIEND_AVATAR + " TEXT, " + KEY_FRIEND_PHONE + " TEXT, " + KEY_FRIEND_EMAIL + " TEXT);"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE_FRIENDS); onCreate(db); } }
mDb.query(DATABASE_TABLE_FRIENDS, null, null, null, null, null, null); //Cursor
Databases (2)
• If you need DB on SD – you should handle this on your own
String databasePath = Environment.getExternalStorageDirectory() + SLASH_DELIMITER + DATABASE_DIR + SLASH_DELIMITER + DATABASE_FILENAME; if (isSdMounted()) { try { mDb = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.OPEN_READWRITE | SQLiteDatabase.NO_LOCALIZED_COLLATORS); checkAndUpgradeDb(mDb); } catch (SQLiteException sqle) { File folder = new File(Environment.getExternalStorageDirectory() + SLASH_DELIMITER + DATABASE_DIR); if (!folder.exists()) { folder.mkdirs(); } mDb = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.NO_LOCALIZED_COLLATORS); db.execSQL(SQL_CREATE_DB); db.setVersion(DATABASE_VERSION); } } }
Let’s Have a Break! 10 min
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Work with XML in Android
• SAX – org.xml.sax
• Easier SAX – android.sax
• DOM – javax.xml.parsers.DocumentBuilder
• XML Pull (similar to StAX) – org.xmlpull.v1.XmlPullParser SafeSaxTest.java in Git
SAX
• Event-driven (faster parsering)
• Minimize memory footprints
• org.xml.sax.helpers.DefaultHandler
public class MyXmlHandler extends DefaultHandler{ @Override public void characters(char[] ch, int start, int length) throws SAXException {} @Override public void endElement(String uri, String localName, String name) throws SAXException {} @Override public void startDocument() throws SAXException {} @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException {} … }
http://www.saxproject.org/
Easier SAX
• android.sax - a framework that makes it easy to write efficient and robust SAX handlers
RootElement root = new RootElement("rss"); final List<Message> messages = new ArrayList<Message>(); Element channel = root.getChild("channel"); Element item = channel.getChild(ITEM); item.setEndElementListener(new EndElementListener(){ public void end() { messages.add(currentMessage.copy()); } }); item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { currentMessage.setTitle(body); } }); … try { Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler()); } catch (Exception e) {}
ElementListener EndElementListener EndTextElementListener StartElementListener TextElementListener Element RootElement
DOM
• You require access to the entire document (XSLT)
• XML validation, random access DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); List<Message> messages = new ArrayList<Message>(); try { DocumentBuilder builder = factory.newDocumentBuilder(); Document dom = builder.parse(this.getInputStream()); Element root = dom.getDocumentElement(); NodeList items = root.getElementsByTagName(ITEM); for (int i=0;i<items.getLength();i++){ Message message = new Message(); Node item = items.item(i); NodeList properties = item.getChildNodes(); for (int j=0;j<properties.getLength();j++){ Node property = properties.item(j); String name = property.getNodeName(); if (name.equalsIgnoreCase(TITLE)){ message.setTitle(property.getFirstChild().getNodeValue()); } else if (){ … } messages.add(message); } } catch (Exception e) { throw new RuntimeException(e); } javax.xml.parsers.DocumentBuilder
XML Pull (similar to StAX)
• A median between tree based and event based approaches
List<Message> messages = null; XmlPullParser parser = Xml.newPullParser(); try { // auto-detect the encoding from the stream parser.setInput(this.getInputStream(), null); int eventType = parser.getEventType(); Message currentMessage = null; boolean done = false; while (eventType != XmlPullParser.END_DOCUMENT && !done){ String name = null; switch (eventType){ case XmlPullParser.START_DOCUMENT: messages = new ArrayList<Message>(); break; case XmlPullParser.START_TAG: name = parser.getName(); if (name.equalsIgnoreCase(ITEM)){ currentMessage = new Message(); } else if (currentMessage != null){ if (name.equalsIgnoreCase(LINK)){ currentMessage.setLink(parser.nextText()); } else if (…)){ … } } break;
case XmlPullParser.END_TAG: … break; } eventType = parser.next(); } } catch (Exception e) { throw new RuntimeException(e); }
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Input Method
• Extend InputMethodService
• <intent-filter> and Settings Activity
Input View
Candidates View
TYPE_CLASS_TEXT TYPE_CLASS_PHONE
onCreate onCreateInput
View()
onCreateCandid
atesView()
onStartInputView()
onFinishInput ()
onDestroy ()
Edit text Move to the next field
Onscreen Input Modes
• EditText attributes: – android:inputType
– android:inputMethod
– android:imeOptions
– android:imeActionId
– android:imeActionLabel
• Resize modes for Activity – android:windowSoftInputMode
android:inputType="textEmailAddress"
android:inputType="phone" android:imeOptions="actionSearch"
["stateUnspecified ", "stateUnchanged", "stateHidden", "stateAlwaysHidden", "stateVisible", "stateAlwaysVisible", "adjustUnspecified“, "adjustResize", "adjustPan"]
Search in Android (1)
Search
Invoke your search UI
Type-to-search feature
Invoke global/local search box
Disable search
onSearchRequested();
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); // search within your activity setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL); // search using platform global search
@Override public boolean onSearchRequested() { return false; }
@Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); final Intent queryIntent = getIntent(); final String queryAction = queryIntent.getAction(); if (Intent.ACTION_SEARCH.equals(queryAction)) { } }
<intent-filter> <action android:name="android.intent.action.SEARCH" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
Search in Android (2)
• Global Search Box
<!-- Provides search suggestions for words and their definitions. --> <provider android:name="DictionaryProvider" android:authorities="dictionary" android:syncable="false" />
<searchable xmlns:android="http://schemas.android.com/apk/res/android" android:label="@string/search_label" android:searchSettingsDescription="@string/settings_description" android:includeInGlobalSearch="true" android:searchSuggestAuthority="dictionary" android:searchSuggestIntentAction="android.intent.action.VIEW"> </searchable>
Agenda
• Debugging hints and tools
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• Databases
• XML parsers
• Input methods, Search providers
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Coding for Battery Life
• Why does this matter? – Each device has “battery budget”. When it’s gone, it’s gone.
– Apps need to work together to be good citizens of that shared resource
1340mAh 1340mAh 1400mAh
HTC G1 HTC Magic Motorola Droid
Where does it all go?
Watching YouTube: 340mA = 3.4 hours Browsing 3G web: 225mA = 5 hours Airplane mode idle: 2mA = 24 days
Example
• RSS feed reader (phone is in idle) – App wakes up every 10 minutes to update
– Takes about 8 seconds to update, 350mA
– Cost during a given hour: • 3600 seconds * 5mA = 5mAh resting
• 6 times * 8 sec * 350mA = 4.6 mAh updating
• Just one app waking up can trigger cascade
• Bulk data transfer 6MB song from: – EDGE (90kbps): 300mA * 9.1 min = 45 mAh
– 3G (300kbps): 210mA * 2.7 min = 9.5 mAh
– Wi-Fi (1Mbps): 330mA * 48 sec = 4.4 mAh
What developer can do? (1)
• Respect user’s settings
• Check network connection – wait for 3G or Wi-Fi
NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); If(info == null ||!mConnectivityManager.getBackgroundDataSetting()){ return false; }
Subscribe to “android.net.conn.BACKGROUND_DATA_SETTING_CHANGED”
int netType = info.getType(); int netSubtype = info.getSubtype(); if (netType == ConnectivityManager.TYPE_WIFI) { return info.isConnected(); } else if (netType == ConnectivityManager.TYPE_MOBILE && netSubtype == TelephonyManager.NETWORK_TYPE_UMTS && !mTelephony.isNetworkRoaming()) { return info.isConnected(); } else { return false; }
Networking
What developer can do? (2)
• Use efficient data format and parser
Networking
What developer can do? (3)
• Be careful with Wakelocks – Wakelocks are costly
– Pick lowest level possible, use specific timeouts
– Consider using android:keepScreenOn to ensure correctness
• Use coarse network location, it’s much cheaper – GPS: 25 seconds * 140mA = 1mAh
– Network: 2 seconds * 180mA = 0.1mAh
• Float math is expensive
• Produce less garbage
Foreground apps
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag"); wl.acquire(); … wl.release();
Accelerometer sensor:
- Normal: 10mA (orientation)
- Game: 80mA
- Fastest: 90mA
GOOD Wakelock
@Override
protected void onResume() {
...
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GTUG");
mWakeLock.acquire();
...
}
@Override
protected void onPause() {
...
mWakeLock.release();
...
}
BAD Wakelock
@Override
protected void onCreate(Bundle savedInstanceState) {
...
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "GTUG");
mWakeLock.acquire();
...
}
@Override
protected void onDestroy() {
...
mWakeLock.release();
...
}
Holding wakelock in background
GOOD hardware resource handling
@Override
protected void onResume() {
...
camera = Camera.open();
...
}
@Override
protected void onPause() {
...
camera.release();
...
}
BAD hardware resource handling
@Override
protected void onCreate(Bundle savedInstanceState) {
...
camera = Camera.open();
...
}
@Override
protected void onDestroy() {
...
camera.release();
...
}
Not releasing hardware resources
Debugging
• adb bugreport >> 1.txt
– analyse “DUMP OF SERVICE power” section
– analyse “DUMP OF SERVICE media.camera” section
– …
• adb shell dumpsys battery
• adb shell dumpsys batteryinfo
• adb shell dumpsys power
What developer can do? (4)
• Services should be short-lived; these aren’t daemons
• Trigger wake-up through AlarmManager – stopSelf() when finished
• Start your service using <receiver> in manifest – Intent.ACTION_TIMEZONE_CHANGED,
– ConnectivityManager.CONNECTIVITY_ACTION
– Intent.ACTION_DEVICE_STORAGE_LOW
– Intent.ACTION_BATTERY_LOW
– Intent.ACTION_MEDIA_MOUNTED
Background apps
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(context, MyService.class); PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); long interval = DateUtils.MINUTE_IN_MILLIS * 30; long firstWake = System.currentTimeMillis() + interval; am.setRepeating(AlarmManager.RTC, firstWake, interval, pendingIntent);
Update every 30 min Don’t wake-up device
<receiver android:name=".ConnectivityReceiver" android:enabled="false"> ... </receiver>
ComponentName receiver = new ComponentName(context, ConnectivityReceiver.class); PackageManager pm = context.getPackageManager(); pm.setComponentEnabledSetting(receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
What developer can do? (5)
• Use setInexactRepeating()
• Check current battery and network state before running full update
Background apps
public void onCreate() { // Register for sticky broadcast and send default registerReceiver(mReceiver, mFilter); mHandler.sendEmptyMessageDelayed(MSG_BATT, 1000); } IntentFilter mFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); BroadcastReceiver mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { // Found sticky broadcast, so trigger update unregisterReceiver(mReceiver); mHandler.removeMessages(MSG_BATT); mHandler.obtainMessage(MSG_BATT, intent).sendToTarget(); } };
Agenda
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• XML parser
• Databases
• Input methods, Search providers
• Debugging hints and tools
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Mobile Analytics: Flurry, Motally, others
http://www.flurry.com/
http://www.motally.com/
Flurry
• The easy way to analyze your application usage
• How to: – Register, obtain application KEY
– Set permissions
– Put FlurryAgent.jar in /libs
– Make calls from code
Demo
http://www.flurry.com/
public void onStart() { super.onStart(); FlurryAgent.onStartSession(this, "Y9QZANAAAAYZP32CGI44"); }
public void onStop() { super.onStop(); FlurryAgent.onEndSession(this); }
FlurryAgent.onEvent
FlurryAgent.onError
Agenda
• Dealing with screen orientation
• Memory leaks analysis
• Working with Services
• XML parser
• Databases
• Input methods, Search providers
• Debugging hints and tools
• Battery life, wakelocks
• Flurry
• Publishing to Android Market
Signing your apps for Android Market (1)
• Debug signature vs Private key signature
• Sign with the same certificate: – Application upgrade
– Run in the same process
– Data sharing through permissions
• zipalign tool
• Eclipse export tool
Debug certificate:
Keystore name: "debug.keystore"
Keystore password: "android"
Key alias: "androiddebugkey"
Key password: "android"
CN: "CN=Android Debug,O=Android,C=US"
zipalign -v 4 your_project_name-unaligned.apk your_project_name.apk
Signing your apps for Android Market (2)
• Before publishing don’t forget: – Turnoff any Log
– Specify correct versionCode, versionName, min & target SDK versions
– Obtain Map key (http://code.google.com/android/add-ons/google-apis/mapkey.html) if you are using MapView
– Sign application with private key (a validity period ending after 22 October 2033)
Demo
http://market.android.com/publish
Android Market application on device
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(mMarketUrl));
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
// no market
}
market://details?id=<packagename>
market://search?q=pname:<package>
market://search?q=pub:<Developer Name>
market://search?q=<substring>
Devices without Android Market
• Download .apk to SD card
• Check «allow non-market applications» setting
• Run Package Installer
int result = Settings.Secure.getInt(getContentResolver(),
Settings.Secure.INSTALL_NON_MARKET_APPS, 0);
if (result == 0) {
// some dialog here
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(mLocalInstallationFileName)), “
application/vnd.android.package-archive");
startActivity(intent);
finish();
}
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APPLICATION_SETTINGS);
startActivity(intent);
Check for updates manually!
Install application from app memory
• Download .apk to the app memory getAppContext().getCacheDir().getPath()
• Use out = getAppContext().openFileOutput(mFileName, Context.MODE_WORLD_READABLE);
• Run installation File realFilePath = context.getFileStreamPath(mFileName); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(realFilePath), "application/vnd.android.package-archive"); context.startActivity(intent);
QR codes
• http://qrcode.kaywa.com/
• http://zxing.appspot.com/generator/
• http://code.google.com/p/zxing/
market://search?q=pname:com.google.zxing.client.android
ZXing Barcode Scanner
We didn’t tell you about…
• Sensor
• Location
• Media
• Testing and Instrumentation
• OpenGL
• Gestures (since SDK4)
• AccountManager (since SDK 5)
• SparseBooleanArray
• PorterDuffXfermode
• A lot more…
Future reading
• Application signing - http://developer.android.com/guide/publishing/app-signing.html
• Take screenshots through command line - http://android.amberfog.com/?p=168
• Parsers in Adroid - http://www.ibm.com/developerworks/opensource/library/x-android/index.html
• DEX decompiler - http://code.google.com/p/smali/
Recommended Blogs
• http://android-developers.blogspot.com/
• http://kohlerm.blogspot.com/ (Memory)
• http://jeremymanson.blogspot.com/ (Java)
• http://android.amberfog.com/
• http://androidandme.com/
• http://www.androidguys.com/
• http://phandroid.com/
• …
Partners
Contacts
Dmitry Lukashev
http://ru.linkedin.com/in/dmitrylukashev
Blog - http://android.amberfog.com/
Alexey Golubev
http://ru.linkedin.com/in/golubevalexey
Thank You!
Questions?
AudioTrack sample
public class AudioTrackSample implements Runnable {
private static final int sBufferSize = 8000;
private AudioTrack mTrack;
private short mBuffer[];
private short mSample;
class AudioTrackSample {
mBuffer = new short[sBufferSize];
mTrack = new AudioTrack(STREAM_MUSIC, 44100, CHANNEL_OUT_MONO,
ENCODING_PCM_16BIT, sBufferSize * 2, MODE_STREAM);
mSample = 0;
}
public void run() {
mTrack.play();
while(1) {
// fill the buffer
generateTone(mBuffer, sBufferSize);
mTrack.write(mBuffer, 0, sBufferSize);
}
}
public void generateTone(short [] data, int size) {
for (int i = 0; i < size; i++) {
pData[i] = mSample;
mSample += 600; // ~400 Hz sawtooth
}
AudioTrack JNI sample
public class JNISample implements Runnable {
private static final int sBufferSize = 8000;
private AudioTrack mTrack;
private short mBuffer[] = new short[sBufferSize];
private int mSample;
class JNISample {
mBuffer = new short[bufferSize];
mTrack = new AudioTrack(STREAM_MUSIC, 44100, CHANNEL_OUT_MONO,
ENCODING_PCM_16BIT, sBufferSize * 2, MODE_STREAM);
mSample = 0;
}
public void run() {
mTrack.play();
while(1) {
// fill the buffer
generateTone(mBuffer, sBufferSize);
mTrack.write(mBuffer, 0, sBufferSize);
}
}
static {
System.loadLibrary(“generate_tone”);
}
public native int generateTone(short [] data, int size);
}
AudioTrack JNI sample (cont.)
jint Java_com_example_jnisample_JNISample_generateTone(
JNIEnv *env, jobject thiz, jshortArray data, jint size)
{
if (size <= 0) {
return ERR_NO_DATA;
}
// convert java short array to C pointer, pinning the array in memory
pData = (short*) env->GetPrimitiveArrayCritical(data, NULL);
if (!pData) {
return ERR_NO_DATA;
}
// fill buffer with 16-bit PCM audio
short sample = 0;
for (int i = 0; i < size; i++) {
pData[i] = sample;
sample += 600; // ~400 Hz tone
}
// unpin the array
env->ReleasePrimitiveArrayCritical(data, pData, 0);
return SUCCESS;
}
AudioRecord JNI sample
AudioRecord arec = null;
try {
int buffersize = AudioRecord.getMinBufferSize(INPUT_FREQUENCY,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT) * 10;
int actualbuffersize = buffersize * 10;
arec = new AudioRecord(MediaRecorder.AudioSource.MIC, INPUT_FREQUENCY,
AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT,
actualbuffersize);
while (!stop) {
int read = arec.read(audioBuffer, frameSize);
if (stop) {
break;
}
processBuffer(read);
}
} catch ...
private native void processBuffer(int size) throws IOException;
Android 2.2 aka Froyo
Changes
New User Features - Keyboard
• Number pad and punctuation: swipe up from the keyboard to reveal two additional rows of keys.
• Change language: swipe left and right across the space bar will cycle the keyboard through any installed system languages.
New User Features – Share internet connection
• Portable Wi-Fi hotspot (can be shared with up to 8 devices)
• USB Tethering - using your phone as a modem via USB
New User Features – Improved performance
• Faster JavaScript in browser (V8 engine)
• Dalvik JIT
• Kernel Memory Management Boost (improved memory reclaim, faster app switching)
New User Features – Misc (1)
• Tips widget, fixed shortcuts to Phone and Browser
• Exchange support
• Improved Gallery and Camera
• New Android Market app: auto update/update all
• Copy/Past in E-mail application
New User Features – Misc (2)
• Drop-out menus in search
• Improved application manager
• Adobe Flash support (10.1 beta – downloadable from Android Market)
New Platform Technologies
• Media framework – New media framework (Stagefright) that supports local file
playback and HTTP progressive streaming – Continued support for OpenCore in Android 2.2
• Bluetooth – Voice dialing over Bluetooth – Ability to share contacts with other phones – Support for Bluetooth enabled car and desk docks – Improved compatibility matrix with car kits and headsets
• 2.6.32 kernel upgrade – HIGHMEM support for RAM >256MB – SDIO scheduling and BT improvements
What’s new for developer?
Alexey brings gifts to Droid at Google IO ‘09
Cloud to Device Messaging Framework
• It allows third-party application servers to send lightweight messages to their Android applications.
• C2DM makes no guarantees about delivery or the order of messages
• An application on an Android device doesn’t need to be running to receive messages
http://code.google.com/android/c2dm/
Android Application Error Reports
• Automatic crash reports
• Statistics available at Android Market Management console
android.app.admin
• Public interface for managing policies enforced on a device • <uses-policies>
<limit-password /> <watch-login /> <reset-password /> <force-lock /> <wipe-data /> </uses-policies>
• http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html
android.app.backup
• Perform backup of arbitrary data to remote storage
• Easily perform backup of SharedPreferences and files
• Restore the data saved to remote storage • http://developer.android.com/guide/topics/data/backup.html
• New bmgr tool for testing http://developer.android.com/guide/developing/tools/bmgr.html
android.os
• DropBoxManager – Enqueues chunks of data. You can think of this as a persistent, system-
wide, blob-oriented "logcat".
– DropBoxManager entries are not sent anywhere directly, but other system services and debugging tools may scan and upload entries for processing
• RecoverySystem – RecoverySystem contains methods for interacting with the Android
recovery system (the separate partition that can be used to install system updates, wipe user data, etc.)
android.app
• SearchableInfo
– Searchability meta-data for an activity. Only applications that search other applications should need to use this class
– http://developer.android.com/guide/topics/search/searchable-config.html
• UiModeManager
– This class provides access to the system uimode services
– It provides functionality to disable the car mode and it gives access to the night mode setting
android.content
• DialogInterface.OnShowListener – Interface used to allow the creator of a dialog to run some code when the dialog
is shown
• Entity and EntityIterator – A representation of a item using ContentValues. Useful in Contacts
• PeriodicSync – Value type that contains information about a periodic sync
• SyncInfo – Information about the sync operation that is currently underway
android.media
• AudioManager.OnAudioFocusChangeListener
• MediaScannerConnection.OnScanCompletedListener
• SoundPool.OnLoadCompleteListener
• CamcorderProfile – The CamcorderProfile class is used to retrieve the predefined camcorder
profile settings for camcorder applications. These settings are read-only.
• CameraProfile – The CameraProfile class is used to retrieve the pre-defined still image
capture (jpeg) quality levels (0-100) used for low, medium, and high quality settings in the Camera application.
• ThumbnailUtils – Thumbnail generation routines for media provider.
android.net and android.net.http
• SSLSessionCache – File-based cache of established SSL sessions. This is a persistent cache
which can span executions of the application
• TrafficStats – Class that provides network traffic statistics. These statistics include
bytes transmitted and received and network packets transmitted and received, over all interfaces, over the mobile interface, and on a per-UID basis.
• AndroidHttpClient – Subclass of the Apache DefaultHttpClient that is configured with
reasonable default settings and registered schemes for Android
• SslError – One or more individual SSL errors and the associated SSL certificate
android.view and android.gesture
• ScaleGestureDetector
• ScaleGestureDetector.OnScaleGestureListener
• ScaleGestureDetector.SimpleOnScaleGestureListener
• GestureUtils – Utility functions for gesture processing & analysis
– feature extraction (e.g., samplers and those for calculating bounding boxes and gesture path lengths);
– geometric transformation (e.g., translation, rotation and scaling);
– gesture similarity comparison (e.g., calculating Euclidean or Cosine distances between two gestures).
android.graphics and android.opengl
• ImageFormat
• YuvImage
• New APIs for OpenGL ES 2.0, working with YUV image format, and ETC1 for texture compression. – ETC1
– ETC1Util
– ETC1Util.ETC1Texture
– GLES20
android.widget
• HeterogeneousExpandableList – Different view types for group items
– getGroupType(int groupPosition)
– getGroupTypeCount()
– getChildTypeCount()
– getChildType(int groupPosition, int childPosition)
android.speech
• RecognitionListener – Used for receiving notifications from the SpeechRecognizer when the
recognition related events occu
• RecognitionService and RecognitionService.Callback – This class provides a base class for recognition service implementations
– This class should be extended only in case you wish to implement a new speech recognizer
• SpeechRecognizer – This service allows access to the speech recognizer
Other
android.hardware Camera.OnZoomChangeListener android.webkit ConsoleMessage ConsoleMessage.MessageLevel WebSettings.PluginState android.test.mock MockContentProvider MockCursor android.text.style LeadingMarginSpan.LeadingMarginSpan2
android.util Base64 Base64InputStream Base64OutputStream EventLog EventLog.Event Patterns
Thank You!