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.
ONLINE-OFFLINE
Developing Apps for Emerging Markets
OFF GRID ELECTRIC
From: https://medium.com/@Offgrid
OFF GRID ELECTRIC
AGENDA
ARCHITECTURE
LIBRARIES
CHALLENGES
ARCHITECTURE
BE USEFUL
My App
‣Store data locally
‣Separate UI and network
‣Queue requests
MVP(Model View Presenter)
EVENT BUS
DATABASE
REPOSITORY
REPOSITORY
ACTIVITY
FRAGMENT
JOBS
PRESENTER
PRESENTER
SERVICE
DISPLAY A VIEW
PRESENTER
SERVICE
ACTIVITY
EVENT BUS
DATABASE
REPOSITORY
TAKE AN ACTION
PRESENTER
SERVICE
ACTIVITY
EVENT BUS
DATABASEREPOSITORY
JOB NETWORK
APP SERVER
AGREE ON
‣Conflict Resolution
‣Adding Timestamps to Requests
‣Bundling Requests
‣Use of Status Fields
EVENT BUS
DATABASE
REPOSITORY
REPOSITORY
ACTIVITY
FRAGMENT
JOBS
PRESENTER
PRESENTER
SERVICE
LIBRARIES
WHAT WE USE
‣Realm
‣EventBus
‣Android Job
STORE DATA LOCALLY
REALM
▸Easy to Set Up
▸Faster than ORMs
▸Has a Fluent API
REALM - MODEL CLASS
@RealmClass

public class Place implements RealmModel

{

}
REALM - MODEL CLASS
@RealmClass

public class Place implements RealmModel

{

@PrimaryKey

private String localId;
@Index
...
REALM - MODEL CLASS
localId remoteId location
19444498-2a40… 1458260
65031f36-bde9…
e85c9757-f546…
REALM - ADDING A RECORD
public class PlaceRepository implements Repository<Place>

{

@Override

public void add (final Pla...
REALM - ADDING A RECORD
public class PlaceRepository implements Repository<Place>

{

@Override

public void add (final Pla...
REALM - QUERY RESULT
private static final String PLACE_ID = "id";
@Override

public Place toResult (Realm realm)

{

return...
REALM - QUERY RESULT
private static final String PLACE_ID = "id";
@Override

public Place toResult (Realm realm)

{

return...
SEPARATE UI AND
NETWORK
EVENTBUS
From: http://greenrobot.org/eventbus
… WHEN YOU HAVE LOTS OF OBJECTS THAT
ARE POTENTIAL EVENT SOURCES.
Martin Fowler
EVENTBUS
EVENTBUS - SETUP
@Provides

@Singleton

public Bus provideBus ()

{

return new Bus( EventBus.builder()

.logNoSubscriberM...
EVENTBUS - REGISTER
public class LeadsMapPresenter

{

private final Bus bus;

private LeadsMapView leadsMapView;



@Injec...
EVENTBUS - REGISTER
public class LeadsMapPresenter

{

public void attachView (LeadsMapView view)

{

this.leadsMapView = ...
EVENTBUS - REGISTER
public class LeadsMapPresenter

{

public void attachView (LeadsMapView view)

{

this.leadsMapView = ...
TAKE AN ACTION
PRESENTER
SERVICE
ACTIVITY
EVENT BUS
DATABASEREPOSITORY
JOB NETWORK
EVENTBUS - POST EVENT
api.submitPlace( place ).enqueue( new Callback<Place>()

{

…
JOB
EVENTBUS - POST EVENT
api.submitPlace( place ).enqueue( new Callback<Place>()

{

@Override

public void onResponse (Call<...
EVENTBUS - POST EVENT
api.submitPlace( place ).enqueue( new Callback<Place>()

{

@Override

public void onResponse (Call<...
EVENTBUS - RETRIEVE EVENT
public class LeadsMapPresenter

{


@Subscribe

public void onPlaceSubmissionSuccess (PlaceSubmi...
QUEUE REQUESTS
ANDROID JOB
ANDROID JOB
‣Alarm Manager
‣Job Scheduler
‣GCM Network Manager
ANDROID JOB
JOB MANAGER
JOB CREATOR
ANDROID JOB
JOB MANAGER
PROVIDER
JOB
JOB CREATOR
PROVIDER
JOB
PROVIDER
JOB
ANDROID JOB
JOB MANAGER
PROVIDER
JOB
JOB CREATOR
PROVIDER
JOB
PROVIDER
JOB
JOB REQUEST
ANDROID JOB
JOB MANAGER
PROVIDER
JOB
JOB CREATOR
PROVIDER
JOB
PROVIDER
JOB
JOB REQUEST
JOB
ANDROID JOB - CREATOR
@Singleton

public class ConfettiJobCreator implements JobCreator

{

@Inject

Map<String, Provider<...
ANDROID JOB - CREATOR
@Singleton

public class ConfettiJobCreator implements JobCreator

{

@Inject

Map<String, Provider<...
ANDROID JOB - JOB EXECUTION
@Override

protected Result onRunJob (final Params params)

{



} JOB
ANDROID JOB - JOB EXECUTION
@Override

protected Result onRunJob (final Params params)

{

PersistableBundleCompat extras =...
ANDROID JOB - JOB EXECUTION
@Override

protected Result onRunJob (final Params params)

{

PersistableBundleCompat extras =...
ANDROID JOB
JOB MANAGER
PROVIDER
JOB
JOB CREATOR
JOB REQUEST
JOB
ANDROID JOB - SCHEDULING
public static JobRequest buildJobRequest (String placeId)

{

PersistableBundleCompat extras = ne...
ANDROID JOB - SCHEDULING
public static JobRequest buildJobRequest (String placeId)

{

PersistableBundleCompat extras = ne...
ANDROID JOB - SCHEDULING
public static JobRequest buildJobRequest (String placeId)

{

PersistableBundleCompat extras = ne...
ANDROID JOB - SCHEDULING
public static JobRequest buildJobRequest (String placeId)

{

PersistableBundleCompat extras = ne...
ANDROID JOB - SCHEDULING
public static JobRequest buildJobRequest (String placeId)

{

PersistableBundleCompat extras = ne...
ANDROID JOB - SCHEDULING
jobManager.schedule( );
ANDROID JOB - SCHEDULING
jobManager.schedule( SendPlaceRequestJob.buildJobRequest( id ) );
JOB REQUEST
JOB QUEUE
WHAT WE USE
‣Realm
‣EventBus
‣Android Job
CHALLENGES
WHAT’S NEXT?
‣RxJava
‣Mapbox
‣Push Notifications
THANKS
@brwngrldev
+AnnyceDavis
www.adavis.info
Developing Apps for Emerging Markets
Developing Apps for Emerging Markets
Developing Apps for Emerging Markets
Developing Apps for Emerging Markets
Developing Apps for Emerging Markets
Developing Apps for Emerging Markets
Upcoming SlideShare
Loading in …5
×

Developing Apps for Emerging Markets

1,262 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: Mobile
  • Be the first to comment

Developing Apps for Emerging Markets

  1. 1. ONLINE-OFFLINE Developing Apps for Emerging Markets
  2. 2. OFF GRID ELECTRIC From: https://medium.com/@Offgrid
  3. 3. OFF GRID ELECTRIC
  4. 4. AGENDA ARCHITECTURE LIBRARIES CHALLENGES
  5. 5. ARCHITECTURE
  6. 6. BE USEFUL My App ‣Store data locally ‣Separate UI and network ‣Queue requests
  7. 7. MVP(Model View Presenter)
  8. 8. EVENT BUS DATABASE REPOSITORY REPOSITORY ACTIVITY FRAGMENT JOBS PRESENTER PRESENTER SERVICE
  9. 9. DISPLAY A VIEW PRESENTER SERVICE ACTIVITY EVENT BUS DATABASE REPOSITORY
  10. 10. TAKE AN ACTION PRESENTER SERVICE ACTIVITY EVENT BUS DATABASEREPOSITORY JOB NETWORK
  11. 11. APP SERVER
  12. 12. AGREE ON ‣Conflict Resolution ‣Adding Timestamps to Requests ‣Bundling Requests ‣Use of Status Fields
  13. 13. EVENT BUS DATABASE REPOSITORY REPOSITORY ACTIVITY FRAGMENT JOBS PRESENTER PRESENTER SERVICE
  14. 14. LIBRARIES
  15. 15. WHAT WE USE ‣Realm ‣EventBus ‣Android Job
  16. 16. STORE DATA LOCALLY
  17. 17. REALM ▸Easy to Set Up ▸Faster than ORMs ▸Has a Fluent API
  18. 18. REALM - MODEL CLASS @RealmClass
 public class Place implements RealmModel
 {
 }
  19. 19. REALM - MODEL CLASS @RealmClass
 public class Place implements RealmModel
 {
 @PrimaryKey
 private String localId; @Index
 private Long remoteId;
 private Gps location;
 … }
  20. 20. REALM - MODEL CLASS localId remoteId location 19444498-2a40… 1458260 65031f36-bde9… e85c9757-f546…
  21. 21. REALM - ADDING A RECORD public class PlaceRepository implements Repository<Place>
 {
 @Override
 public void add (final Place item)
 {
 
 } …
  22. 22. 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.copyToRealmOrUpdate( item );
 } );
 realm.close();
 } …
  23. 23. REALM - QUERY RESULT private static final String PLACE_ID = "id"; @Override
 public Place toResult (Realm realm)
 {
 return realm.where( Place.class )
 
 }
  24. 24. REALM - QUERY RESULT private static final String PLACE_ID = "id"; @Override
 public Place toResult (Realm realm)
 {
 return realm.where( Place.class )
 .equalTo( PLACE_ID, placeId )
 .findFirst();
 }
  25. 25. SEPARATE UI AND NETWORK
  26. 26. EVENTBUS From: http://greenrobot.org/eventbus
  27. 27. … WHEN YOU HAVE LOTS OF OBJECTS THAT ARE POTENTIAL EVENT SOURCES. Martin Fowler EVENTBUS
  28. 28. EVENTBUS - SETUP @Provides
 @Singleton
 public Bus provideBus ()
 {
 return new Bus( EventBus.builder()
 .logNoSubscriberMessages( BuildConfig.DEBUG )
 .throwSubscriberException( false )
 .build() );
 }
  29. 29. EVENTBUS - REGISTER public class LeadsMapPresenter
 {
 private final Bus bus;
 private LeadsMapView leadsMapView;
 
 @Inject
 LeadsMapPresenter (Bus bus)
 {
 this.bus = bus;
 }
 
 …
  30. 30. EVENTBUS - REGISTER public class LeadsMapPresenter
 {
 public void attachView (LeadsMapView view)
 {
 this.leadsMapView = view;
 bus.register( this );
 }
  31. 31. EVENTBUS - REGISTER public class LeadsMapPresenter
 {
 public void attachView (LeadsMapView view)
 {
 this.leadsMapView = view;
 bus.register( this );
 } public void detachView ()
 {
 this.leadsMapView = null;
 bus.unregister( this );
 }
  32. 32. TAKE AN ACTION PRESENTER SERVICE ACTIVITY EVENT BUS DATABASEREPOSITORY JOB NETWORK
  33. 33. EVENTBUS - POST EVENT api.submitPlace( place ).enqueue( new Callback<Place>()
 {
 … JOB
  34. 34. EVENTBUS - POST EVENT api.submitPlace( place ).enqueue( new Callback<Place>()
 {
 @Override
 public void onResponse (Call<Place> call, Response<Place> response)
 {
 if ( response.isSuccessful() )
 {
 
 } … JOB
  35. 35. EVENTBUS - POST EVENT api.submitPlace( place ).enqueue( new Callback<Place>()
 {
 @Override
 public void onResponse (Call<Place> call, Response<Place> response)
 {
 if ( response.isSuccessful() )
 {
 Place updatedPlace = response.body();
 bus.post( new PlaceSubmissionSuccessEvent( updatedPlace ) );
 } … JOB
  36. 36. EVENTBUS - RETRIEVE EVENT public class LeadsMapPresenter
 { 
 @Subscribe
 public void onPlaceSubmissionSuccess (PlaceSubmissionSuccessEvent event)
 {
 leadsMapView.displaySuccessMessage( PLACE_SUCCESS ); } … PRESENTER
  37. 37. QUEUE REQUESTS
  38. 38. ANDROID JOB
  39. 39. ANDROID JOB ‣Alarm Manager ‣Job Scheduler ‣GCM Network Manager
  40. 40. ANDROID JOB JOB MANAGER JOB CREATOR
  41. 41. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR PROVIDER JOB PROVIDER JOB
  42. 42. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR PROVIDER JOB PROVIDER JOB JOB REQUEST
  43. 43. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR PROVIDER JOB PROVIDER JOB JOB REQUEST JOB
  44. 44. ANDROID JOB - CREATOR @Singleton
 public class ConfettiJobCreator implements JobCreator
 {
 @Inject
 Map<String, Provider<Job>> jobs;
 
 @Override
 public Job create (String tag)
 {
 
 }
 }
  45. 45. ANDROID JOB - CREATOR @Singleton
 public class ConfettiJobCreator implements JobCreator
 {
 @Inject
 Map<String, Provider<Job>> jobs;
 
 @Override
 public Job create (String tag)
 {
 Provider<Job> jobProvider = jobs.get( tag );
 return jobProvider.get();
 }
 }
  46. 46. ANDROID JOB - JOB EXECUTION @Override
 protected Result onRunJob (final Params params)
 {
 
 } JOB
  47. 47. ANDROID JOB - JOB EXECUTION @Override
 protected Result onRunJob (final Params params)
 {
 PersistableBundleCompat extras = params.getExtras();
 String placeId = extras.getString( PARAM_PLACE_ID );
 
 
 } JOB
  48. 48. ANDROID JOB - JOB EXECUTION @Override
 protected Result onRunJob (final Params params)
 {
 PersistableBundleCompat extras = params.getExtras();
 String placeId = extras.getString( PARAM_PLACE_ID );
 
 if ( submitRequest( placeId ) )
 {
 return Result.SUCCESS;
 }
 return Result.FAILURE;
 } JOB
  49. 49. ANDROID JOB JOB MANAGER PROVIDER JOB JOB CREATOR JOB REQUEST JOB
  50. 50. ANDROID JOB - SCHEDULING public static JobRequest buildJobRequest (String placeId)
 {
 PersistableBundleCompat extras = new PersistableBundleCompat();
 extras.putString( PARAM_PLACE_ID, placeId );
 
 
 }
  51. 51. ANDROID JOB - SCHEDULING public static JobRequest buildJobRequest (String placeId)
 {
 PersistableBundleCompat extras = new PersistableBundleCompat();
 extras.putString( PARAM_PLACE_ID, placeId );
 
 return new JobRequest.Builder( SendPlaceRequestJob.JOB_TAG )
 
 }
  52. 52. ANDROID JOB - SCHEDULING public static JobRequest buildJobRequest (String placeId)
 {
 PersistableBundleCompat extras = new PersistableBundleCompat();
 extras.putString( PARAM_PLACE_ID, placeId );
 
 return new JobRequest.Builder( SendPlaceRequestJob.JOB_TAG )
 .setExecutionWindow( 10_000L, 20_000L )
 .setRequiredNetworkType( JobRequest.NetworkType.CONNECTED )
 
 }
  53. 53. ANDROID JOB - SCHEDULING public static JobRequest buildJobRequest (String placeId)
 {
 PersistableBundleCompat extras = new PersistableBundleCompat();
 extras.putString( PARAM_PLACE_ID, placeId );
 
 return new JobRequest.Builder( SendPlaceRequestJob.JOB_TAG )
 .setExecutionWindow( 10_000L, 20_000L )
 .setRequiredNetworkType( JobRequest.NetworkType.CONNECTED )
 .setExtras( extras )
 
 }
  54. 54. ANDROID JOB - SCHEDULING public static JobRequest buildJobRequest (String placeId)
 {
 PersistableBundleCompat extras = new PersistableBundleCompat();
 extras.putString( PARAM_PLACE_ID, placeId );
 
 return new JobRequest.Builder( SendPlaceRequestJob.JOB_TAG )
 .setExecutionWindow( 10_000L, 20_000L )
 .setRequiredNetworkType( JobRequest.NetworkType.CONNECTED )
 .setExtras( extras )
 .setRequirementsEnforced( true )
 .build();
 }
  55. 55. ANDROID JOB - SCHEDULING jobManager.schedule( );
  56. 56. ANDROID JOB - SCHEDULING jobManager.schedule( SendPlaceRequestJob.buildJobRequest( id ) ); JOB REQUEST JOB QUEUE
  57. 57. WHAT WE USE ‣Realm ‣EventBus ‣Android Job
  58. 58. CHALLENGES
  59. 59. WHAT’S NEXT? ‣RxJava ‣Mapbox ‣Push Notifications
  60. 60. THANKS @brwngrldev +AnnyceDavis www.adavis.info

×