android and the seven dwarfs from devox'15

Post on 16-Apr-2017

944 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Snow White and the Seven Dwarfs

Murat Yener @yenerm

Android

who am I?-Java, Android, web -Android Dev @Intel -Conference Speaker (JavaOne, Devoxx…) -GDG Istanbul Organizer -Book Author -Java Champion -GDE on Android

40% discount with promo

code

VBK43 when ordering

through wiley.com

valid until end of December 2015

Once upon a time…

the princess…well, the Android

had some problems after eating an… apple

while waiting for the prince charming…

can Seven Dwarfs help? Butterknife

Dagger

Volley / OkHttp / Retrofit

GreenBus / Otto

GreenDAO / Schematic

Priority JobQueue

Timber / Hugo

Butterknife

Dependency Injection for views and actions

Cleaner code

Simple annotation based usage

Use Nullable to avoid exceptions

Based on compile time code generation

compile ‘com.jakewharton:butterknife:7.0.1’

class ExampleActivity extends Activity {

@Bind(R.id.title) TextView title;

@Bind(R.id.subtitle) TextView subtitle;

@Bind(R.id.footer) TextView footer;

@Override public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.simple_activity);

ButterKnife.bind(this);

// TODO Use fields...

}

}

@Nullable @Bind(R.id.might_not_be_there) TextView mightNotBeThere;

@Nullable @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {

// TODO ...

}

//Simple listener injection

@OnClick(R.id.submit)

public void submit(View view) {

// TODO submit data to server...

}

//Multi listener

@OnClick({ R.id.door1, R.id.door2, R.id.door3 })

public void pickDoor(DoorView door) {

if (door.hasPrizeBehind()) {

Toast.makeText(this, "You win!", LENGTH_SHORT).show();

} else {

Toast.makeText(this, "Try again", LENGTH_SHORT).show();

}

}

ProGuard Configuration

-keep class butterknife.** { *; }

-dontwarn butterknife.internal.**

-keep class **$$ViewInjector { *; }

-keepclasseswithmembernames class * {

@butterknife.* <fields>;

}

-keepclasseswithmembernames class * {

@butterknife.* <methods>;

}

DaggerFast dependency injection

Standard javax.inject (JSR 330)

Make your code easy to test

Compile time code generation (No reflection)

Complicated

Hard to do for ongoing project

compile 'com.google.dagger:dagger:2.0' apt 'com.google.dagger:dagger-compiler:2.0'

@Module + @Provides: mechanism for providing dependencies.

@Inject: mechanism for requesting dependencies.

@Component: bridge between modules and injections

class CoffeeMaker { @Inject Heater heater; @Inject Pump pump; ...} @Moduleclass DripCoffeeModule { @Provides Heater provideHeater() { return new ElectricHeater(); } @Provides Pump providePump(Thermosiphon pump) { return pump; }}

@Component(modules = DripCoffeeModule.class) interface CoffeeShop { CoffeeMaker maker();}

public @interface Component { Class<?>[] modules() default {}; Class<?>[] dependencies() default {};}

public @interface Module { Class<?>[] includes() default { };} public @interface Provides { }

public @interface MapKey { boolean unwrapValue();}

public interface Lazy<T> { T get();}

ProGuard Configuration

NONE

OkHttp

Widely used, simple fix for HttpClient

Internal cache

HTTP2 and SPDY support

Uses GZIP

Manual handling of background execution

compile 'com.squareup.okhttp:okhttp:2.5.0'

//ClientOkHttpClient client = new OkHttpClient(); //Get URLString run(String url) throws IOException { Request request = new Request.Builder() .url(url) .build(); Response response = client.newCall(request).execute(); return response.body().string(); } //Post URL public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); String post(String url, String json) throws IOException { RequestBody body = RequestBody.create(JSON, json); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string(); }

ProGuard Configuration

-keepattributes Signature

-keepattributes *Annotation*

-keep class com.squareup.okhttp.** { *; }

-keep interface com.squareup.okhttp.** { *; }

-dontwarn com.squareup.okhttp.**

Volley

Simplest HttpClient fix for Android

Internal Queue

Internal Cache

Runs in a separate thread

Not good for large downloads

compile ‘com.mcxiaoke.volley:library-aar:1/0/0’

RequestQueue queue = Volley.newRequestQueue(this); StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() { @Override public void onResponse(String response) { //.. } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { //.. } }); queue.add(stringRequest);

//Cancel requests queue.cancelAll(TAG);

ProGuard Configuration

NONE

RetrofitREST Client for Java

Generates an implementation of the API

Uses annotation to describe URLs, query params

Object conversion to JSON request body

Multipart request body and file upload

Only good for REST APIs

compile 'com.squareup.retrofit:retrofit:1.9.0'

public interface GitHubService { @Headers("Cache-Control: max-age=640000") @GET("/users/{user}/repos") List<Repo> listRepos(@Path("user") String user); @POST("/users/new") void createUser(@Body User user, Callback<User> cb);}

RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint("https://api.github.com") .build();GitHubService service = restAdapter.create(GitHubService.class);

List<Repo> repos = service.listRepos("octocat");

-keep class com.squareup.okhttp.** { *; }-keep interface com.squareup.okhttp.** { *; }-dontwarn com.squareup.okhttp.**

-dontwarn rx.**-dontwarn retrofit.**-dontwarn okio.**-keep class retrofit.** { *; }

-keepclasseswithmembers class * { @retrofit.http.* <methods>;}

GreenRobot EventBus

Dispatches events through a bus

Event Driven

Very easy and clean implementation

Easily transfer data between components

Uses reflection on runtime

compile 'de.greenrobot:eventbus:2.4.0'

//Custom Event Object

public class SampleEvent {

private String message;

public SampleEvent(String message){

this.message=message;

}

}

//Caller

//geteventbus

eventBus.post(new SampleEvent(“An event”);

//callee

//geteventbus

eventBus.register(this);

public void onEvent(SampleEvent event) {

textField.setText(event.getMessage());

};

ProGuard Configuration

-keepclassmembers class ** {

public void onEvent*(**);

}

# Only required if you use AsyncExecutor

-keepclassmembers class * extends

de.greenrobot.event.util.ThrowableFailureEvent {

public <init>(java.lang.Throwable);

}

# Don't warn for missing support classes

-dontwarn de.greenrobot.event.util.*$Support

-dontwarn de.greenrobot.event.util.*$SupportManagerFragment

Otto

Event bus

decouple different parts of your application

Communication between components

Uses reflection

compile 'com.squareup:otto:1.3.8'

Bus bus = new Bus();bus.post(new AnswerAvailableEvent(42));bus.register(this);

@Subscribepublic void answerAvailable(AnswerAvailableEvent event) { // TODO: React to the event somehow!}

-keepclassmembers class ** {

@com.squareup.otto.Subscribe public *;

@com.squareup.otto.Produce public *;

}

GreenDao

from the creators of GreenRobot EventBus

Standard SqLite

No create table… etc

Uses code generation for model and dao

compile 'de.greenrobot:greendao'

new DaoMaster.DevOpenHelper(this, "notes-db", null);

daoMaster = new DaoMaster(db);daoSession = daoMaster.newSession();noteDao = daoSession.getNoteDao();

Note note = new Note(null, noteText, comment, new Date());noteDao.insert(note);

-keepclassmembers class * extends de.greenrobot.dao.AbstractDao {

public static java.lang.String TABLENAME;

}

-keep class **$Properties

SchematicAutomatically generate ContentProviders

Backed by SQLite Database

Can be used w/ Android System

Loaders, SyncAdapter, Permissions

Harder to use (deal w/ Cursors)

apt ‘net.simonvt.schematic:schematic-compiler:0.6.0’ compile ‘net.simonvt.schematic:schematic:0.6.0’

public interface ListColumns { @DataType(INTEGER) @PrimaryKey @AutoIncrement String _ID = "_id"; @DataType(TEXT) @NotNull String TITLE = "title"; } @Database(version = NotesDatabase.VERSION) public final class NotesDatabase { public static final int VERSION = 1; @Table(ListColumns.class) public static final String LISTS = "lists"; } @ContentProvider(authority = NotesProvider.AUTHORITY, database = NotesDatabase.class) public final class NotesProvider { public static final String AUTHORITY = "net.simonvt.schematic.sample.NotesProvider"; @TableEndpoint(table = NotesDatabase.LISTS) public static class Lists { @ContentUri( path = Path.LISTS, type = "vnd.android.cursor.dir/list", defaultSort = ListColumns.TITLE + " ASC") public static final Uri LISTS = Uri.parse("content://" + AUTHORITY + "/lists") }}

ProGuard Configuration

NONE

Priority JobQueue

Persistent queue for scheduling jobs

Easy to prioritize

Delay job execution

Group jobs for batch execution

Not really needed above 5.0

compile 'com.path:android-priority-jobqueue:1.1.2'

public class PostTweetJob extends Job { public static final int PRIORITY = 1; public PostTweetJob(String text) { super(new Params(PRIORITY).requireNetwork().persist()); } @Override public void onAdded() {} @Override public void onRun() throws Throwable { webservice.postTweet(text); } @Override protected boolean shouldReRunOnThrowable(Throwable throwable) { }}

ProGuard Configuration

NONE

TimberLogger with a small, extensible API

Default behavior: Nothing

Behavior is added through Tree instances.

Install instance by calling Timber.plant()

DebugTree: output logs for debug builds and auto tag generation

compile 'com.jakewharton.timber:timber:4.1.0'

/** A tree which logs important information for crash reporting. */private static class CrashReportingTree extends Timber.Tree { @Override protected void log(int priority, String tag, String message, Throwable t) { if (priority == Log.VERBOSE || priority == Log.DEBUG) { return; } FakeCrashLibrary.log(priority, tag, message); if (t != null) { if (priority == Log.ERROR) { FakeCrashLibrary.logError(t); } else if (priority == Log.WARN) { FakeCrashLibrary.logWarning(t); } } }}

public void greetingClicked(Button button) { Timber.i("A button with ID %s was clicked.", button.getId());

//Do stuff}

ProGuard Configuration

NONE

Hugo

Annotation-triggered method call logging

Generates logging code

Zero effect on non-debug builds.

see next slide

buildscript { repositories { mavenCentral() } dependencies { classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1' } } apply plugin: 'com.android.application'apply plugin: 'com.jakewharton.hugo'

@DebugLogpublic String getName(String first, String last) { SystemClock.sleep(15); // Don't ever really do this! return first + " " + last;}

V/Example: ⇢ getName(first="Jake", last="Wharton") V/Example: ⇠ getName [16ms] = "Jake Wharton"

ProGuard Configuration

NONE

BONUSThe Prince Charming: Lambdas on Android

wait, Java 8?!?

Android 5.0 and above use Java 7

but not invokeDynamic

so no Lambdas…

Retrolambda

lambda expressions

method references

try-with-resources statements

limited support for backporting default methods and static methods on interfaces

buildscript { repositories { mavenCentral() }

dependencies { classpath 'me.tatarka:gradle-retrolambda:3.2.3' } }

// Required because retrolambda is on maven central repositories { mavenCentral() }

apply plugin: 'com.android.application' //or apply plugin: 'java' apply plugin: ‘me.tatarka.retrolambda'

OR

plugins { id "me.tatarka.retrolambda" version "3.2.3" }

PROGUARD -dontwarn java.lang.invoke.*

Android Studio build.gradle

android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }

Libraries fix things…

Use them only when you have a (exprected) problem…

and don’t overuse them…

Conclusion…

@yenerm murat@muratyener.com

-the end questions?

top related