The Android winds of change - YOW! Conferences · RecyclerView in action protected void...

Post on 27-Jul-2020

2 views 0 download

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