Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

No internet? No Problem!

852 views

Published on

At Off Grid Electric our mission is to power homes across rural Africa with affordable, solar energy. In order to do that we need to provide our employees with tools that work both on and offline. So how did we do it? In this talk, learn about the techniques we employed to provide a unique online-offline experience in our Android applications. We’ll discuss the overall architecture, third party libraries used, and some of the challenges that we faced. As more and more users come online in various parts of the world it makes sense for companies to begin exploring how they can modify their applications to be more network-friendly. This talk will get you headed in the right direction!

Published in: Technology
  • Be the first to comment

No internet? No Problem!

  1. 1. NO INTERNET? NO PROBLEM!
  2. 2. OFF GRID ELECTRIC From: https://medium.com/@Offgrid
  3. 3. OFF GRID ELECTRIC
  4. 4. AGENDA ARCHITECTURE LIBRARIES CHALLENGES
  5. 5. CHALLENGES
  6. 6. 3G COVERAGE IN ARUSHA, TZ
  7. 7. 4G LTE COVERAGE IN WASHINGTON, DC
  8. 8. DON’T ASSUME BE RESILIENT
  9. 9. ARCHITECTURE
  10. 10. BE USEFUL Our App ‣Store data locally ‣Separate UI and network ‣Queue requests
  11. 11. MVP(Model View Presenter)
  12. 12. EVENT BUS/RXJAVA DATABASE REPOSITORY REPOSITORY ACTIVITY FRAGMENT JOBS PRESENTER PRESENTER SERVICE
  13. 13. DOWNLOAD ALL THE THINGS
  14. 14. DISPLAY A VIEW Is pre-populated data needed?
  15. 15. EVENT BUS/RXJAVA FRAGMENT PRESENTER SERVICE REPOSITORY DATABASE
  16. 16. TAKE AN ACTION Is the Network Available?
  17. 17. EVENT BUS/RXJAVA FRAGMENT PRESENTER SERVICE REPOSITORY DATABASE JOB
  18. 18. APP SERVER
  19. 19. CONFLICT RESOLUTION
  20. 20. REQUEST TIMESTAMPS
  21. 21. STATUS FIELDS ACTION STATE
  22. 22. BUNDLING REQUESTS /users/commissions /users/info /users/roles
  23. 23. BUNDLING REQUESTS /users/commissions /users/info /users/roles /users/me
  24. 24. EVENT BUS/RXJAVA DATABASE REPOSITORY REPOSITORY ACTIVITY FRAGMENT JOBS PRESENTER PRESENTER SERVICE
  25. 25. LIBRARIES
  26. 26. WHAT WE USE ‣Realm ‣EventBus ‣Android Job
  27. 27. STORE DATA LOCALLY
  28. 28. REALM - MODEL CLASS @RealmClass
 public class Place implements RealmModel {
 }
  29. 29. REALM - MODEL CLASS @RealmClass
 public class Place implements RealmModel {
 @PrimaryKey
 private String localId; @Index
 private Long remoteId;
 private Gps location; … }
  30. 30. REALM - MODEL CLASS localId remoteId location 19444498-2a40… 1458260 65031f36-bde9… e85c9757-f546…
  31. 31. REALM - ADDING A RECORD public class PlaceRepository implements Repository<Place> {
 @Override
 public void add (final Place item) {
 
 } …
  32. 32. REALM - ADDING A RECORD public class PlaceRepository implements Repository<Place> {
 @Override
 public void add (final Place item) {
 Realm realm = Realm.getDefaultInstance();
 realm.executeTransaction((realm) -> {
 realm.insertOrUpdate(item);
 });
 realm.close();
 } …
  33. 33. REALM - QUERY RESULT @Override
 public Place toResult (Realm realm) {
 return realm.where(Place.class)
 
 }
  34. 34. REALM - QUERY RESULT @Override
 public Place toResult (Realm realm) {
 return realm.where(Place.class)
 .equalTo(PLACE_ID, placeId)
 .findFirst();
 }
  35. 35. REALM - QUERY RESULT @Override
 public Place toResult (Realm realm) {
 return realm.where(Place.class)
 .equalTo(PLACE_ID, placeId)
 .findFirst();
 }
  36. 36. SEPARATE UI AND NETWORK
  37. 37. EVENTBUS From: http://greenrobot.org/eventbus
  38. 38. EVENTBUS - SETUP @Provides
 @Singleton
 public Bus provideBus () {
 return new Bus(EventBus.builder()
 .logNoSubscriberMessages(BuildConfig.DEBUG)
 .throwSubscriberException(false)
 .build());
 }
  39. 39. EVENTBUS - REGISTER public class LeadsMapPresenter {
 private final Bus bus;
 private LeadsMapView leadsMapView;
 
 @Inject
 LeadsMapPresenter (Bus bus) {
 this.bus = bus;
 }
 
 …
  40. 40. EVENTBUS - REGISTER public class LeadsMapPresenter { 
 public void attachView (LeadsMapView view) {
 this.leadsMapView = view;
 bus.register(this);
 }
  41. 41. EVENTBUS - REGISTER public class LeadsMapPresenter { 
 public void attachView (LeadsMapView view) {
 …
 } public void detachView () {
 this.leadsMapView = null;
 bus.unregister(this);
 }
  42. 42. EVENT BUS/RXJAVA FRAGMENT PRESENTER SERVICE REPOSITORY DATABASE JOB
  43. 43. EVENTBUS - POST EVENT api.submitPlace(place).enqueue(new Callback<Place>() {
 … JOB
  44. 44. EVENTBUS - POST EVENT api.submitPlace(place).enqueue(new Callback<Place>() {
 
 public void onResponse (…, Response<Place> response) {
 if (response.isSuccessful()) {
 
 } … JOB
  45. 45. EVENTBUS - POST EVENT api.submitPlace(place).enqueue(new Callback<Place>() {
 
 public void onResponse (…, Response<Place> response) {
 if (response.isSuccessful()) {
 Place updatedPlace = response.body();
 bus.post(new PlaceSubmissionSuccessEvent(updatedPlace));
 } … JOB
  46. 46. EVENTBUS - RETRIEVE EVENT public class LeadsMapPresenter { 
 @Subscribe
 public void handle (PlaceSubmissionSuccessEvent event) {
 leadsMapView.displaySuccessMessage(); } … PRESENTER
  47. 47. EVENTBUS - SEND STATUS BAR NOTIFICATION SERVICE
  48. 48. EVENTBUS - SEND STATUS BAR NOTIFICATION SERVICE @Subscribe(priority = 1) open fun onEvent(event: PaymentSuccessEvent) { }
  49. 49. EVENTBUS - SEND STATUS BAR NOTIFICATION SERVICE @Subscribe(priority = 1) open fun onEvent(event: PaymentSuccessEvent) { val notif = getString(R.string.payment_submitted_message) createNotification(notif, 1) }
  50. 50. QUEUE REQUESTS
  51. 51. ANDROID JOB
  52. 52. ANDROID JOB ‣Alarm Manager ‣Job Scheduler ‣GCM Network Manager
  53. 53. ANDROID JOB - COMPARISON Library Min API Google Play? JobScheduler 21 No Firebase Job Dispatcher 9 Yes Android Job 14 No
  54. 54. ANDROID JOB JOB MANAGER JOB CREATOR
  55. 55. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR PROVIDER JOB PROVIDER JOB
  56. 56. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR PROVIDER JOB PROVIDER JOB JOB REQUEST
  57. 57. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR PROVIDER JOB PROVIDER JOB JOB REQUEST JOB
  58. 58. ANDROID JOB - CREATOR public class ConfettiJobCreator implements JobCreator {
 
 Map<String, Provider<Job>> jobs;
 
 public Job create (String tag) {
 
 }
 }
  59. 59. ANDROID JOB - CREATOR public Job create (String tag) {
 Provider<Job> jobProvider = jobs.get(tag);
 return jobProvider.get();
 }
  60. 60. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR JOB REQUEST JOB
  61. 61. ANDROID JOB - JOB EXECUTION protected Result onRunJob (final Params params) {
 
 } JOB
  62. 62. ANDROID JOB - JOB EXECUTION protected Result onRunJob (final Params params) {
 PersistableBundleCompat extras = params.getExtras();
 String placeId = extras.getString(PARAM_PLACE_ID);
 
 
 } JOB
  63. 63. ANDROID JOB - JOB EXECUTION protected Result onRunJob (final Params params) {
 PersistableBundleCompat extras = params.getExtras();
 String placeId = extras.getString(PARAM_PLACE_ID);
 
 return submitRequest(placeId) ? SUCCESS: FAILURE;
 } JOB
  64. 64. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR JOB REQUEST JOB
  65. 65. ANDROID JOB - SCHEDULING public static JobRequest get (String placeId) {
 PersistableBundleCompat extras = …;
 extras.putString(PARAM_PLACE_ID, placeId);
 
 
 }
  66. 66. ANDROID JOB - SCHEDULING public static JobRequest get (String placeId) {
 PersistableBundleCompat extras = …;
 extras.putString(PARAM_PLACE_ID, placeId);
 
 return new JobRequest.Builder(SendPlaceRequestJob.JOB_TAG)
 
 }
  67. 67. ANDROID JOB - SCHEDULING public static JobRequest get (String placeId) {
 PersistableBundleCompat extras = …;
 extras.putString(PARAM_PLACE_ID, placeId);
 
 return new JobRequest.Builder(SendPlaceRequestJob.JOB_TAG)
 .setExecutionWindow(10_000L, 20_000L)
 .setRequiredNetworkType(NetworkType.CONNECTED)
 
 }
  68. 68. ANDROID JOB - SCHEDULING public static JobRequest get (String placeId) {
 PersistableBundleCompat extras = …;
 extras.putString(PARAM_PLACE_ID, placeId);
 
 return new JobRequest.Builder(SendPlaceRequestJob.JOB_TAG)
 .setExecutionWindow(10_000L, 20_000L)
 .setRequiredNetworkType(NetworkType.CONNECTED)
 .setExtras(extras)
 
 }
  69. 69. ANDROID JOB - SCHEDULING public static JobRequest get (String placeId) {
 PersistableBundleCompat extras = …;
 extras.putString(PARAM_PLACE_ID, placeId);
 
 return new JobRequest.Builder(SendPlaceRequestJob.JOB_TAG)
 .setExecutionWindow(10_000L, 20_000L)
 .setRequiredNetworkType(NetworkType.CONNECTED)
 .setExtras(extras)
 .setRequirementsEnforced(true)
 .build();
 }
  70. 70. if has network connection attempt to submit request
  71. 71. if has network connection attempt to submit request else queue request
  72. 72. ANDROID JOB - SCHEDULING jobManager.schedule( );
  73. 73. ANDROID JOB - SCHEDULING jobManager.schedule(SendPlaceRequestJob.buildJobRequest(id)); JOB REQUEST JOB QUEUE
  74. 74. WHAT WE USE ‣Realm ‣EventBus ‣Android Job
  75. 75. WHAT’S NEXT? ‣SMS Fallback ‣Async Code Paths ‣Push Notifications
  76. 76. THANKS @brwngrldev adavis.info --- @moyheen medium.com/@moyinoluwa

×