Mode offline : Notre application Android fonctionne au niveau -5 d’un parking

Mode offline : Notre application Android fonctionne au niveau -5 d’un parking

Votre application Android fonctionne-t-elle parfaitement en mode offline ?

Pour Virtuo, c’est indispensable. Virtuo est la startup française qui propose une nouvelle expérience de location de voiture. Tout se fait depuis un mobile : la création de compte, la réservation, l’état des lieux et l’ouverture de la voiture. Grâce à ça, plus de file au guichet pour récupérer sa clé. Parce qu’on n’a pas peur des challenges, nos clients n’ont jamais de clé et utilisent leur smartphone ou leur smartwatch pour ouvrir et démarrer leur véhicule pendant toute la durée de la location. Et ça fonctionne, puisque Virtuo totalise déjà plus de 7.000 jours de locations qui commencent toujours de la même manière : dans le sous-sol d’un parking, sans accès à la 3G.

Au programme : BLE : La seule solution rapide et vraiment offline Cache HTTP : Connaissez vous le header max-stale Synchronisation et conflits : Quelle stratégie adopter Programmation réactive : Pourquoi elle nous a aidé à nous améliorer UX/Design : Comment un bandeau permet d’améliorer l'expérience Stratégie de cache en fonction du contexte : Pourquoi ne peut-on pas appliquer la même stratégie avant, pendant ou après une réservation

Ddc294f25a19a5c979deabbac498e020?s=128

Mathieu Hausherr

April 11, 2017
Tweet

Transcript

  1. #VirtuoOffline #AndroidMakers AndroidMakers Mathieu Hausherr @mhausherr Romain Potier @potierromain 1

  2. #VirtuoOffline #AndroidMakers Mode offline Notre application fonctionne au niveau -5

    d’un parking 2
  3. #VirtuoOffline #AndroidMakers 3

  4. #VirtuoOffline #AndroidMakers 4

  5. #VirtuoOffline #AndroidMakers 5

  6. #VirtuoOffline #AndroidMakers 6

  7. #VirtuoOffline #AndroidMakers 7

  8. #VirtuoOffline #AndroidMakers 8

  9. #VirtuoOffline #AndroidMakers 9

  10. #VirtuoOffline #AndroidMakers 10

  11. #VirtuoOffline #AndroidMakers 11

  12. #VirtuoOffline #AndroidMakers 12

  13. #VirtuoOffline #AndroidMakers 13

  14. #VirtuoOffline #AndroidMakers 14 Version 0.0.1-alpha

  15. #VirtuoOffline #AndroidMakers 15

  16. #VirtuoOffline #AndroidMakers 16 Plus jamais ça !

  17. #VirtuoOffline #AndroidMakers 17 Cache Http Connection: keep-alive Content-Encoding: gzip Content-Type:

    application/json; charset=utf-8 Date: Thu, 23 Feb 2017 16:31:56 GMT Cache-Control: max-age=600 Cache-Control: max-stale=2419200
  18. #VirtuoOffline #AndroidMakers 18 Cache Http Connection: keep-alive Content-Encoding: gzip Content-Type:

    application/json; charset=utf-8 Date: Thu, 23 Feb 2017 16:31:56 GMT Cache-Control: max-age=600 Cache-Control: max-stale=2419200
  19. #VirtuoOffline #AndroidMakers 19 Cache Http Connection: keep-alive Content-Encoding: gzip Content-Type:

    application/json; charset=utf-8 Date: Thu, 23 Feb 2017 16:31:56 GMT Cache-Control: max-age=600 Cache-Control: max-stale=2419200
  20. #VirtuoOffline #AndroidMakers 20 Cache Http Download Cache Expiration max-age max-stale

    Real cache Expiration
  21. #VirtuoOffline #AndroidMakers 21 OkHttp Interceptor public class VInterceptor implements Interceptor

    { @Override public Response intercept(Chain chain) throws IOException { Request.Builder builder = chain.request().newBuilder(); return chain.proceed(builder.build()); } }
  22. #VirtuoOffline #AndroidMakers 22 OkHttp Interceptor if (isNetworkAvailable && !forceCache) {

    … } else { addCacheControl(builder); response = chain.proceed(builder.build()); }
  23. #VirtuoOffline #AndroidMakers 23 OkHttp Interceptor if (isNetworkAvailable && !forceCache) {

    try { response = chain.proceed(builder.build()); } catch (Exception e) { if (GET.equals(chain.request().method()) { addCacheControl(builder); response = chain.proceed(builder.build()); } else { throw e; } } }
  24. #VirtuoOffline #AndroidMakers 24 OkHttp Interceptor if (isNetworkAvailable && !forceCache) {

    try { response = chain.proceed(builder.build()); } catch (Exception e) { if (GET.equals(chain.request().method()) { addCacheControl(builder); response = chain.proceed(builder.build()); } else { throw e; } } }
  25. #VirtuoOffline #AndroidMakers 25 OkHttp Interceptor if (isNetworkAvailable && !forceCache) {

    try { response = chain.proceed(builder.build()); } catch (Exception e) { if (GET.equals(chain.request().method()) { addCacheControl(builder); response = chain.proceed(builder.build()); } else { throw e; } } }
  26. #VirtuoOffline #AndroidMakers 26 OkHttp Interceptor void addCacheControl(Request.Builder builder) { builder.cacheControl(new

    CacheControl.Builder() .onlyIfCached() .maxStale(28, TimeUnit.DAYS) .build()); }
  27. #VirtuoOffline #AndroidMakers 27

  28. #VirtuoOffline #AndroidMakers 28 Chuck Interceptor OkHttpClient client = new OkHttpClient.Builder()

    .addInterceptor(new ChuckInterceptor(context)) .addInterceptor(new VInterceptor(context)) .build();
  29. #VirtuoOffline #AndroidMakers 29 Bien joué les gars !

  30. #VirtuoOffline #AndroidMakers 30 Version 1.0.0

  31. #VirtuoOffline #AndroidMakers 31

  32. #VirtuoOffline #AndroidMakers 32 Déclaration la plus rapide possible, image avec

    sha1 POST /inspection [{
 "created_at": …,
 "zone_id": …,
 "image_hash": …
 }]
  33. #VirtuoOffline #AndroidMakers 33

  34. #VirtuoOffline #AndroidMakers 34 JobScheduler sur Android @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public

    class InspectionJobService extends JobService { @Override public boolean onStartJob(final JobParameters params) {} @Override public boolean onStopJob(JobParameters params) {} }
  35. #VirtuoOffline #AndroidMakers 35 JobScheduler sur Android @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public

    class InspectionJobService extends JobService { @Override public boolean onStartJob(final JobParameters params) {} @Override public boolean onStopJob(JobParameters params) {} }
  36. #VirtuoOffline #AndroidMakers 36 JobScheduler sur Android, Notation de la location

    @Override public boolean onStartJob(final JobParameters params) { inspectionService.sendReport(map).subscribeOn(…).observeOn(…) .subscribe((Action1) (bookingResult) -> { jobFinished(params, false); }, (throwable) -> { jobFinished(params, NetworkUtil.noNetworkError(throwable)); } );
  37. #VirtuoOffline #AndroidMakers 37 JobScheduler sur Android, Notation de la location

    @Override public boolean onStartJob(final JobParameters params) { inspectionService.sendReport(map).subscribeOn(…).observeOn(…) .subscribe((Action1) (bookingResult) -> { jobFinished(params, false); }, (throwable) -> { jobFinished(params, NetworkUtil.noNetworkError(throwable)); } );
  38. #VirtuoOffline #AndroidMakers 38 JobScheduler sur Android, Notation de la location

    @Override public boolean onStartJob(final JobParameters params) { inspectionService.sendReport(map).subscribeOn(…).observeOn(…) .subscribe((Action1) (bookingResult) -> { jobFinished(params, false); }, (throwable) -> { jobFinished(params, NetworkUtil.noNetworkError(throwable)); } );
  39. #VirtuoOffline #AndroidMakers 39 JobScheduler sur Android, Ajout dans le scheduler

    PersistableBundle persistableBundle = new PersistableBundle(); persistableBundle.putString(…); JobScheduler scheduler = (JobScheduler) getActivity().getSystemService(Context.JOB_SCHED ULER_SERVICE);
  40. #VirtuoOffline #AndroidMakers 40 JobScheduler sur Android, Ajout dans le scheduler

    PersistableBundle persistableBundle = new PersistableBundle(); persistableBundle.putString(…); JobScheduler scheduler = (JobScheduler) getActivity().getSystemService(Context.JOB_SCHED ULER_SERVICE);
  41. #VirtuoOffline #AndroidMakers 41 JobScheduler sur Android, Ajout dans le scheduler

    JobInfo jobInfo = new JobInfo.Builder(99, new ComponentName(context, InspectionJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setPersisted(true) .setExtras(persistableBundle) .build(); scheduler.schedule(jobInfo);
  42. #VirtuoOffline #AndroidMakers 42 JobScheduler sur Android, Ajout dans le scheduler

    JobInfo jobInfo = new JobInfo.Builder(99, new ComponentName(context, InspectionJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setPersisted(true) .setExtras(persistableBundle) .build(); scheduler.schedule(jobInfo);
  43. #VirtuoOffline #AndroidMakers 43 JobScheduler sur Android, Ajout dans le scheduler

    JobInfo jobInfo = new JobInfo.Builder(99, new ComponentName(context, InspectionJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setPersisted(true) .setExtras(persistableBundle) .build(); scheduler.schedule(jobInfo);
  44. #VirtuoOffline #AndroidMakers 44 JobScheduler sur Android, Ajout dans le scheduler

    JobInfo jobInfo = new JobInfo.Builder(99, new ComponentName(context, InspectionJobService.class)) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setPersisted(true) .setExtras(persistableBundle) .build(); scheduler.schedule(jobInfo);
  45. #VirtuoOffline #AndroidMakers 45 Version 1.1.0

  46. #VirtuoOffline #AndroidMakers 46

  47. #VirtuoOffline #AndroidMakers 47

  48. #VirtuoOffline #AndroidMakers 48

  49. #VirtuoOffline #AndroidMakers 49

  50. #VirtuoOffline #AndroidMakers 50 Fetch de Picasso new Picasso.Builder(context) .build() .load(imageUrl)

    .fetch(callback);
  51. #VirtuoOffline #AndroidMakers 51 Premiers retours utilisateurs

  52. #VirtuoOffline #AndroidMakers 52

  53. #VirtuoOffline #AndroidMakers 53

  54. #VirtuoOffline #AndroidMakers 54

  55. #VirtuoOffline #AndroidMakers 55

  56. #VirtuoOffline #AndroidMakers 56 Optimiser le code

  57. #VirtuoOffline #AndroidMakers 57 Programmation réactive, clés partenaire Mise à jour

    du serveur Téléchargement des photos Téléchargement de la clé Mise à jour d’un compteur
  58. #VirtuoOffline #AndroidMakers 58 Programmation réactive, clés partenaire Mise à jour

    du serveur Téléchargement des photos Téléchargement de la clé Mise à jour d’un compteur Mise à jour du serveur Téléchargement des photos Téléchargement de la clé Mise à jour d’un compteur Mise à jour du serveur Téléchargement des photos Téléchargement de la clé Mise à jour d’un compteur
  59. #VirtuoOffline #AndroidMakers 59 Programmation réactive, clés partenaire Observable.from(Lists.newArrayList(id1, id2, id3)).subscribeOn(…)

    .flatMap((Func1) (interventionId) -> { return interventionService.create(param); }) .flatMap((Func1) (interventionResult) -> { return downloadDamagesPictures(interventionResult.damages);}) .flatMap((Func1) (result) -> { return carConnector.downloadKey(…); })
  60. #VirtuoOffline #AndroidMakers 60 Programmation réactive, clés partenaire Observable.from(Lists.newArrayList(id1, id2, id3)).subscribeOn(…)

    .flatMap((Func1) (interventionId) -> { return interventionService.create(param); }) .flatMap((Func1) (interventionResult) -> { return downloadDamagesPictures(interventionResult.damages);}) .flatMap((Func1) (result) -> { return carConnector.downloadKey(…); })
  61. #VirtuoOffline #AndroidMakers 61 Programmation réactive, clés partenaire Observable.from(Lists.newArrayList(id1, id2, id3)).subscribeOn(…)

    .flatMap((Func1) (interventionId) -> { return interventionService.create(param); }) .flatMap((Func1) (interventionResult) -> { return downloadDamagesPictures(interventionResult.damages);}) .flatMap((Func1) (result) -> { return carConnector.downloadKey(…); })
  62. #VirtuoOffline #AndroidMakers 62 Programmation réactive, clés partenaire Observable.from(Lists.newArrayList(id1, id2, id3)).subscribeOn(…)

    .flatMap((Func1) (interventionId) -> { return interventionService.create(param); }) .flatMap((Func1) (interventionResult) -> { return downloadDamagesPictures(interventionResult.damages);}) .flatMap((Func1) (result) -> { return carConnector.downloadKey(…); })
  63. #VirtuoOffline #AndroidMakers 63 Programmation réactive, clés partenaire Observable.from(Lists.newArrayList(id1, id2, id3)).subscribeOn(…)

    .flatMap((Func1) (interventionId) -> { return interventionService.create(param); }) .flatMap((Func1) (interventionResult) -> { return downloadDamagesPictures(interventionResult.damages);}) .flatMap((Func1) (result) -> { return carConnector.downloadKey(…); })
  64. #VirtuoOffline #AndroidMakers 64 Programmation réactive, clés partenaire .doOnNext((Action1) (aVoid) ->

    { updateView(++index); }) .toList() .subscribe( (Action1) (aVoid) -> {okTransfert();}, (throwable) -> {manageErrors(throwable);} );
  65. #VirtuoOffline #AndroidMakers 65 Programmation réactive, clés partenaire .doOnNext((Action1) (aVoid) ->

    { updateView(++index); }) .toList() .subscribe( (Action1) (aVoid) -> {okTransfert();}, (throwable) -> {manageErrors(throwable);} );
  66. #VirtuoOffline #AndroidMakers 66 Programmation réactive, clés partenaire .doOnNext((Action1) (aVoid) ->

    { updateView(++index); }) .toList() .subscribe( (Action1) (aVoid) -> {okTransfert();}, (throwable) -> {manageErrors(throwable);} );
  67. #VirtuoOffline #AndroidMakers 67 Pour tester

  68. #VirtuoOffline #AndroidMakers 68 Code promo ANDROID15

  69. #VirtuoOffline #AndroidMakers 69 THANKS