No internet? No Problem!
-
Upload
annyce-davis -
Category
Technology
-
view
763 -
download
0
Transcript of No internet? No Problem!
NO INTERNET? NO PROBLEM!
OFF GRID ELECTRIC
AGENDA
ARCHITECTURELIBRARIES
CHALLENGES
CHALLENGES
3G COVERAGE IN ARUSHA, TZ
4G LTE COVERAGE IN WASHINGTON, DC
DON’T ASSUME
BE RESILIENT
ARCHITECTURE
BE USEFUL
Our App ‣Store data locally ‣Separate UI and network ‣Queue requests
MVP(Model View Presenter)
EVENT BUS/RXJAVA
DATABASE
REPOSITORY
REPOSITORY
ACTIVITY
FRAGMENT
JOBS
PRESENTER
PRESENTER
SERVICE
DOWNLOAD ALL THE THINGS
DISPLAY A VIEW
Is pre-populated data needed?
EVENT BUS/RXJAVA
FRAGMENT PRESENTER SERVICE REPOSITORY DATABASE
TAKE AN ACTION
Is the Network Available?
EVENT BUS/RXJAVA
FRAGMENT PRESENTER SERVICE REPOSITORY DATABASE
JOB
APP SERVER
CONFLICT RESOLUTION
REQUEST TIMESTAMPS
STATUS FIELDS
ACTION STATE
BUNDLING REQUESTS
/users/commissions /users/info /users/roles
BUNDLING REQUESTS
/users/commissions
/users/info
/users/roles/users/me
EVENT BUS/RXJAVA
DATABASE
REPOSITORY
REPOSITORY
ACTIVITY
FRAGMENT
JOBS
PRESENTER
PRESENTER
SERVICE
LIBRARIES
WHAT WE USE
‣Realm ‣EventBus ‣Android Job
STORE DATA LOCALLY
REALM - MODEL CLASS
@RealmClasspublic class Place implements RealmModel {
}
REALM - MODEL CLASS
@RealmClasspublic 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.insertOrUpdate(item); }); realm.close(); }
…
REALM - QUERY RESULT
@Overridepublic Place toResult (Realm realm) { return realm.where(Place.class)
}
REALM - QUERY RESULT
@Overridepublic Place toResult (Realm realm) { return realm.where(Place.class) .equalTo(PLACE_ID, placeId) .findFirst();}
REALM - QUERY RESULT
@Overridepublic Place toResult (Realm realm) { return realm.where(Place.class) .equalTo(PLACE_ID, placeId) .findFirst();}
SEPARATE UI AND NETWORK
EVENTBUS - SETUP
@Provides @Singletonpublic 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) { … }
public void detachView () { this.leadsMapView = null; bus.unregister(this); }
EVENT BUS/RXJAVA
FRAGMENT PRESENTER SERVICE REPOSITORY DATABASE
JOB
EVENTBUS - POST EVENT
api.submitPlace(place).enqueue(new Callback<Place>() {
…JOB
EVENTBUS - POST EVENT
api.submitPlace(place).enqueue(new Callback<Place>() { public void onResponse (…, Response<Place> response) { if (response.isSuccessful()) { }
…
JOB
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
EVENTBUS - RETRIEVE EVENT
public class LeadsMapPresenter { @Subscribe public void handle (PlaceSubmissionSuccessEvent event) { leadsMapView.displaySuccessMessage(); }
…
PRESENTER
EVENTBUS - SEND STATUS BAR NOTIFICATION
SERVICE
EVENTBUS - SEND STATUS BAR NOTIFICATION
SERVICE
@Subscribe(priority = 1) open fun onEvent(event: PaymentSuccessEvent) {
}
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) }
QUEUE REQUESTS
ANDROID JOB
ANDROID JOB
‣Alarm Manager
‣Job Scheduler
‣GCM Network Manager
ANDROID JOB - COMPARISON
Library Min API Google Play?
JobScheduler 21 No
Firebase Job Dispatcher 9 Yes
Android Job 14 No
ANDROID JOB
JOB MANAGER
JOB CREATOR
ANDROID JOB
JOB MANAGERPROVIDER
JOB
JOB CREATOR
PROVIDER
JOB
PROVIDER
JOB
ANDROID JOB
JOB MANAGERPROVIDER
JOB
JOB CREATOR
PROVIDER
JOB
PROVIDER
JOB
JOB REQUEST
ANDROID JOB
JOB MANAGERPROVIDER
JOB
JOB CREATOR
PROVIDER
JOB
PROVIDER
JOB
JOB REQUEST
JOB
ANDROID JOB - CREATOR
public class ConfettiJobCreator implements JobCreator { Map<String, Provider<Job>> jobs; public Job create (String tag) {
}}
ANDROID JOB - CREATOR
public Job create (String tag) { Provider<Job> jobProvider = jobs.get(tag); return jobProvider.get(); }
ANDROID JOB
JOB MANAGERPROVIDER
JOB
JOB CREATOR
JOB REQUEST
JOB
ANDROID JOB - JOB EXECUTION
protected Result onRunJob (final Params params) {
}
JOB
ANDROID JOB - JOB EXECUTION
protected Result onRunJob (final Params params) { PersistableBundleCompat extras = params.getExtras(); String placeId = extras.getString(PARAM_PLACE_ID);
}
JOB
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
ANDROID JOB
JOB MANAGERPROVIDER
JOB
JOB CREATOR
JOB REQUEST
JOB
ANDROID JOB - SCHEDULING
public static JobRequest get (String placeId) { PersistableBundleCompat extras = …; extras.putString(PARAM_PLACE_ID, placeId);
}
ANDROID JOB - SCHEDULING
public static JobRequest get (String placeId) { PersistableBundleCompat extras = …; extras.putString(PARAM_PLACE_ID, placeId); return new JobRequest.Builder(SendPlaceRequestJob.JOB_TAG)
}
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)
}
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) }
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();}
if has network connection attempt to submit request
if has network connection attempt to submit request
else queue request
ANDROID JOB - SCHEDULING
jobManager.schedule( );
ANDROID JOB - SCHEDULING
jobManager.schedule(SendPlaceRequestJob.buildJobRequest(id));
JOB REQUEST
JOB QUEUE
WHAT WE USE
‣Realm ‣EventBus ‣Android Job
WHAT’S NEXT?
‣SMS Fallback ‣Async Code Paths ‣Push Notifications