Download - Droidcon Berlin Barcamp 2016

Transcript
Page 1: Droidcon Berlin Barcamp 2016

Android Technical Lead

Applause Inc.

Przemek Jakubczyk

pjakubczyk

@pjakubczyk

1

Page 2: Droidcon Berlin Barcamp 2016

A guide to make crashproof libraries

It's always your fault

2

Page 3: Droidcon Berlin Barcamp 2016

Background

At Applause I am responsible for quality

Applause SDK is crash and bug reporting library

shipped to over 1000 customers

SDK works with apps around the world

3

Page 4: Droidcon Berlin Barcamp 2016

History

Joined 2 years ago to project with no QA

Today ~2200 unit tests covering 3k methods

82% lines covered

Last major problem was support for Marshmallow

4

Other than that over 6 months of no customer complain

Page 5: Droidcon Berlin Barcamp 2016

How your SDK should look like

Be universal

Work in every provided configuration

Be robust

Work in any environment

Be defensive

5

Page 6: Droidcon Berlin Barcamp 2016

GRADLE

6

Page 7: Droidcon Berlin Barcamp 2016

Just use it.

7

because it’s the base for Android build system

Page 8: Droidcon Berlin Barcamp 2016

Gradle

use simple tricks to protect your styling

8

android {

resourcePrefix 'applause_'

}

or pass custom values to source code via DSL

defaultConfig {

resValue "string","applause_library_version”, "$version"}

Page 9: Droidcon Berlin Barcamp 2016

Gradle

easier integration with your Groovy scripts

for example create own distribution task

task buildAll (dependsOn: 'assembleRelease', type: Copy) {

from “build/outputs/”into “another_path”

}

9

Page 10: Droidcon Berlin Barcamp 2016

Gradle

10

Not possible :)

Often heard question. How to pass arguments to tasks?

Page 11: Droidcon Berlin Barcamp 2016

Create new task for each configuration.

Gradle

11

task buildAll ( dependsOn:

["assembleFreeRelease, assemblePaidRelease"])

Page 12: Droidcon Berlin Barcamp 2016

Java

12

Page 13: Droidcon Berlin Barcamp 2016

Java

catching Exception doesn’t solve problem

often hides real cause

tempting but dangerous

13

try { network.getClients();} catch (Exception e) { // handle exception}

Page 14: Droidcon Berlin Barcamp 2016

Java

Null Object Pattern

instead constantly checking for not null value

14

public interface Api { void post(String action); Api NULL = new Api() {

void post(String action){} };}

getApi().post(“Works”)

Page 15: Droidcon Berlin Barcamp 2016

Java

NPE

Null Pointer Exception is the most popular exception thrown in runtime.

NullObject pattern partially solves the problem.

Use empty objects, collections etc;

15

List<User> fetchUsers(){ try {

return api.getAllUsers(); } catch (IOException e){

return new ArrayList<Users>(); }}

Page 16: Droidcon Berlin Barcamp 2016

Java

Usually library is started by one static method

… and next versions provide more functionality

Init interface becomes complex

16

Page 17: Droidcon Berlin Barcamp 2016

Java

public static void start(String baseUrl,String defaultUser,String defaultPassword,boolean cacheRequests,boolean forceHttps,int timeout)

17

Conf conf = new Conf.Builder().withUrl(“http://api.github.com”).withUser(“pjakubczyk”).withPassword(“droidcon2016Berlin”).withCache(false).withHttps(true).withTimeout(15).build();

Library.start(conf);

Page 18: Droidcon Berlin Barcamp 2016

Java

Builder pattern organize configuration

Easier data validation

Pass only parameters user wants

Handling default values

18

Page 19: Droidcon Berlin Barcamp 2016

Examples

Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build();

19

Stetho.newInitializerBuilder(context) .enableWebKitInspector(myInspector) .build()

Page 20: Droidcon Berlin Barcamp 2016

Concurrency

New Thread is your enemy

20

ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "NetworkExecutor"); }});

Page 21: Droidcon Berlin Barcamp 2016

The String

String is a bad information holder

Often passing 2 string parameters to method

Lead to simple mistakes

User vs Person

Instead of getters maybe method ‘serializeForAuth’ ?

21

Page 22: Droidcon Berlin Barcamp 2016

Android

22

Page 23: Droidcon Berlin Barcamp 2016

Android

View.inEditMode() determines if view is drawn in Android Studio

Usually to disable other components

Let’s invert the usage

23

Page 24: Droidcon Berlin Barcamp 2016

24

public void onFinishInflate(){TextView title = findViewById(R.id.title);if(inEditMode()) {

int count = title.getText().length();if(count > 30){

title.setTextSize(24.0f);} else {title.setTextSize(30.0f);}

}}

Page 25: Droidcon Berlin Barcamp 2016

Android

Build.VERSION.SDK_INT

ApplicationInfo.targetSdkVersion

the library doesn’t know where it’s run

25

Page 26: Droidcon Berlin Barcamp 2016

Android

usually interface for loading pictures from to web to Widget looks like this:

pictureLoader.load(“url_to_resource”, imageView);

passing arguments extending from View, Activity etc.

often lead to Memory leak

queue is flooded with requests holding all references

Go back to javadoc and check java.lang.ref.WeakReference<T> :)

26

Page 27: Droidcon Berlin Barcamp 2016

Android

Wrap OS Exceptions with your own implementation

BitmapFactory

Throws unchecked OutOfMemoryError

27

Page 28: Droidcon Berlin Barcamp 2016

28

public static class BitmapCreatorException extends Exception { public BitmapCreatorException(String detailMessage) { super(detailMessage); }

public BitmapCreatorException(Throwable throwable) { super(throwable); } }

Page 29: Droidcon Berlin Barcamp 2016

29

Bitmap createScaled(Bitmap bitmap, int width, int height) throws BitmapCreatorException { try { Bitmap scaledBitmap =

Bitmap.createScaledBitmap(bitmap, width, height, true); return nullCheck(scaledBitmap); } catch (OutOfMemoryError error) { throw new BitmapCreatorException(error); }}

Bitmap Provider

Page 30: Droidcon Berlin Barcamp 2016

30

Bitmap nullCheck(Bitmap bitmap) throws BitmapCreatorException { if (bitmap == null) throw new BitmapCreatorException("Bitmap is empty"); else return bitmap;}

Bitmap Provider

Page 31: Droidcon Berlin Barcamp 2016

Android

ProGuard is outstanding tool to shrink code and inject bytecode optimizations

While shipping your code you must either provide:

copy&paste configuration to ProGuard (latest plugin supports auto-configuration)

be transparent to ProGuard.

Configuration vs Transparency?

Transparency!31

Page 32: Droidcon Berlin Barcamp 2016

Android http://www.methodscount.com/

32

Product Method count

com.squareup.okhttp3:okhttp:3.0.1 2704

io.relayr:android-sdk:1.0.2 5413

io.reactivex:rxjava:1.1.0 4605

com.google.code.gson:gson:2. 1341

com.applause:applause-sdk:3.4.0 5041

com.fasterxml.jackson.core:jackson-databind:2.7.0 10732

com.parse:parse-android:1.13.0 4543

Page 33: Droidcon Berlin Barcamp 2016

The End

33

Page 34: Droidcon Berlin Barcamp 2016

License

34

Page 35: Droidcon Berlin Barcamp 2016

Licence

35

by default you own the copyright

no licence doesn’t conclue you can use it in your project

open code (found online) != open source movement

transferring code goes along with transferring the ownership

Check the licence

Check if it infects yours

(Apache, MIT vs GPL v2, LGPL, BSD)

Page 36: Droidcon Berlin Barcamp 2016

Thank you

36

Page 37: Droidcon Berlin Barcamp 2016

A guide to make crashproof libraries

It's always your fault

37