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

private Long remoteId;

private Gps location;

…
}
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 Place item)

{



}
…
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();

}
…
REALM - QUERY RESULT
private static final String PLACE_ID = "id";
@Override

public Place toResult (Realm realm)

{

return realm.where( Place.class )



}
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();

}
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()

.logNoSubscriberMessages( BuildConfig.DEBUG )

.throwSubscriberException( false )

.build() );

}
EVENTBUS - REGISTER
public class LeadsMapPresenter

{

private final Bus bus;

private LeadsMapView leadsMapView;



@Inject

LeadsMapPresenter (Bus bus)

{

this.bus = bus;

}



…
EVENTBUS - REGISTER
public class LeadsMapPresenter

{

public void attachView (LeadsMapView view)

{

this.leadsMapView = view;

bus.register( this );

}
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 );

}
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<Place> call, Response<Place> response)

{

if ( response.isSuccessful() )

{



}
…
JOB
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
EVENTBUS - RETRIEVE EVENT
public class LeadsMapPresenter

{


@Subscribe

public void onPlaceSubmissionSuccess (PlaceSubmissionSuccessEvent event)

{

leadsMapView.displaySuccessMessage( PLACE_SUCCESS );
}
…
PRESENTER
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<Job>> jobs;



@Override

public Job create (String tag)

{



}

}
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();

}

}
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 = params.getExtras();

String placeId = extras.getString( PARAM_PLACE_ID );





} JOB
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
ANDROID JOB
JOB MANAGER
PROVIDER
JOB
JOB CREATOR
JOB REQUEST
JOB
ANDROID JOB - SCHEDULING
public static JobRequest buildJobRequest (String placeId)

{

PersistableBundleCompat extras = new PersistableBundleCompat();

extras.putString( PARAM_PLACE_ID, placeId );





}
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 )



}
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 )



}
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 )



}
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();

}
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

  • 1.
  • 2.
    OFF GRID ELECTRIC From:https://medium.com/@Offgrid
  • 3.
  • 4.
  • 5.
  • 6.
    BE USEFUL My App ‣Storedata locally ‣Separate UI and network ‣Queue requests
  • 7.
  • 8.
  • 9.
  • 10.
    TAKE AN ACTION PRESENTER SERVICE ACTIVITY EVENTBUS DATABASEREPOSITORY JOB NETWORK
  • 11.
  • 12.
    AGREE ON ‣Conflict Resolution ‣AddingTimestamps to Requests ‣Bundling Requests ‣Use of Status Fields
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
    REALM ▸Easy to SetUp ▸Faster than ORMs ▸Has a Fluent API
  • 18.
    REALM - MODELCLASS @RealmClass
 public class Place implements RealmModel
 {
 }
  • 19.
    REALM - MODELCLASS @RealmClass
 public class Place implements RealmModel
 {
 @PrimaryKey
 private String localId; @Index
 private Long remoteId;
 private Gps location;
 … }
  • 20.
    REALM - MODELCLASS localId remoteId location 19444498-2a40… 1458260 65031f36-bde9… e85c9757-f546…
  • 21.
    REALM - ADDINGA RECORD public class PlaceRepository implements Repository<Place>
 {
 @Override
 public void add (final Place item)
 {
 
 } …
  • 22.
    REALM - ADDINGA 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.
    REALM - QUERYRESULT private static final String PLACE_ID = "id"; @Override
 public Place toResult (Realm realm)
 {
 return realm.where( Place.class )
 
 }
  • 24.
    REALM - QUERYRESULT private static final String PLACE_ID = "id"; @Override
 public Place toResult (Realm realm)
 {
 return realm.where( Place.class )
 .equalTo( PLACE_ID, placeId )
 .findFirst();
 }
  • 26.
  • 27.
  • 28.
    … WHEN YOUHAVE LOTS OF OBJECTS THAT ARE POTENTIAL EVENT SOURCES. Martin Fowler EVENTBUS
  • 29.
    EVENTBUS - SETUP @Provides
 @Singleton
 publicBus provideBus ()
 {
 return new Bus( EventBus.builder()
 .logNoSubscriberMessages( BuildConfig.DEBUG )
 .throwSubscriberException( false )
 .build() );
 }
  • 30.
    EVENTBUS - REGISTER publicclass LeadsMapPresenter
 {
 private final Bus bus;
 private LeadsMapView leadsMapView;
 
 @Inject
 LeadsMapPresenter (Bus bus)
 {
 this.bus = bus;
 }
 
 …
  • 31.
    EVENTBUS - REGISTER publicclass LeadsMapPresenter
 {
 public void attachView (LeadsMapView view)
 {
 this.leadsMapView = view;
 bus.register( this );
 }
  • 32.
    EVENTBUS - REGISTER publicclass LeadsMapPresenter
 {
 public void attachView (LeadsMapView view)
 {
 this.leadsMapView = view;
 bus.register( this );
 } public void detachView ()
 {
 this.leadsMapView = null;
 bus.unregister( this );
 }
  • 33.
    TAKE AN ACTION PRESENTER SERVICE ACTIVITY EVENTBUS DATABASEREPOSITORY JOB NETWORK
  • 34.
    EVENTBUS - POSTEVENT api.submitPlace( place ).enqueue( new Callback<Place>()
 {
 … JOB
  • 35.
    EVENTBUS - POSTEVENT api.submitPlace( place ).enqueue( new Callback<Place>()
 {
 @Override
 public void onResponse (Call<Place> call, Response<Place> response)
 {
 if ( response.isSuccessful() )
 {
 
 } … JOB
  • 36.
    EVENTBUS - POSTEVENT 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
  • 37.
    EVENTBUS - RETRIEVEEVENT public class LeadsMapPresenter
 { 
 @Subscribe
 public void onPlaceSubmissionSuccess (PlaceSubmissionSuccessEvent event)
 {
 leadsMapView.displaySuccessMessage( PLACE_SUCCESS ); } … PRESENTER
  • 39.
  • 40.
  • 41.
    ANDROID JOB ‣Alarm Manager ‣JobScheduler ‣GCM Network Manager
  • 42.
  • 43.
    ANDROID JOB JOB MANAGER PROVIDER JOB JOBCREATOR PROVIDER JOB PROVIDER JOB
  • 44.
    ANDROID JOB JOB MANAGER PROVIDER JOB JOBCREATOR PROVIDER JOB PROVIDER JOB JOB REQUEST
  • 45.
    ANDROID JOB JOB MANAGER PROVIDER JOB JOBCREATOR PROVIDER JOB PROVIDER JOB JOB REQUEST JOB
  • 46.
    ANDROID JOB -CREATOR @Singleton
 public class ConfettiJobCreator implements JobCreator
 {
 @Inject
 Map<String, Provider<Job>> jobs;
 
 @Override
 public Job create (String tag)
 {
 
 }
 }
  • 47.
    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();
 }
 }
  • 48.
    ANDROID JOB -JOB EXECUTION @Override
 protected Result onRunJob (final Params params)
 {
 
 } JOB
  • 49.
    ANDROID JOB -JOB EXECUTION @Override
 protected Result onRunJob (final Params params)
 {
 PersistableBundleCompat extras = params.getExtras();
 String placeId = extras.getString( PARAM_PLACE_ID );
 
 
 } JOB
  • 50.
    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
  • 51.
  • 52.
    ANDROID JOB -SCHEDULING public static JobRequest buildJobRequest (String placeId)
 {
 PersistableBundleCompat extras = new PersistableBundleCompat();
 extras.putString( PARAM_PLACE_ID, placeId );
 
 
 }
  • 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 )
 
 }
  • 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 )
 
 }
  • 55.
    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 )
 
 }
  • 56.
    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();
 }
  • 57.
    ANDROID JOB -SCHEDULING jobManager.schedule( );
  • 58.
    ANDROID JOB -SCHEDULING jobManager.schedule( SendPlaceRequestJob.buildJobRequest( id ) ); JOB REQUEST JOB QUEUE
  • 59.
  • 60.
  • 65.
  • 66.