The Android winds of change From Kit-Kat to L, and the power of saving power
Why are you here? Info on the new IDE, and setting up projects! Want to know the changes L brings to your Kit-Kat apps! How to analyse your app s performance
Android Java based! Approximately annual upgrade! Previous version: Kit-Kat! Based on Holo design! Next version: Something starting with L! Based on Material design
The app: Pokedex Master/Detail view of Pokemon! Originally designed for Android Kit-Kat
The migration Change the structure of the app! Style it! Improve performance
The IDE & project setup
A comparison Used Eclipse with ADT plugin to develop! Ant to build, especially via CI! Manual dependency management Build Tool Dependency Management Updates Eclipse with ADT Android Studio Ant Manual via lib imports Gradle Automatic via Gradle ADT plugin not actively being updated Constantly being updated with new IDE features
What do I need to change? <manifest xmlns:android="http://schemas.android.com/apk/res/android">!!! <uses-sdk! android:minsdkversion="15"! android:targetsdkversion="18" />!!! </manifest>
What do I need to change? android {! compilesdkversion 'android-l'! buildtoolsversion '20.0.0'! defaultconfig {! targetsdkversion 'L'! }! } dependencies {! compile 'com.android.support:support-v4:+'! compile 'com.android.support:cardview-v7:+'! compile 'com.android.support:recyclerview-v7:+'! compile 'com.android.support:palette-v7:+'! }
New components
RecyclerView More advanced and flexible than ListView! Forces the ViewHolder pattern! Recycling process is more efficient! Requires an external LayoutManager
RecyclerView in action <android.support.v7.widget.recyclerview xmlns:android="http://schemas.android.com/apk/res/android" android:id= @+id/my_recycler_view">! </android.support.v7.widget.recyclerview>
RecyclerView in action protected void oncreate(bundle savedinstancestate) { // Set the content view RecyclerView recyclerview =!! (RecyclerView)findViewById(R.id.my_recycler_view);! RecyclerView.LayoutManager layoutmanager = " " " " new LinearLayoutManager(this); recyclerview.setlayoutmanager(layoutmanager);! } // Setup adapter
ViewHolder pattern? View scrolls off screen View is held off screen Scroll RecyclerView up View is recycled when needed avoiding the need to recreate the View, thus improving app performance
Implementing the ViewHolder pattern Create a RecyclerView.ViewHolder 1 Bind the data to the element s view in onbindviewholder() 4 2 Create the RecyclerView.Adapter to use the ViewHolder 3 Inflate the element s view in RecyclerView.Adapter s oncreateviewholder()
Implementing the ViewHolder pattern Create a RecyclerView.ViewHolder 1 Bind the data to the element s view in onbindviewholder() 4 2 Create the RecyclerView.Adapter to use the ViewHolder 3 Inflate the element s view in RecyclerView.Adapter s oncreateviewholder()
Implementing the ViewHolder pattern Create a RecyclerView.ViewHolder 1 Bind the data to the element s view in onbindviewholder() 4 2 Create the RecyclerView.Adapter to use the ViewHolder 3 Inflate the element s view in RecyclerView.Adapter s oncreateviewholder()
Implementing the ViewHolder pattern Create a RecyclerView.ViewHolder 1 Bind the data to the element s view in onbindviewholder() 4 2 Create the RecyclerView.Adapter to use the ViewHolder 3 Inflate the element s view in RecyclerView.Adapter s oncreateviewholder()
How does this look? public class PokedexHolder extends RecyclerView.ViewHolder {! public CardView card;! public ImageView image;! public TextView text;!! public PokedexHolder(View v) {! super(v);! card = (CardView) v.findviewbyid(r.id.card_view);! image = (ImageView) v.findviewbyid(r.id.img_image);! text = (TextView) v.findviewbyid(r.id.txt_name);! }! }
How does this look? public class PokemonArrayAdapter!! extends RecyclerView.Adapter<PokedexHolder>!! implements View.OnClickListener {! @Override! public PokedexHolder oncreateviewholder(viewgroup viewgroup, int pos) {! View v = LayoutInflater.from(viewGroup.getContext()) " " ".inflate(r.layout.row_pokemon, viewgroup, false);" PokedexHolder holder = new PokedexHolder(v);" holder.card.setonclicklistener(this); } return holder;! }!!
How does this look? pubic class PokemonAdapter {!! public void onbindviewholder( " " PokedexHolder pokedexholder, int position) {!! Pokemon p = getpokemonfromlist(position);! pokedexholder.image.setimagedrawable(!! context.getresources().getdrawable(p.getimage()));! } }! pokedexholder.text.settext(p.getname());!
How does this look? protected void oncreate(bundle savedinstancestate) {!!!! PokemonArrayAdapter adapter =!!! new PokemonArrayAdapter(this, items);!!! recyclerview.setadapter(adapter);! }
CardView Material design > Paper > CardView! Mobile, Tablet, Wearables(Glass, Watch), TV, Auto! Rounded corners! Elevations / Shadows
CardView <android.support.v7.widget.cardview xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:android="http://schemas.android.com/apk/res/android" card_view:cardcornerradius="4dp"> <!-- Insert card layout here --> </android.support.v7.widget.cardview>!
Progress
Material design: What is it?
What is Material design? More paper look and feel! To provide a way to the user to relate to elements on devices using:! cards! shadows! elevations
How does it differ from Kit-Kat? Bright bold colours vs splashes of colour! Motion to indicate actions have been performed! Custom palettes! Elevations and shadows
Themes Themes aren t new! Base theme: Theme.material! Dark, Light, Light with DarkActionBar
Colours Wider gamut of colours! Combinations of primary and secondary colours
Palettes & named colours Custom palettes! Named colours
Changing the Pokedex theme
Changing the Pokedex theme <resources>! <!-- Base application theme. -->! <style name= AppTheme" parent="android:theme.material.light">! <item name="android:colorprimary">@color/red</item>! <item name="android:textcolorprimary">@color/white</item>! <item name="android:colorprimarydark">@color/dark_red</item>! <item name="android:windowbackground">@color/green</item>! <item name="android:navigationbarcolor">@color/red</item>! </style>! </resources>
Adding custom colours public void onbindviewholder(...) { Palette palette = Palette.generate(image);! if (palette.getdarkmutedcolor()!= null) {! holder.text.setbackgroundcolor(!!! palette.getdarkmutedcolor().getrgb()); } if (palette.getvibrantcolor()!= null) {! holder.text.settextcolor(!!! palette.getvibrantcolor().getrgb()); } }
Visually complete!
But the battery is draining quickly
But the battery is draining quickly We need to fix this before it goes to the Play Store!
Battery Performance
Project Volta Performance improvements in Android platform! Tools to analyse application efficiency! Increase user awareness of power consumption
Project Volta Waking a device for 1 sec = 2 mins of standby time lost
Battery stats adb shell dumpsys batterystats --charged <package-name>
Battery Historian Python script used to create an HTML visualisation of the data obtained from batterystats command Battery Stats Battery Historian HTML Visualisation
What does the output look like?
What can we infer from this graph?
What can we infer from this graph?
Pokedex Performance
Power Management Strategies
Being lazy is good Make apps Lazy first! Reduce number of time app is active! Coalesce actions together! Defer actions to a later time! eg: when charging
Consider this What is the longest time I am willing to wait to complete this task?
JobScheduler Allows you to schedule jobs to occur at a later time.
What is a job? A job is a non user-facing network call eg:! CPU intensive operations you d prefer when the user isn t around! Some job you need to perform periodically, or in the future
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"!
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"! JobInfo uploadjob =! new JobInfo.Builder(mJobId, mservicecomponent)!.setrequirednetworkcapabilities(jobinfo.networktype.any)!.setperiodic(15 * DateUtils.HOURS_IN_MILLIS)!.setRequiresCharging(true)!.build();
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"! JobInfo uploadjob =! new JobInfo.Builder(mJobId, mservicecomponent)!.setrequirednetworkcapabilities(jobinfo.networktype.any)!.setperiodic(15 * DateUtils.HOURS_IN_MILLIS)!.setRequiresCharging(true) Job ID!.build();
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"! JobInfo uploadjob =! new JobInfo.Builder(mJobId, mservicecomponent)!.setrequirednetworkcapabilities(jobinfo.networktype.any)!.setperiodic(15 * DateUtils.HOURS_IN_MILLIS)!.setRequiresCharging(true) Job Endpoint!.build();
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"! JobInfo uploadjob =! new JobInfo.Builder(mJobId, mservicecomponent)!.setrequirednetworkcapabilities(jobinfo.networktype.any)!.setperiodic(15 * DateUtils.HOURS_IN_MILLIS)!.setRequiresCharging(true)!.build(); Network Type
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"! JobInfo uploadjob =! new JobInfo.Builder(mJobId, mservicecomponent)!.setrequirednetworkcapabilities(jobinfo.networktype.any)!.setperiodic(15 * DateUtils.HOURS_IN_MILLIS)!.setRequiresCharging(true)!.build(); Periodically recur
Create a job example #1 Every fifteen hours, perform some database clean-up and upload some logs to the server"! JobInfo uploadjob =! new JobInfo.Builder(mJobId, mservicecomponent)!.setrequirednetworkcapabilities(jobinfo.networktype.any)!.setperiodic(15 * DateUtils.HOURS_IN_MILLIS)!.setRequiresCharging(true)!.build(); Charging constraint
Schedule Criteria Network activity aware! Idle mode! Run task while charging! Metered/unmetered
Schedule features Persistence! Retry / back-off! One-time / periodic
Create a job example #2 Schedule a job to run five seconds from now, but before fifteen minutes have passed"!
Create a job example #2 Schedule a job to run five seconds from now, but before fifteen minutes have passed"! JobInfo job = new JobInfo.Builder(mJobId + 1, mservicecomponent)!.setminimumlatency(5 * DateUtils.SECONDS_IN_MILLIS)!.setOverrideDeadline(15 * DateUtils.MINUTES_IN_MILLIS)!.build();
Create a job example #2 Schedule a job to run five seconds from now, but before fifteen minutes have passed"! JobInfo job = new JobInfo.Builder(mJobId + 1, mservicecomponent)!.setminimumlatency(5 * DateUtils.SECONDS_IN_MILLIS)!.setOverrideDeadline(15 * DateUtils.MINUTES_IN_MILLIS)!.build(); Job ID
Create a job example #2 Schedule a job to run five seconds from now, but before fifteen minutes have passed"! JobInfo job = new JobInfo.Builder(mJobId + 1, mservicecomponent)!.setminimumlatency(5 * DateUtils.SECONDS_IN_MILLIS)!.setOverrideDeadline(15 * DateUtils.MINUTES_IN_MILLIS)!.build(); Job Endpoint
Create a job example #2 Schedule a job to run five seconds from now, but before fifteen minutes have passed"! JobInfo job = new JobInfo.Builder(mJobId + 1, mservicecomponent)!.setminimumlatency(5 * DateUtils.SECONDS_IN_MILLIS)!.setOverrideDeadline(15 * DateUtils.MINUTES_IN_MILLIS)!.build(); Start Delay
Create a job example #2 Schedule a job to run five seconds from now, but before fifteen minutes have passed"! JobInfo job = new JobInfo.Builder(mJobId + 1, mservicecomponent)!.setminimumlatency(5 * DateUtils.SECONDS_IN_MILLIS)!.setOverrideDeadline(15 * DateUtils.MINUTES_IN_MILLIS)!.build(); Deadline
Creating an endpoint System Service! onstartjob() System calls the onstartjob() of your service!!
Creating an endpoint System Service! onstartjob() { } System calls the onstartjob() of your service!!
Creating an endpoint System Service! onstartjob() { } System calls the onstartjob() of your service! Perform logic!
Creating an endpoint System Service! onstartjob() { jobfinished(); } System calls the onstartjob() of your service! Perform logic! Call jobfinished() when job is complete
Creating an endpoint System Service! onstartjob() { jobfinished(); } System calls the onstartjob() of your service! Perform logic! Call jobfinished() when job is complete
Criteria has changed and not valid System Service! onstartjob()
Criteria has changed and not valid System Service! onstartjob() onstopjob() System calls the onstopjob()!!
Criteria has changed and not valid System Service! onstartjob() onstopjob() System calls the onstopjob()!!
Criteria has changed and not valid System Service! onstopjob() {! } System calls the onstopjob()!!
Criteria has changed and not valid System Service! onstopjob() {! } System calls the onstopjob()! Perform logic!
Criteria has changed and not valid System Service! onstopjob() { jobfinished(); } System calls the onstopjob()! Perform logic! Call jobfinished() when job is complete
Criteria has changed and not valid System Service! onstopjob() { jobfinished(); } System calls the onstopjob()! Perform logic! Call jobfinished() when job is complete
Pokedex Performance Before After
Battery Historian http://github.com/google/battery-historian
What just happened Changed structure of app! New IDE! RecyclerView, CardView! Applied colour! Themes! Custom colour palettes! Improved performance! JobScheduler! Battery Historian
Where to from here? Download latest Android Studio with Android L-Preview API! Read the Google docs on Material and Android L-Preview! Start coding!!!
Final thoughts
Questions? www.outware.com.au! OMPodcast