ezhome is a new breed of gardening company that’s revolutionizing the way gardening is done. We’re using technology to help gardeners with their work, enable better communication, and vastly improve the customer experience.
@ezhomeinc
Gardener App Capabilities
Real-time database
Coordinators & Monitoring team
More...
Easy & Secure authentication
More...
Offline gardener actions
Offline photos uploading
Share state
More...
Real-time Feature Toggle
Firebase Database
• Cloud-hosted NoSQL database• Real-time - Non HTTP• Data stored as JSON• Offline • Accessible by different devices• Security rules• Multiple Authentication Providers
Firebase Authentication
Plan Data Structure
Need to take care how the data:• is going to be saved• is going to be retrieved
{ "users": { "alovelace": { "name": "Ada Lovelace", "contacts": { "ghopper": true }, }, "ghopper": { }, "eclarke": { } }}
Plan Nested Data Structure
NoSQL databases does not allow JOINS. The solution is to use embedded data with nested data structure. Firebase allows nesting data up to 32 levels deep
{ "chats": { "one": { "title": "Historical Tech Pioneers", "messages": { "m1": { "sender": "ghopper", "message": "Test text." }, "m2": { }, } }, "two": { } }}
Avoid nesting data
When you fetch data at a location in firebase, you also retrieve all of its child nodes.
ExampleI want show the conversation titles from chats:
databaseReference.child("chats");
More... Fatal Exception: java.lang.RuntimeException: Uncaught exception in Firebase runloop. Please report to [email protected]
at com.firebase.client.android.AndroidPlatform$1$1.run(AndroidPlatform.java:59)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5057)
at java.lang.reflect.Method.invokeNative(Method.java)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(NativeStart.java)
Caused by java.lang.OutOfMemoryError: [memory exhausted] at dalvik.system.NativeStart.main(NativeStart.java)
More...
Flatten data structure FTW
{ "chats": { "one": { "title": "Historical Tech Pioneers", "lastMessage": "ghopper: Relay malfunction found. Cause: moth.", "timestamp": 1459361875666 }, "two": {}, "three": {} }}
{ "members": { "one": { "ghopper": true, "alovelace": true, "eclarke": true }, "two": { }, "three": { } }}
More...{ "messages": { "one": { "m1": { "name": "eclarke", "message": "The relay seems to be malfunctioning.", "timestamp": 1459361875337 }, "m2": { ... }, "m3": { ... } }, "two": { ... }, "three": { ... } }}
Nested VS Flatten
Flatten Nested Flatten
Firebase usage FirebaseDatabase.getInstance().setPersistenceEnabled(true);
DatabaseReference postsRef = FirebaseDatabase.getInstance().getReference("posts");ValueEventListener postListener = new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { // Get Post object and use the values to update the UI Post post = dataSnapshot.getValue(Post.class); }
@Override public void onCancelled(DatabaseError databaseError) { // Getting Post failed, log a message Log.w(TAG, "loadPost:onCancelled", databaseError.toException()); }};postsRef.addValueEventListener(postListener);
Keep in mind...
• Synchronizes and stores a local copy of the data for active listeners
• You can keep the data fresh even if the reference has no active listeners (but be careful)
DatabaseReference postsRef = FirebaseDatabase.getInstance().getReference("posts");postsRef.keepSynced(true);
Gardener App Architecture + RxJava
Inspired by@fernando_cejas of
SoundCloud
RxFirebase by ezhome
RxJava wrapper on top of Firebase. The library includes:• RxFirebaseAuth• RxFirebaseDatabase
https://github.com/ezhome/Android-RxFirebase
RxFirebaseAuth//Observes sign-in in Firebase with Auth Providers
Observable<FirebaseUser> observeSignIn(final AuthCredential authCredential);
//Observes sign-in in Firebase with Custom token
Observable<FirebaseUser> observeSignIn(final String token);
//Observes the sign-out
Observable<Boolean> observeSignOut();
//Observes the authentication state
Observable<FirebaseUser> observeAuthState();
//Observes sign-in in Firebase with Auth Providers
Observable<FirebaseUser> observeSignIn(final AuthCredential authCredential);
//Observes sign-in in Firebase with Custom token
Observable<FirebaseUser> observeSignIn(final String token);
//Observes the sign-out
Observable<Boolean> observeSignOut();
//Observes the authentication state
Observable<FirebaseUser> observeAuthState();
RxFirebaseAuth
//Observes sign-in in Firebase with Auth Providers
Observable<FirebaseUser> observeSignIn(final AuthCredential authCredential);
//Observes sign-in in Firebase with Custom token
Observable<FirebaseUser> observeSignIn(final String token);
//Observes the sign-out
Observable<Boolean> observeSignOut();
//Observes the authentication state
Observable<FirebaseUser> observeAuthState();
RxFirebaseAuth
//Observes sign-in in Firebase with Auth Providers
Observable<FirebaseUser> observeSignIn(final AuthCredential authCredential);
//Observes sign-in in Firebase with Custom token
Observable<FirebaseUser> observeSignIn(final String token);
//Observes the sign-out
Observable<Boolean> observeSignOut();
//Observes the authentication state
Observable<FirebaseUser> observeAuthState();
RxFirebaseAuth
//Observes sign-in in Firebase with Auth Providers
Observable<FirebaseUser> observeSignIn(final AuthCredential authCredential);
//Observes sign-in in Firebase with Custom token
Observable<FirebaseUser> observeSignIn(final String token);
//Observes the sign-out
Observable<Boolean> observeSignOut();
//Observes the authentication state
Observable<FirebaseUser> observeAuthState();
RxFirebaseAuth
//Observes saving data and returns the generated key
Observable<String> observeValuePush(final DatabaseReference firebaseRef, final Object object);
//Observes retrieve data and emits when data changed in a specific Firebase Query
Observable<DataSnapshot> observeValueEvent(final Query firebaseRef);
//Observes new additions in a specific Firebase Query
Observable<FirebaseChildEvent> observeChildAdded(final Query firebaseRef);
more…….
RxFirebaseDatabase
//Observes saving data and returns the generated key
Observable<String> observeValuePush(final DatabaseReference firebaseRef, final Object object);
//Observes retrieve data and emits when data changed in a specific Firebase Query
Observable<DataSnapshot> observeValueEvent(final Query firebaseRef);
//Observes new additions in a specific Firebase Query
Observable<FirebaseChildEvent> observeChildAdded(final Query firebaseRef);
more…….
RxFirebaseDatabase
//Observes saving data and returns the generated key
Observable<String> observeValuePush(final DatabaseReference firebaseRef, final Object object);
//Observes retrieve data and emits when data changed in a specific Firebase Query
Observable<DataSnapshot> observeValueEvent(final Query firebaseRef);
//Observes new additions in a specific Firebase Query
Observable<FirebaseChildEvent> observeChildAdded(final Query firebaseRef);
more…….
RxFirebaseDatabase
//Observes saving data and returns the generated key
Observable<String> observeValuePush(final DatabaseReference firebaseRef, final Object object);
//Observes retrieve data and emits when data changed in a specific Firebase Query
Observable<DataSnapshot> observeValueEvent(final Query firebaseRef);
//Observes new additions in a specific Firebase Query
Observable<FirebaseChildEvent> observeChildAdded(final Query firebaseRef);
more…….
RxFirebaseDatabase
RxFirebaseDatabasepublic Observable<DataSnapshot> observeValueEvent(final Query firebaseRef) { return Observable.create(new Observable.OnSubscribe<DataSnapshot>() { @Override public void call(final Subscriber<? super DataSnapshot> subscriber) { final ValueEventListener listener = firebaseRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { subscriber.onNext(dataSnapshot); }
@Override public void onCancelled(DatabaseError error) { FirebaseDatabaseErrorFactory.buildError(subscriber, error); }); subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { firebaseRef.removeEventListener(listener); } })); } });}
RxFirebaseDatabase public Observable<DataSnapshot> observeValueEvent(final Query firebaseRef) { return Observable.create(new Observable.OnSubscribe<DataSnapshot>() { @Override public void call(final Subscriber<? super DataSnapshot> subscriber) { final ValueEventListener listener = firebaseRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { subscriber.onNext(dataSnapshot); }
@Override public void onCancelled(DatabaseError error) { FirebaseDatabaseErrorFactory.buildError(subscriber, error); } }); subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { firebaseRef.removeEventListener(listener); } })); } });}
RxFirebaseDatabasepublic Observable<DataSnapshot> observeValueEvent(final Query firebaseRef) { return Observable.create(new Observable.OnSubscribe<DataSnapshot>() { @Override public void call(final Subscriber<? super DataSnapshot> subscriber) { final ValueEventListener listener = firebaseRef.addValueEventListener(new ValueEventListener() { @Override public void onDataChange(ataSnapshot dataSnapshot) { subscriber.onNext(dataSnapshot); }
@Override public void onCancelled(FirebaseError error) { attachErrorHandler(subscriber, error); } }); subscriber.add(Subscriptions.create(new Action0() { @Override public void call() { firebaseRef.removeEventListener(listener); } })); } });}
Why Observable.create()?
Observable.defer() : create a new Observable each time you get a subscriberObserable.create() : can use same function for each subscriber. More efficient!
OthersObservable.fromCallable() : not really works with exceptions as it’s expectedObservable.fromAsync() : quickly deprecated by RxJava (they were undecided)Observable.fromEmitter : seems final but still RxJava guys doing work (not tested yet by us)
Old VS New
Firebase v2.5.2
• No dependencies• No control over conflict resolution• UI browser super slow
Firebase by Google v9.x
• Google Play services• No control over conflict resolution• UI browser super fast BUT buggy• Remote config• Notifications• Dynamic links• Analytics• Crash reporting• Images upload
Pos & Cons
Pos
• Autoscaling built-in• Robust APIs - Cross platform• Realtime• Declarative Security Rules model
Cons
• Storage format is in JSON, migration is hard to different system
• No reporting tools to analyze Firebase queries
• Need to build indexes manually• Data validation is hard• Costs
Is it production ready?
Questions
We are hiring!!!ezhome.com/careers
github.com/ezhomegithub.com/spirosoik
@spirosoik