webinar - developing with couchbase lite on android
DESCRIPTION
Learn how to develop with Couchbase Lite for Android in this technical lecture led by Traun Leyden, lead Couchbase Lite Android developer. The session will include a look into the Native API and a walkthrough of a demo app. In this webinar you will see: A 5-minute recap of the Couchbase Lite architecture An overview of the Couchbase Lite iOS API Demo - Grocery Sync Demo App with new Android Native API Code walkthrough of Grocery Sync Demo AppTRANSCRIPT
Webinar Overview
• Understand the overall problem
• Overview of the JSON Anywhere soluDon • Couchbase Lite for Android This will assume some familiarity with Android, but hopefully not too
much
• Where to go from here!
Bird’s eye view of the Problem
• A typical app today is extremely data rich
• This data is largely unstructured • RepresenDng this in JSON is a natural fit, but .. • How do you build a mobile app using tools like SQLite or
Core Data while the data is stored in JSON?!
Couchbase Lite on Android
• InstallaDon and setup walk-‐through • Demo of Grocery Sync sample app
• Tour of Grocery Sync code with digressions about the API and the document model and querying
• Beyond Grocery Sync!
InstallaDon setup and walk-‐through
• Clone the GrocerySync-‐Android repository • Import into Android Studio
• Build & Run!
1. Clone the GrocerySync-‐Android repository
$ git clone -‐-‐recursive [email protected]:couchbaselabs/GrocerySync-‐Android.git
$ cd GrocerySync-‐Android
$ git checkout native_api
$ git submodule init && git submodule update
This will be easier after our next release!
Import into Android Studio
Choose Import Project ..
Import into Android Studio Choose the folder
you did the git clone into
Import into Android Studio Check auto-‐import and
use default gradle wrapper
Build and Run
Hit the Run buTon to launch
the app
Build and Run
The emulator will launch running Grocery Sync
Live Demo
Demo Recap • We were able to create, update, and delete records
• Verified that documents were Sync’d to Couchbase Server by inspecDng the Sync Gateway via curl
A Tour of the Code and the API
IniDalizaDon MainAc=vity#startCBLite()!
// Initialize Couchbase Lite and find/create my database: protected void startCBLite() throws CBLiteException { manager = new CBLManager(getApplicationContext().getFilesDir());
CBLManager
Database “otherdb” CBLDatabase
“db”
CBLDocument “doc3”
CBLDocument
“doc2”
Document “doc1”
CBLDocument “doc1” !
{ ! “text”: “Shaving Cream”, ! “created”: “2013-10-08”, ! “check”: false !}
thumb.jpg
Manager, Databases, Documents
Manager
• CollecDon of named databases
• No known use cases for mulDple Managers on Android Essen=ally an ar=fact of the iOS port
• Manages database storage (local directory)
Database
• A Couchbase Lite database is a “real” database on not simply a wrapper for a cloud service
• Namespace for documents
• Contains views and their indexes • Contains validaDon funcDons • Source and target of replicaDon!
Document
• Has unique ID within its database • Contains arbitrary* JSON object *except keys that start with “_” are reserved There is no explicit, enforced schema Denormaliza=on is OK — use arrays or dic=onaries
• May contain binary aTachments Data blobs, can be large, tagged with MIME type
• Versioned Mul=-‐Version Concurrency Control (MVCC) Every update creates a revision ID (based on digest of contents) Revision ID history is stored Enables conflict resolu=on in sync’ing Very painful if you try to build this yourself!
View “completed”
Views & Queries
CBLDatabase “db”
CBLView “byDate”
function(doc) { ! emit(doc.created, ! doc.title); !} map function
key! value! docID!
“2013-‐03-‐12”! “taxes”! “doc17”!
“2013-‐09-‐30”! “call mom”! “doc62”!
“2013-‐10-‐17”! “cat food”! “doc82”!
“2013-‐10-‐17”! “tea bags”! “doc83”!
“2013-‐10-‐22”! “upgrade”! “doc90”!view index
CBLQuery }
Views
• Map/Reduce mechanism A standard method of indexing in NoSQL A view is similar to index in rela=onal database.
• App-‐defined map funcDon Called on every document Can emit arbitrary key/value pairs into the index
• OpDonal reduce funcDon Data aggrega=on / grouping
• FuncDons are registered as naDve callbacks Na=ve callbacks make sense for performance and to match the rest of the
app codebase!
CreaDng a Database View MainAc=vity#startCBLite()!
// Define a view with a map function that indexes to-‐do items by creation date: db = manager.getDatabase(DATABASE_NAME); CBLView view = db.getView(String.format("%s/%s", dDocName, byDateViewName)); view.setMap(new CBLMapFunction() { @Override public void map(Map<String, Object> document, CBLMapEmitFunction emitter) { Object createdAt = document.get("created_at"); if(createdAt != null) { emitter.emit(createdAt.toString(), document); } } }, "1.0");
Not a UIView — a database “view” is like an index.
Queries and LiveQueries
Queries
• Basic feature set Key ranges, offset/limit, reverse, group by key… No joins or fancy sor=ng but compound keys (and clever emits) allow for some tricks
• LiveQuery subclass Monitors a view for changes Can think of it as a “pub-‐sub” approach Register a callback that is triggered when the query changes !
Live Queries & UI
key! value! docID!
“2013-‐09-‐30”! “Pencil shavings”! “doc62”!
“2013-‐10-‐17”! “Mangos”! “doc82”!
“2013-‐10-‐17”! “second”! “doc83”!view index CBLLiveQuery
CBLQuery } data source
Refresh ListViewAda
pter
Driving the Table from a View Query MainAc=vity#startLiveQuery()!
liveQuery = view.createQuery().toLiveQuery(); liveQuery.addChangeListener(new CBLLiveQueryChangedFunction() { @Override public void onLiveQueryChanged(CBLQueryEnumerator queryEnumerator) { final List<CBLQueryRow> rows = getRowsFromQueryEnumerator(queryEnumerator); runOnUiThread(new Runnable() { @Override public void run() { .. update itemListView with new itemListViewAdapter } }); } }); liveQuery.start();
ListAdapter View objects GrocerySyncListAdapter#getView()!
TextView label = ((ViewHolder)itemView.getTag()).label; CBLQueryRow row = list.get(position); CBLDocument document = row.getDocument(); label.setText((String)document.getCurrentRevision().getProperty("text")); boolean checked = false; checked = ((Boolean) document.getCurrentRevision().getProperty(“check")).booleanValue(); ImageView icon = ((ViewHolder)itemView.getTag()).icon; if(checked) { icon.setImageResource(R.drawable.list_area___checkbox___checked); } else { icon.setImageResource(R.drawable.list_area___checkbox___unchecked); } return itemView;
Responding To Taps MainAc=vity#onItemClick()!
CBLQueryRow row = (CBLQueryRow) adapterView.getItemAtPosition(position); CBLDocument document = row.getDocument(); Map<String, Object> curProperties = document.getProperties(); Map<String, Object> newProperties = new HashMap<String, Object>(); newProperties.putAll(curProperties); boolean checked = ((Boolean) newProperties.get("check")).booleanValue(); newProperties.put("check", !checked); document.putProperties(newProperties);
Adding New Items MainAc=vity#createGroceryItem()!
SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-‐MM-‐dd'T'HH:mm:ss.SSS'Z'"); UUID uuid = UUID.randomUUID(); Calendar calendar = GregorianCalendar.getInstance(); long currentTime = calendar.getTimeInMillis(); String currentTimeString = dateFormatter.format(calendar.getTime()); String id = currentTime + "-‐" + uuid.toString(); CBLDocument document = database.createDocument(); Map<String, Object> properties = new HashMap<String, Object>(); properties.put("_id", id); properties.put("text", text); properties.put("check", Boolean.FALSE); properties.put("created_at", currentTimeString); document.putProperties(properties); return document;
DeleDng Items MainAc=vity#onItemLongClick()!
CBLQueryRow row = (CBLQueryRow) adapterView.getItemAtPosition(position); final CBLDocument clickedDocument = row.getDocument(); clickedDocument.delete();
OK, But how does Sync work?
ReplicaDon
Database “db”
ReplicaDon Dir: push Remote: hep://server/db Auth: <token> ReplicaDon
Dir: pull Remote: hep://server/db Auth: <token>
CreaDng ReplicaDons MainAc=vity#startSync()!
URL syncUrl; try { syncUrl = new URL(SYNC_URL); } catch (MalformedURLException e) { throw new RuntimeException(e); } CBLReplicator pullReplication = database.getPullReplication(syncUrl); pullReplication.setContinuous(true); CBLReplicator pushReplication = database.getPushReplication(syncUrl); pushReplication.setContinuous(true); pullReplication.addObserver(this); pushReplication.addObserver(this); pullReplication.start(); pushReplication.start();
ReplicaDon
• Each ReplicaDon is one-‐direcDonal (push or pull) • ReplicaDons can be one-‐shot, conDnuous or persistent One-‐shot: Stops when complete. Con=nuous: Keeps monitoring changes =ll app quits Persistent: Automa=cally starts again on next relaunch
• Replicator runs in a background thread It detects online/offline, handles connec=on errors, retries… You just see document-‐changed or query-‐changed no=fica=ons.
• Progress is observable through Observer/Observable (to be changed)!
Monitoring ReplicaDons MainAc=vity#update()!
public void update(Observable observable, Object o) { CBLReplicator replicator = (CBLReplicator) observable; if (!replicator.isRunning()) { String msg = String.format("Replicator %s not running", replicator); Log.d(TAG, msg); } else { int processed = replicator.getChangesProcessed(); int total = replicator.getChangesTotal(); String msg = String.format("Replicator processed %d / %d", processed, total); Log.d(TAG, msg); } }
Beyond Grocery Sync
Models
• Currently missing from the Android NaDve API
• The plan is that models will be inspired by ORMLite Will use annota=on based approach rather than subclassing
RepresenDng Document Types
• There are no tables to separate different record types! Conven=on is to use a “type” property
• Map funcDons can pick out docs with the right type if (doc.type == “item”) emit(doc.created, doc.text);!
Slicing up hierarchical data From ToDoLite project!
TODO Lists!
Chores!
Errands!
..!
..!
…!
Chores!
Do Laundry!
Shave Yak!
..!
..!
…!
Errands!
Pickup GTA5 at best buy!
Wash car!
..!
..!
…!
Slicing up hierarchical data From ToDoLite project!
view.setMap(new CBLMapFunction() { public void map(Map<String, Object> document, CBLMapEmitFunction emitter) { if (document.get("type").equals("todo_list")) { Object createdAt = document.get("created_at"); String listId = (String) document.get("list_id"); Object[] key = new Object[] { createdAt, listId } emitter.emit(key, document); } } }, "1.0");
key! docID!
[“errands”, “2013-‐09-‐30”]! “doc82”!
[“errands, “2013-‐06-‐02”]! “doc62”!
[“chores”, “2013-‐10-‐17”]! “doc83”!
[“chores”, “2013-‐10-‐28”]! “doc90”!
…! …!
Slicing up hierarchical data: Querying From ToDoLite project!
CBLQuery query = view.createQuery(); query.setDescending(true); String myListId = “chores”; query.setStartKey(new Object[] { myListId, new HashMap() }); query.setEndKey(new Object[] { myListId });
{
key! docID!
[“errands”, “2013-‐09-‐30”]! “doc82”!
[“errands, “2013-‐06-‐02”]! “doc62”!
[“chores”, “2013-‐10-‐17”]! “doc83”!
[“chores”, “2013-‐10-‐28”]! “doc90”!
…! …!
Where to go from here
Couchbase Cloud
Couchbase Cloud gives you this in a few clicks
• To create a connected Mobile App, you will need a backend
• For producDon deployment, you will want to use Sync Gateway + Couchbase Server
• But for development, Couchbase Cloud is the quickest path
How to get up and running in 5 mins
• Signup for Couchbase Cloud The fastest way to experiment with Couchbase Lite Signup at hep://console.couchbasecloud.com
• Download the GrocerySync example from github
• Follow the instrucDons in this webinar
Join the Couchbase Lite community
• Official homepage mobile.couchbase.com
• Google Group heps://groups.google.com/forum/?fromgroups#!forum/mobile-‐
couchbase
• Github Repository heps://github.com/couchbase/couchbase-‐lite-‐android
Thanks for watching!