Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mode offline : Notre application Android foncti...

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

Mathieu Hausherr

April 11, 2017
Tweet

More Decks by Mathieu Hausherr

Other Decks in Technology

Transcript

  1. #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
  2. #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
  3. #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
  4. #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()); } }
  5. #VirtuoOffline #AndroidMakers 22 OkHttp Interceptor if (isNetworkAvailable && !forceCache) {

    … } else { addCacheControl(builder); response = chain.proceed(builder.build()); }
  6. #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; } } }
  7. #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; } } }
  8. #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; } } }
  9. #VirtuoOffline #AndroidMakers 26 OkHttp Interceptor void addCacheControl(Request.Builder builder) { builder.cacheControl(new

    CacheControl.Builder() .onlyIfCached() .maxStale(28, TimeUnit.DAYS) .build()); }
  10. #VirtuoOffline #AndroidMakers 28 Chuck Interceptor OkHttpClient client = new OkHttpClient.Builder()

    .addInterceptor(new ChuckInterceptor(context)) .addInterceptor(new VInterceptor(context)) .build();
  11. #VirtuoOffline #AndroidMakers 32 Déclaration la plus rapide possible, image avec

    sha1 POST /inspection [{
 "created_at": …,
 "zone_id": …,
 "image_hash": …
 }]
  12. #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) {} }
  13. #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) {} }
  14. #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)); } );
  15. #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)); } );
  16. #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)); } );
  17. #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);
  18. #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);
  19. #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);
  20. #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);
  21. #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);
  22. #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);
  23. #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
  24. #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
  25. #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(…); })
  26. #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(…); })
  27. #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(…); })
  28. #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(…); })
  29. #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(…); })
  30. #VirtuoOffline #AndroidMakers 64 Programmation réactive, clés partenaire .doOnNext((Action1) (aVoid) ->

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

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

    { updateView(++index); }) .toList() .subscribe( (Action1) (aVoid) -> {okTransfert();}, (throwable) -> {manageErrors(throwable);} );