Wifi: pUp3EkaP
> 2000 members Largest Android Active Community
Jonathan Yarkoni
Android Developer & Advocate
Ironsource
Android Academy Staff
Yonatan Levin
Android Google Developer
Expert
Britt Barak
Android LeadFigure8
Yossi Segev
Android DeveloperColu
Shahar Avigezer
Android DeveloperHello Heart
Community Mentors
What Do We Do?
● Android Fundamentals - NOW
● Android UI / UX - 29/1 !
● Community Hackathon - 9/3 !!!
● Android Performance
● Mentors Program● Active community
+RanNachmany@shed2k
From good to awesome
01Install
01Install
02Launch
Start!
01Install
02Launch
03Look & Feel
Start!
01Install
02Launch
03Look & Feel
04Use
Start!
Step 1: InstallFirst touch point.
Minimize Permissions• Request only core functionality blockers.
Minimize Permissions• Request only core functionality blockers.• You don't need permission to launch another App that has
the permission
Minimize Permissions• Request only core functionality blockers.• You don't need permission to launch another App that has
the permission
Need a contact?
Use the force, Luke
Asking for contact
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType(Phone.CONTENT_ITEM_TYPE);startActivityForResult(intent, MY_REQUEST_CODE);
void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data != null) { Uri uri = data.getData(); if (uri != null) { Cursor c = getContentResolver().query(uri, new String[] {Contacts.DISPLAY_NAME,
Phone.NUMBER}, null,null, null) }
}}
Asking for contact
Need a UUID?
Option 1: TelephonyManager.getDeviceIdRequires READ_PHONE_STATE.
Option 2: Settings.Secure.ANDROID_IDReset at wipeFollows the device, not the user.
Use the force, Luke
Generate UUIDUse backup API
Step 2: First Launch“ok, let’s see what this app is all about”
My BigBRAND
LOADING ...
A bunch of data to insert?
SQLTransactions speeds things up
db.beginTransaction();
try{ for(int i=0; i<selectedIds.length; i++){ values.put(COLUMN_ID,selectedIds[i]); values.put(COLUMN_STARRED,starred); db.insert(TABLE_STARRED,null,values); db.yieldIfContendedSafely(); } db.setTransactionSuccessful();}finally { db.endTransaction();}
Bulk DB update
Not fast enough?
Server Side● Create a db file using SQLite and
fill it.● Name your primary key columns
“_id”● Create a table:
“android_metadata”● Insert a single row containing the
locale (ex: "en_US")
Import ready to use DB
Mobile Side● Grab the zipped DB from assets
or network.● Unzip it to your getDatabaseDir()● Use as usual
Import ready to use DB
SQLite version
Step 3: Look & Feel“Where is the share button?”
HOTMAIL
OUTLOOK.COM
HOTMAIL OUTLOOK.COM
Follow the guidelines
LOOK AND FEEL
LOOK AND FEEL
LOOK AND FEEL
LOOK AND FEEL
LOOK AND FEEL
LOOK AND FEEL
Follow the guidelines
Please
Step 4: Daily Use“I really like it, but...”
Always up to dateUsers don't like to wait. • Location updated.• Data downloaded from web.
Always up to dateUsers don't like to wait. • Location updated.• Data downloaded from web.
Find the location fast.• Loop through all providers->getLastKnownLoc.
• If there is one or more location which is recent enough – return the most accurate one
• If not – Return the latest one.• In case of #2 – look for “fastest” provider:
• Coarse Accuracy && low power consumption. • Register for location update.
Use passive location• >Gingerbread. • Passive location – Receive location update when other app is using
location provider. • Requires ACCESS_FINE_LOCATION permission.• Location.getProvider().
Fused Location• Define your priorities • Let Google play services do the heavy lifting.
// Create an instance of GoogleAPIClient.if (mGoogleApiClient == null) { mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build();}
Using fused location
@Overridepublic void onConnected(Bundle connectionHint) { //get last known location mLastLocation = LocationServices.FusedLocationApi.getLastLocation( mGoogleApiClient); if (mLastLocation != null) { //do something }
//register for location updates LocationRequest request = LocationRequest.create(); request.setInterval(minTime); request.setPriority(lowPowerMoreImportantThanAccuracy ? LocationRequest.PRIORITY.BALANCED_POWER_ACCURACY : LocationRequest.PRIORITY_HIGH_ACCURACY ); LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, request, this);}
Using fused location
@Overridepublic void onLocationChanged(Location location) { mCurrentLocation = location; mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); updateUI();}
Using fused location
Data updates
The challenges:• When to update?• Where the data is coming from?• What to update?• How to update?
When to update my App?• Right before the user launches it
• Assuming I have battery.• Assuming I have BW.
Where the data is coming from?• My Server• 3rd Party Servers
Where the data is coming from?• My Server• 3rd Party Servers
What to Update? • What's Hot• Interesting stuff• Other
BG Update
What's Hot
BG Update
Interesting Stuff Other
BG Update
What's Hot
BG Update
Interesting Stuff Other
FCM Embedded
BG Update
What's Hot
BG Update
Interesting Stuff Other
FCM Embedded
FCM + Back-off
BG Update
What's Hot
BG Update
Interesting Stuff Other
FCM Embedded
FCM + Back-off
Daily / Bundled
Being Invisible• I don't need to think how the app works.• I never notice the app's work.• I am never being bothered by the app.
Work OfflineQueue and Send transactions.• Use persistence layer.• Tape From Square.
Work Semi-Offline• Be resilient to poor networks.
• Prioritize your transactions.• Be able to cancel transaction on the fly, or clear the Queue.• Adjust your app’s behavior and timeouts accordingly.
• Use Volley (or any other cool transport layer)
Be efficient – Data usage
Radio Resource• Frequency is expensive. • Cell tower can not service 100% of its clients 100% of the time. • Frequency is dynamically allocated to clients.
• Cellular cells uses various multiplexing methods (OFDM/OFDMA, FTDMA).
UMTS RRC States. (source: 3gpp)
Radio State Machine
IDLE
FACH
DCH
Power
Bandwidth
Radio State Machine
IDLE
FACH
DCH
Power
~10Sec tail time
~12-75 Sec tail time
Bandwidth
Avoid bursty traffic• Transmit data “together”.
• Piggyback if needed.• Pre-fetch data for the next 2-5 minutes.
• Don't ping just to keep TCP connection alive• RRC != TCP Connection.• TCP connection is kept even in IDLE mode
#1: Case study: Pandora• Music file streamed as single file.• Analytics data sends ~2KB every 62.5 seconds
Source: AT&T research
#1: Case study: Pandora
0.2% of data consumed 46% of energy!
Don't be HTTP rookie• Don't download what you already have.• Take care of server headers
• Max-age, expires. • Use conditional GET when cache expires
• Use “last modified” header.• Server return 304, with no body.
Don't be lazy• Read AT&T research:
• Top Radio Resources Issues in Mobile Applications• AT&T Lab Research – call for more efficient apps
• Watch my BABBQ talk - Building battery efficient apps • (or ask yonathan to invite me once more ;-) )
• Use ARO.• Developed by AT&T.• Monitors and analyze network activity. • http://developer.att.com/• https://github.com/attdevsupport/ARO
Adaptive App• Optimized for different User Experience.
• User has more than one device. • Be predictable.• Behave as expected
Text Input• Specify the Edit Text input to show the right keyboard type.
• use android:inputType attribute• Four classes of keyboards:
• Plain text• Decimal Number• Phone Number• Date or Time
Text Input• Plain text types:
• URIs• Email address• People's names• Postal address• Passwords
mGeofenceList.add(new Geofence.Builder() // Set the request ID of the geofence. This is a string to identify this // geofence. .setRequestId(entry.getKey())
.setCircularRegion( entry.getValue().latitude, entry.getValue().longitude, Constants.GEOFENCE_RADIUS_IN_METERS ) .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS) .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT) .build());
Geofencing
private GeofencingRequest getGeofencingRequest() { GeofencingRequest.Builder builder = new GeofencingRequest.Builder(); builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER); builder.addGeofences(mGeofenceList); return builder.build();}
Geofencing
private PendingIntent getGeofencePendingIntent() { // Reuse the PendingIntent if we already have it. if (mGeofencePendingIntent != null) { return mGeofencePendingIntent; } Intent intent = new Intent(this, GeofenceTransitionsIntentService.class); // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when // calling addGeofences() and removeGeofences(). return PendingIntent.getService(this, 0, intent, PendingIntent. FLAG_UPDATE_CURRENT);}
Geofencing
LocationServices.GeofencingApi.addGeofences( mGoogleApiClient, getGeofencingRequest(), getGeofencePendingIntent() ).setResultCallback(this);
Geofencing
private void initTextToSpeech() { Intent intent = new Intent(Engine.ACTION_CHECK_TTS_DATA); startActivityForResult(intent,TTS_DATA_CHECK)}
Geek Magic - Text to speech
private void onActivityResult(int request, int result, Intent data) { if (TTS_DATA_CHECK == request && Engine.CHECK_VOICE_DATA_PASS == result) { tts = new TextToSpeech(this, new on InitListener() { public void onInit(int status) { if (TextToSpeech == status) { ttsIsInit = true; } }
});}else { startActivity(new Intent(Engine.ACTION_INSTALL_TTS_DATA));}
}
Geek Magic - Text to speech
private void say(Strint text) { if (null != tts && ttsIsInit) { tts.speak(text,TextToSpeech.QUEUE_ADD, null); }}
Geek Magic - Text to speech
private void requestVoiceInput() { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.String.voice_input_prompt); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH); startActivityForResult(intent,VOICE_RECOGNITION);}
Geek Magic - Speech Recognition
private void requestVoiceInput() { Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.String.voice_input_prompt); intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH); startActivityForResult(intent,VOICE_RECOGNITION);}
Geek Magic - Speech Recognition
Geek Magic – Speech Recognition• More than one result is returned.• Loop through all results, taking context in mind.
<intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <data android:host="mysite.com" android:scheme="http"/></intent-filter>
Intercept Links
Follow the guidelines
Be FreshBe InvisibleBe EfficientBe AdaptiveBe Psychic
Make it quick
Start!
minimize permissions
Thank You!+RanNachmany@shed2k
Recommended