The Android winds of change - YOW! Conferences · RecyclerView in action protected void...
Transcript of The Android winds of change - YOW! Conferences · RecyclerView in action protected void...
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
Ant Manual via lib imports
ADT plugin not actively being updated
Android Studio
Gradle Automatic via Gradle
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?
Scroll RecyclerView up
View scrolls off screen View is held off screen
View is recycled when needed avoiding the need to recreate the View, thus improving app performance
Implementing the ViewHolder pattern
4Bind the data to the
element’s view in onBindViewHolder()
3
Inflate the element’s view in RecyclerView.Adapter’s onCreateViewHolder()
2
Create a RecyclerView.ViewHolder
Create the RecyclerView.Adapter to use the ViewHolder
1
Implementing the ViewHolder pattern
4Bind the data to the
element’s view in onBindViewHolder()
3
Inflate the element’s view in RecyclerView.Adapter’s onCreateViewHolder()
2
Create a RecyclerView.ViewHolder
Create the RecyclerView.Adapter to use the ViewHolder
1
Implementing the ViewHolder pattern
4Bind the data to the
element’s view in onBindViewHolder()
3
Inflate the element’s view in RecyclerView.Adapter’s onCreateViewHolder()
2
Create a RecyclerView.ViewHolder
Create the RecyclerView.Adapter to use the ViewHolder
1
Implementing the ViewHolder pattern
4Bind the data to the
element’s view in onBindViewHolder()
3
Inflate the element’s view in RecyclerView.Adapter’s onCreateViewHolder()
2
Create a RecyclerView.ViewHolder
Create the RecyclerView.Adapter to use the ViewHolder
1
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)! .build();
Job ID
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();
Job Endpoint
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 calls the onStartJob() of your service!!
System
Service !!!!!onStartJob()
Creating an endpoint
• System calls the onStartJob() of your service!!
System
Service !!!!!onStartJob() { }
Creating an endpoint
• System calls the onStartJob() of your service!• Perform logic!
System
Service !!!!!onStartJob() { … }
Creating an endpoint
• System calls the onStartJob() of your service!• Perform logic!• Call jobFinished() when job is complete
System
Service !!!!!onStartJob() { … jobFinished(); }
Creating an endpoint
• System calls the onStartJob() of your service!• Perform logic!• Call jobFinished() when job is complete
System
Service !!!!!onStartJob() { … jobFinished(); }
Criteria has changed and not valid
System
Service !!!!!onStartJob()
Criteria has changed and not valid
• System calls the onStopJob()!!
System
Service !!!!!onStartJob()
onStopJob()
Criteria has changed and not valid
• System calls the onStopJob()!!
System
Service !!!!!onStartJob()
onStopJob()
Criteria has changed and not valid
• System calls the onStopJob()!!
System
Service !!!!!onStopJob() { !}
Criteria has changed and not valid
• System calls the onStopJob()!• Perform logic!
System
Service !!!!!onStopJob() { … !}
Criteria has changed and not valid
• System calls the onStopJob()!• Perform logic!• Call jobFinished() when job is complete
System
Service !!!!!onStopJob() { … jobFinished(); }
Criteria has changed and not valid
• System calls the onStopJob()!• Perform logic!• Call jobFinished() when job is complete
System
Service !!!!!onStopJob() { … jobFinished(); }
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