droidcon berlin barcamp 2016
TRANSCRIPT
Android Technical Lead
Applause Inc.
Przemek Jakubczyk
pjakubczyk
@pjakubczyk
1
A guide to make crashproof libraries
It's always your fault
2
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
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
How your SDK should look like
Be universal
Work in every provided configuration
Be robust
Work in any environment
Be defensive
5
GRADLE
6
Just use it.
7
because it’s the base for Android build system
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"}
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
Gradle
10
Not possible :)
Often heard question. How to pass arguments to tasks?
Create new task for each configuration.
Gradle
11
task buildAll ( dependsOn:
["assembleFreeRelease, assemblePaidRelease"])
Java
12
Java
catching Exception doesn’t solve problem
often hides real cause
tempting but dangerous
13
try { network.getClients();} catch (Exception e) { // handle exception}
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”)
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>(); }}
Java
Usually library is started by one static method
… and next versions provide more functionality
Init interface becomes complex
16
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);
Java
Builder pattern organize configuration
Easier data validation
Pass only parameters user wants
Handling default values
18
Examples
Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .build();
19
Stetho.newInitializerBuilder(context) .enableWebKitInspector(myInspector) .build()
Concurrency
New Thread is your enemy
20
ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r, "NetworkExecutor"); }});
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
Android
22
Android
View.inEditMode() determines if view is drawn in Android Studio
Usually to disable other components
Let’s invert the usage
23
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);}
}}
Android
Build.VERSION.SDK_INT
ApplicationInfo.targetSdkVersion
the library doesn’t know where it’s run
25
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
Android
Wrap OS Exceptions with your own implementation
BitmapFactory
Throws unchecked OutOfMemoryError
27
28
public static class BitmapCreatorException extends Exception { public BitmapCreatorException(String detailMessage) { super(detailMessage); }
public BitmapCreatorException(Throwable throwable) { super(throwable); } }
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
30
Bitmap nullCheck(Bitmap bitmap) throws BitmapCreatorException { if (bitmap == null) throw new BitmapCreatorException("Bitmap is empty"); else return bitmap;}
Bitmap Provider
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
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
The End
33
License
34
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)
Thank you
36
A guide to make crashproof libraries
It's always your fault
37