Lock in $30 Savings on PRO—Offer Ends Soon! ⏳

Développer une app Android en 2015

Développer une app Android en 2015

Je développe sur Android depuis 2009, année de naissance de Cupcake, Donut et Eclair et époque où un écran 3.2” et une trackball étaient la norme. Depuis, l’écosystème Android a beaucoup évolué apportant son lot de nouveautés et de challenges. Nous verrons ensemble ce qui se cache derrière une application Android moderne : RxJava, Material Design, RecyclerView... seront notamment de la partie.

Talk donnée au Paris Android User Group le 03/03/2015 chez BlaBlaCar
http://www.meetup.com/Android-Paris/events/220665795/
https://www.youtube.com/watch?v=vJtDozdLEg8

Florian Mierzejewski

March 03, 2015
Tweet

More Decks by Florian Mierzejewski

Other Decks in Programming

Transcript

  1. OkHttp An HTTP & SPDY client for Android and Java

    applications • Support de HTTP/2 et SPDY • Support de GZIP • Réponses en cache
  2. Retrofit A type-safe REST client for Android and Java Utilisation

    d’annotations pour décrire les requêtes HTTP
  3. RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(“https://api-v2launch.trakt.tv”) .build(); ! Movies movies

    = restAdapter.create(Movies.class); ! List<Comment> comments = movies.comments(42, 1, 10);
  4. RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(“https://api-v2launch.trakt.tv”) .setRequestInterceptor(new RequestInterceptor() { @Override

    public void intercept(RequestFacade request) { request.addHeader(“trakt-api-key”, apiKey); } }) .build();
  5. @GET("/movies/{id}/comments") List<Comment> comments(/*…*/); ! @GET("/movies/{id}/comments") void comments(/*…*/, Callback<List<Comment>> cb); !

    @GET("/movies/{id}/comments") Response comments(/*…*/); ! @GET("/movies/{id}/comments") Observable<List<Comment>> comments(/*…*/);
  6. @GET("/movies/{id}/comments") List<Comment> comments(/*…*/); ! @GET("/movies/{id}/comments") void comments(/*…*/, Callback<List<Comment>> cb); !

    @GET("/movies/{id}/comments") Response comments(/*…*/); ! @GET("/movies/{id}/comments") Observable<List<Comment>> comments(/*…*/);
  7. @GET("/movies/{id}/comments") List<Comment> comments(/*…*/); ! @GET("/movies/{id}/comments") void comments(/*…*/, Callback<List<Comment>> cb); !

    @GET("/movies/{id}/comments") Response comments(/*…*/); ! @GET("/movies/{id}/comments") Observable<List<Comment>> comments(/*…*/);
  8. @GET("/movies/{id}/comments") List<Comment> comments(/*…*/); ! @GET("/movies/{id}/comments") void comments(/*…*/, Callback<List<Comment>> cb); !

    @GET("/movies/{id}/comments") Response comments(/*…*/); ! @GET("/movies/{id}/comments") Observable<List<Comment>> comments(/*…*/);
  9. • Pas de dépendance • Jar < 700KB • Java

    6+ & Android 2.3+ • Support des lambdas de Java 8 • Polyglotte (Scala, Groovy, Clojure et Kotlin) • Exécution synchrone ou asynchrone
  10. public interface Observer<T> { ! public abstract void onNext(T t);

    ! public abstract void onCompleted(); ! public abstract void onError(Throwable e); ! }
  11. public interface Observer<T> { ! public abstract void onNext(T t);

    ! public abstract void onCompleted(); ! public abstract void onError(Throwable e); ! }
  12. public interface Observer<T> { ! public abstract void onNext(T t);

    ! public abstract void onCompleted(); ! public abstract void onError(Throwable e); ! }
  13. Observable<Integer> Observable.create(new Observable.OnSubscribe<Integer>() { @Override public void call(Subscriber<? super Integer>

    observer) { try { if (!observer.isUnsubscribed()) { for (int i = 1; i < 5; i++) { observer.onNext(i); } observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } } );
  14. Observable.just(1, 2, 3) .subscribe(new Subscriber<Integer>() { @Override public void onNext(Integer

    item) { System.out.println("Next: " + item); } ! @Override public void onError(Throwable error) { System.err.println("Error: " + error.getMessage()); } ! @Override public void onCompleted() { System.out.println("Sequence complete."); } });
  15. Observable.just(1, 2, 3) .subscribe(new Subscriber<Integer>() { @Override public void onNext(Integer

    item) { System.out.println("Next: " + item); } ! @Override public void onError(Throwable error) { System.err.println("Error: " + error.getMessage()); } ! @Override public void onCompleted() { System.out.println("Sequence complete."); } });
  16. Observable.just(1, 2, 3) .subscribe(new Subscriber<Integer>() { @Override public void onNext(Integer

    item) { System.out.println("Next: " + item); } ! @Override public void onError(Throwable error) { System.err.println("Error: " + error.getMessage()); } ! @Override public void onCompleted() { System.out.println("Sequence complete."); } });
  17. > Next: 1 > Next: 2 > Next: 3 >

    Sequence complete.
  18. comments(42, 1, 10) .filter(c -> !c.spoiler) .toSortedList((c1, c2) -> Integer.compare(c2.likes,

    c1.likes)) .lift(flattenList()) .take(3) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(myObserver);
  19. Pourquoi un ContentProvider ? ! • Système de notification des

    URIs • Reste assez “bas niveau” pour faire ce que l’on veut
  20. Ce qui est moins cool ! • Beaucoup de boilerplate

    • Difficile de ne pas faire d’erreurs
  21. Schematic à la rescousse ! ! • Génération du ContentProvider

    • Annotations, annotations everywhere • Permet de se concentrer sur le schéma et la bonne gestion des URIs https://github.com/SimonVT/schematic
  22. Par contre ! • Très peu de documentation • “La

    documentation c’est le code” • SimonVT/cathode - ProviderSchematic.java https://github.com/SimonVT/schematic
  23. public interface MovieColumns { @DataType(INTEGER) @PrimaryKey @AutoIncrement String ID =

    “_id”; /* … */ } ! Supporte également @DefaultValue, @NotNull, @References et @Unique https://github.com/SimonVT/schematic
  24. DatabaseX.java ! @Table(MovieColumns.class) public static final String MOVIES = “movies";

    /* … */ ! @ExecOnCreate // indexes, triggers… https://github.com/SimonVT/schematic
  25. ProviderX.java - Movies ! @ContentUri( path = Path.MOVIES, type =

    "vnd.android.cursor.dir/movie") public static final Uri CONTENT_URI = buildUri(Path.MOVIES); https://github.com/SimonVT/schematic
  26. ProviderX.java - Movies ! @InexactContentUri( path = Path.MOVIES + "/#",

    name = "MOVIE_ID", type = "vnd.android.cursor.item/movie", whereColumn = MovieColumns.ID_TRAKT, pathSegment = 1) public static Uri withId(String traktId) { return buildUri(Path.MOVIES, traktId); } https://github.com/SimonVT/schematic
  27. ProviderX.java - Movies ! ! @NotifyUpdate(paths = Path.MOVIES + "/#")

    public static Uri[] notifyUpdate(Context context, Uri uri, String where, String[] whereArgs) { int id = Integer.valueOf(uri.getPathSegments().get(1)); return new Uri[]{CONTENT_URI, withId(id)} } https://github.com/SimonVT/schematic
  28. Du Rx dans votre base https://github.com/square/sqlbrite ! • Wrap SQLiteOpenHelper

    • Requêtes en base deviennent des Observables • Supporte les notifications quand une table est modifiée
  29. Ce qui est cool : ! • 3 LayoutManager de

    base • Animations • Modifier l’orientation • Layout depuis le haut ou le bas • Gestion des spans
  30. Ce qui l’est moins : ! • Dividers ? •

    Headers / Footers ? • Réécrire les adapters • OnItemClickListener ? • ChoiceModes ?
  31. Headers / Footers ! 1. Wrapper l’adapter dans un autre

    2. Gérer les headers / footers à la main 3. Ecrire son propre LayoutManager 4. Padding et Translation
  32. Une ligne de code pour supporter tous les types d’écrans

    ! new GridLayoutManager( getActivity(), getResources().getInteger(R.integer.grid_poster_columns)); res/values integers.xml <integer name="grid_poster_columns">3</integer> res/values-land … res/values-sw600dp … …