android, the life of your app

Post on 12-May-2015

269 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

DESCRIPTION

You can find the slides with speaker notes here : http://bit.ly/lifeofapp During this talk we live the life of your app on the user's point of view. The idea is to follow the user experience from the Play Store to the daily use, measure each time its frustration to find ways for us, as developers, to avoid them.

TRANSCRIPT

THE LIFE OF YOUR APP

Eyal LEZMY

http://eyal.fr

SLIDES http://bit.ly/lifeofapp

AGENDACODEURS EN SEINE

01

Install it

02

Launch it

03

Look at it

04

Use it

IT ALL STARTS ON THE PLAY STORE

01

Request only what your app requires

1/3 of apps request more permissions than they need

MINIMISE PERMISSIONS

Users should prefer apps

requesting the least

permissions

You don’t need permission

Use ContentProviders

MINIMISE PERMISSIONS

Users should prefer apps

requesting the least

permissions

Permission are not required to launch another activity that has the permission

MINIMISE PERMISSIONS

Need a contact?

MINIMISE PERMISSIONS

Use the force, Luke

MINIMISE PERMISSIONS

MINIMISE PERMISSIONS

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);} }

}}

MINIMISE PERMISSIONS

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType(Phone.CONTENT_ITEM_TYPE);startActivityForResult(intent, MY_REQUEST_CODE);

Start the contact app

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);} }

}}

MINIMISE PERMISSIONS

Intent intent = new Intent(Intent.ACTION_GET_CONTENT);intent.setType(Phone.CONTENT_ITEM_TYPE);startActivityForResult(intent, MY_REQUEST_CODE);

Start the contact app

Handle the result

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);} }

}}

Need an UUID? TelephonyManager.getDeviceId()

Requires READ_PHONE_STATE permission

MINIMISE PERMISSIONS

Settings.Secure.ANDROID_IDReset at every wipeNot applicable on multi user environment

Need an UUID? TelephonyManager.getDeviceId()

Requires READ_PHONE_STATE permission

MINIMISE PERMISSIONS

Settings.Secure.ANDROID_IDReset at every wipeNot applicable on multi user environment

NO!

Need an UUID? Generate your own UUID and use

Backup API !

MINIMISE PERMISSIONS

String id = UUID.randomUUID().toString();

Need an UUID? Generate your own UUID and use

Backup API !

MINIMISE PERMISSIONS

String id = UUID.randomUUID().toString();

YES!

Android Backup API

· API is available on all Android devices. · Manufacturors can implements their own transport and storage for the API

· Each device as its own backup data

· A new device will take a backup from a device associated with your google account.

· IT'S NOT A SYNC API !

MINIMISE PERMISSIONS

THE FIRST LAUNCH02

Make a good

impression

THE FIRST LAUNCH

Lets start!

THE FIRST LAUNCH

http://cyrilmottier.com

THE FIRST LAUNCH

<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.MyTheme" ... >

We define our personal theme including the correct styles and colors

Let’s start...Again!

THE FIRST LAUNCH

My Big

BRAND

LOADING ...

A bunch of data to insert? Use SQL transactions to save

time!

THE FIRST LAUNCH

THE FIRST LAUNCH

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();

}

Start transaction

THE FIRST LAUNCH

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();

}

Start transaction

End transaction

THE FIRST LAUNCH

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();

}

Start transaction

End transaction Optimise multi-threaded insertion

Import directly, ready to use databases

Faster?

THE FIRST LAUNCH

openDatabase(dbPath, null,SQLiteDatabase.NO_LOCALIZED_COLLATORS | SQLiteDatabase.CREATE_IF_NECESSARY);

· Create your db file using SQlite and fill it

· Name your primary key columns "_id"

· Create the table : "android_metadata"

· And insert a single row containing the local if defined (ex: "en_US"), or open your database using :

THE FIRST LAUNCH

ON THE SERVER

· Grab the zipped database from assets or network

· Unzip it to your getDatabaseDir()

· Open the database

THE FIRST LAUNCH

ON THE MOBILE

Be careful of the SQLite version !

LOOK AND FEEL03

? ? ?

LOOK AND FEEL

HOTMAIL OUTLOOK.COM

LOOK AND FEEL

HOTMAIL OUTLOOK.COM

SAME!

LOOK AND FEEL

FOLLOW THE GUIDELINES!http://d.android.com/design

Redesigned by Taylor Ling

LOOK AND FEEL

By Microsoft

LOOK AND FEEL

LOOK AND FEEL

LOOK AND FEEL

LOOK AND FEEL

FOLLOW THE GUIDELINES!http://d.android.com/design

LOOK AND FEEL

FOLLOW THE GUIDELINES!http://d.android.com/design

PLEASE!

A DAILY USE04

UI Thread =

Events+Draw 16 ms to draw a frame (~60 fps)

SMOOTHEN YOUR UI

Garbage Collector may take 10ms And stop all threads

Flatten the View

Hierarchy· Use RelativeLayout instead of LinearLayout

· Use the <merge/> tag when possible

· Use hierarchyviewer to inspect your layouts

SMOOTHEN YOUR UI

Avoid overdraws · Do not draw your background several times

· Use the “GPU Overdraw” tool from Android 4.2

SMOOTHEN YOUR UI

Use SparseArray

SMOOTHEN YOUR UI

Avoid autoboxing

SMOOTHEN YOUR UI

Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);

SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);

SMOOTHEN YOUR UI

Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);

new Integer(1789)

SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);

SMOOTHEN YOUR UI

Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);

new Integer(1789)new Integer(1789)

SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);

SMOOTHEN YOUR UI

Hashmap<Integer, Object> hashmap = new HashMap<Integer, Object>();hashmap.put(1789, mRevolution);...hashmap.get(1789);

new Integer(1789)new Integer(1789)

Low memory footprint and no more GC!

SparseArray<Object> sparseArray = new SparseArray<Object>();sparseArray.put(1789, mRevolution);...sparseArray.get(1789);

· Reuse it

· Use sampling

SMOOTHEN YOUR UI

Load bitmapcleverly

SMOOTHEN YOUR UI

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;

Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;

SMOOTHEN YOUR UI

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;

Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;

Define the bitmap to reuse (API level 11)

SMOOTHEN YOUR UI

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;

Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;

Use the option for loadingDefine the bitmap to reuse (API level 11)

SMOOTHEN YOUR UI

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inBitmap = myOldBitmap;

Bitmap scaledBitmap = BitmapFactory.decode...(..., bitmapOptions);

Define the subsampling level (API Level 1)

BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = sampleSize;

Use the option for loadingDefine the bitmap to reuse (API level 11)

PRESERVE THE BATTERY

Screen: 1st item of consumtion30 to 70% of the battery life

PRESERVE THE BATTERY

Basically you don’t need itOnly a few kind of applications should need to stay the device awake (Reader, Games, …)

Think about the contextRelease wake lock if you have good assumptions that the user is not using your app anymore

WakeLocks

PRESERVE THE BATTERY

GPS· Permission ACCESS_FINE_LOCATION · Drains a lot of power· Works offline

Network location· Permission ACCESS_COARSE_LOCATION· Need to be online· Fast· Precise in urban area

Geolocation

PRESERVE THE BATTERY

Define a strategy· What is the needed precision for my app?· Define the measure interval wisely· Consider the GPS fix time

Use Fused Location ProviderOn Google Play Services

Geolocation

PRESERVE THE BATTERY

Radio drains a lot of powerGroup data to minimize the number of requests

Use caching!

Network

PRESERVE THE BATTERY

PRESERVE THE BATTERY

private void enableHttpResponseCache() { try {

long httpCacheSize = 10 * 1024 * 1024; // 10 MiB

File httpCacheDir = new File(getCacheDir(), “http”);

Class.forName(“android.net.http.HttpResponseCache”)

.getMethod(“install”, File.class, long.class)

.invoke(null, httpCacheDir, httpCacheSize);

} catch (Exception httpResponseCacheNotAvailable) {

Log.d(TAG, “HTTP response cache is unavailable.”);

}

}

Enable cache, if available (API level 13)Or use a backport like HttpResponseCache

Enable GZIP on the server30 to 50% less trafic

Use ETAGs

Network

PRESERVE THE BATTERY

PRESERVE THE BATTERY

BE INSPIRED

Gestures Text to Speech Accelerometer

Voice recognition Proximity sensor Bluetooth

NFC Direct Wifi Second Screen

BE INSPIRED

Explore all the device’s possibilities

Thank You for your time !

http://eyal.fr

SLIDEShttp://bit.ly/lifeofapp

top related