SlideShare a Scribd company logo
Submit Search
Upload
Login
Signup
Droidcon ES '16 - How to fail going offline
Report
Javier de Pedro López
Follow
Android Developer @ MOBGEN at MOBGEN
Jul. 16, 2016
•
0 likes
•
654 views
1
of
48
Droidcon ES '16 - How to fail going offline
Jul. 16, 2016
•
0 likes
•
654 views
Download Now
Download to read offline
Report
Software
Architecture proposal to work offline using mainly android components and a single dataflow
Javier de Pedro López
Follow
Android Developer @ MOBGEN at MOBGEN
Recommended
Enhance react app with patterns - part 1: higher order component
Yao Nien Chung
3.4K views
•
45 slides
Selenide alternative in Python - Introducing Selene [SeleniumCamp 2016]
Iakiv Kramarenko
10K views
•
93 slides
Web ui tests examples with selenide, nselene, selene & capybara
Iakiv Kramarenko
1.1K views
•
21 slides
軟體測試是在測試什麼?
Yao Nien Chung
3.3K views
•
42 slides
You do not need automation engineer - Sqa Days - 2015 - EN
Iakiv Kramarenko
1.4K views
•
85 slides
KISS Automation.py
Iakiv Kramarenko
449 views
•
68 slides
More Related Content
What's hot
Unit Testing for Great Justice
Domenic Denicola
2.1K views
•
67 slides
Dependency Injection
ColdFusionConference
248 views
•
62 slides
Bowtie: Interactive Dashboards
Jacques Kvam
621 views
•
165 slides
AngularJS Unit Test
Chiew Carol
871 views
•
47 slides
Testing in AngularJS
Peter Drinnan
5.7K views
•
17 slides
Easy automation.py
Iakiv Kramarenko
506 views
•
82 slides
What's hot
(20)
Unit Testing for Great Justice
Domenic Denicola
•
2.1K views
Dependency Injection
ColdFusionConference
•
248 views
Bowtie: Interactive Dashboards
Jacques Kvam
•
621 views
AngularJS Unit Test
Chiew Carol
•
871 views
Testing in AngularJS
Peter Drinnan
•
5.7K views
Easy automation.py
Iakiv Kramarenko
•
506 views
It's a Kind of Magic: Under the Covers of Spring Boot
VMware Tanzu
•
1.8K views
WuKong - Framework for Integrated Test
Summer Lu
•
662 views
Three Simple Chords of Alternative PageObjects and Hardcore of LoadableCompon...
Iakiv Kramarenko
•
3.5K views
Kiss PageObjects [01-2017]
Iakiv Kramarenko
•
5.9K views
Codeception presentation
Andrei Burian
•
1.5K views
Java Libraries You Can't Afford to Miss
Andres Almiray
•
3.9K views
Jumping Into WordPress Plugin Programming
Dougal Campbell
•
23.7K views
learning react
Eueung Mulyana
•
256 views
Your code are my tests
Michelangelo van Dam
•
2.6K views
Selenide Alternative in Practice - Implementation & Lessons learned [Selenium...
Iakiv Kramarenko
•
3.2K views
Controller Testing: You're Doing It Wrong
johnnygroundwork
•
1K views
SymfonyCon Berlin 2016 Jenkins Deployment Pipelines
cpsitgmbh
•
5.8K views
The Screenplay Pattern: Better Interactions for Better Automation
Applitools
•
2.6K views
Getting Started with Maven and Cucumber in Eclipse
Tom Arend
•
468 views
Viewers also liked
World-Class Testing Development Pipeline for Android
Pedro Vicente Gómez Sánchez
9.3K views
•
61 slides
Profile.e (yuki sato)0529
Sato Yuki
517 views
•
4 slides
Tarea 1 e busines
pilarguerra08
412 views
•
12 slides
Curso de Alta Formación en Marketing para Empresas de Servicios
Unibo
655 views
•
10 slides
Brand boost by wildtangent
WildTangent
837 views
•
15 slides
Update on the ELIXIR UK node by Chris Ponting
ELIXIR UK
639 views
•
30 slides
Viewers also liked
(20)
World-Class Testing Development Pipeline for Android
Pedro Vicente Gómez Sánchez
•
9.3K views
Profile.e (yuki sato)0529
Sato Yuki
•
517 views
Tarea 1 e busines
pilarguerra08
•
412 views
Curso de Alta Formación en Marketing para Empresas de Servicios
Unibo
•
655 views
Brand boost by wildtangent
WildTangent
•
837 views
Update on the ELIXIR UK node by Chris Ponting
ELIXIR UK
•
639 views
S&B Parking Operators
SB Ibérica
•
544 views
fluke presentation voicemail on email – cisco
Fluke Infotech
•
368 views
(Self) Publishing
Vlad Micu
•
836 views
CompuSystems Dashboards
CompuSystems, Inc.
•
244 views
Examen trimestra diego reyes
Diego Reyes
•
129 views
2012 Email Evolution Sneak Peak Webinar: Part 1
Vivastream
•
558 views
Servicio Eiden : Opinan nuestros clientes
Santiago Trevisán
•
428 views
Status Www Undersøgelse Udenpix
Elisabeth Tejlmand
•
354 views
Da schau her! Augenfreundliche Gestaltung von e-Learning Kursen
Gergely Rakoczi
•
291 views
Parroquia de San Pedro y San Pablo (Cabanillas del Campo)
Javier Lozano
•
176 views
Estudio earcas
SociosaniTec
•
1.3K views
Dios es mi Guía
adeni11
•
301 views
Ejercicio de dinamica
Jacinto Matom Gallego
•
3.4K views
1 year with ROM on production
Oskar Szrajer
•
1K views
Similar to Droidcon ES '16 - How to fail going offline
My way to clean android - Android day salamanca edition
Christian Panadero
5.2K views
•
35 slides
Building Large Scale PHP Web Applications with Laravel 4
Darwin Biler
14.8K views
•
26 slides
From Legacy to Hexagonal (An Unexpected Android Journey)
Jose Manuel Pereira Garcia
3.8K views
•
87 slides
My way to clean android (EN) - Android day salamanca edition
Christian Panadero
3.1K views
•
35 slides
Intro To JavaScript Unit Testing - Ran Mizrahi
Ran Mizrahi
2.4K views
•
31 slides
All a flutter about Flutter.io
Steven Cooper
529 views
•
63 slides
Similar to Droidcon ES '16 - How to fail going offline
(20)
My way to clean android - Android day salamanca edition
Christian Panadero
•
5.2K views
Building Large Scale PHP Web Applications with Laravel 4
Darwin Biler
•
14.8K views
From Legacy to Hexagonal (An Unexpected Android Journey)
Jose Manuel Pereira Garcia
•
3.8K views
My way to clean android (EN) - Android day salamanca edition
Christian Panadero
•
3.1K views
Intro To JavaScript Unit Testing - Ran Mizrahi
Ran Mizrahi
•
2.4K views
All a flutter about Flutter.io
Steven Cooper
•
529 views
Adding a modern twist to legacy web applications
Jeff Durta
•
345 views
Javascript first-class citizenery
toddbr
•
1.2K views
Net conf BG xamarin lecture
Tsvyatko Konov
•
85 views
ActiveWeb: Chicago Java User Group Presentation
ipolevoy
•
2K views
Cleaning your architecture with android architecture components
Debora Gomez Bertoli
•
455 views
HTML5 for the Silverlight Guy
David Padbury
•
2.5K views
How to code to code less
Anton Novikau
•
482 views
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Victor Rentea
•
1.6K views
Testing in android
jtrindade
•
300 views
softshake 2014 - Java EE
Alexis Hassler
•
1.7K views
Mobile Developers Talks: Delve Mobile
Konstantin Loginov
•
534 views
2008 - TechDays PT: WCF, JSON and AJAX for performance and manageability
Daniel Fisher
•
379 views
Why you should be using the shiny new C# 6.0 features now!
Eric Phan
•
298 views
JS Class 2016
Yves-Emmanuel Jutard
•
1.1K views
Recently uploaded
Empowering Advanced Users: Extending OutSystems UI Framework with Openness an...
Bernardo Cardoso
28 views
•
41 slides
Transform Your Digital Reality with Metaverse Development Solutions
Flexsin
46 views
•
10 slides
Game Dev Session 01.pdf
AbelPhilipJoseph
5 views
•
27 slides
Webinar - MariaDB Temporal Tables: a demonstration
Federico Razzoli
23 views
•
32 slides
KaseSync: Revolutionizing Support Experiences With Community-CRM Integration
Grazitti Interactive
20 views
•
8 slides
Kubernetes with Cilium in AWS - Experience Report!
QAware GmbH
14 views
•
17 slides
Recently uploaded
(20)
Empowering Advanced Users: Extending OutSystems UI Framework with Openness an...
Bernardo Cardoso
•
28 views
Transform Your Digital Reality with Metaverse Development Solutions
Flexsin
•
46 views
Game Dev Session 01.pdf
AbelPhilipJoseph
•
5 views
Webinar - MariaDB Temporal Tables: a demonstration
Federico Razzoli
•
23 views
KaseSync: Revolutionizing Support Experiences With Community-CRM Integration
Grazitti Interactive
•
20 views
Kubernetes with Cilium in AWS - Experience Report!
QAware GmbH
•
14 views
Analyzing Dart Language with Pharo: Report and early results
ESUG
•
12 views
Melbourne MUG - September 2023
JayJiang19
•
43 views
Creating Unit Tests Using Genetic Programming
ESUG
•
10 views
OutSystems Security Specialization - Study Help Deck
Fábio Godinho
•
29 views
Dido_Grigorov_Zurich_2020.pdf
PlamenaDzharadat
•
10 views
OpenAI GPT in Depth - Questions and Misconceptions
Ivo Andreev
•
11 views
Improving Performance Through Object Lifetime Profiling: the DataFrame Case
ESUG
•
12 views
From Shopify to WooCommerce Comparing E-commerce Platforms for Small Business...
WebConnect Pvt Ltd
•
6 views
Ecological Impact of Native vs. Cross-Platform Mobile Apps: a Preliminary Study
Olivier Le Goaër
•
10 views
Test Automation at Scale: Lessons from Top-Performing Distributed Teams
Applitools
•
5 views
Why Should You Choose a Personal Trainer over Group Gym Classes?
Neighborhood Trainer
•
14 views
Pharo: a reflective language A first systematic analysis of reflective APIs
ESUG
•
5 views
Fuzzing for CPS Mutation Testing
Lionel Briand
•
6 views
Software Bill of Materials and the Vulnerability Exploitability eXchange
Petar Radanliev
•
8 views
Droidcon ES '16 - How to fail going offline
1.
HOWTOFAIL GOING OFFLINE @droidpl javier.pedro@mobgen.com javierdepedrolopez@gmail.com
2.
JavierdePedroLópez StrengthsExperience SeniorAndroidDeveloper 3Yearsprofessionally 2Differentcompanies Freetime architecture DesignPatterns Gradle Codequality @droidpl javier.pedro@mobgen.com javierdepedrolopez@gmail.com
3.
Thistalk Try & try
& fail Architecture proposal Use case Show me the code
4.
TRY&TRY&FAIL Section 1
5.
Try&try&fail Pojos Services SyncAdapters WhyIdidinvestigate? • I work
on an SDK similar to firebase • Should work offline • Should have a simple API • Should be reliable and user friendly WhyThismatters?
6.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? • Offline improves
user experience • No loading times (most of the cases) • App always accesible • Just like magic • Makes your app less error prone • Forces the developer to think more mobile • Say no to: “you always have a good internet connection”
7.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? Views/MVP/ Interactors Using POJOS Database Callback Network Callback Pojo Callback
8.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? Using POJOS • Action
cancellation • Lifecycle management • Single responsibility principle broken • Messy thread management • Hard to read code • Fast to implement
9.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? Views/MVP/ Interactors Using Services Service ResultReceiver/Binder Threading Database Sync Network Sync Pojo Callback
10.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? Problems using Services •
Action cancellation is still a pain • Easy to leak memory • Easy to split in many services • Export to other apps • Easier to handle threading • Possibility to have many processes • Simplified callback system
11.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? Views/ MVP/ Interactors Using Sync Adapters Network Sync Sync adapter Sync trigger Database (content provider) Sync Content observer
12.
Try&try&fail WhyThismatters? Pojos Services SyncAdapters WhyIdidinvestigate? Problems using Services •
Hard disconnection errors • Sync adapter documentation • Sync adapter configuration (many files) • Too linked to accounts • Uses system tools • Content change notifications • All the benefits from services
13.
ARCHITECTUREPROPOSAL Section 2
14.
ARCHITECTUREPROPOSAL ACTLOCALLY SYNCGLOBALLY
15.
LoadersArchitecture proposal Jobschedulers Alltogether Read Write Loaders Repositories Views/ MVP/ Interactors Loader inits provides data gets notified android lifecycle asks
for data Data source
16.
Loader statesArchitecture proposal Jobschedulers Alltogether Read Write Loaders Repositories Started Stopped
Reset Can load Can observe Can deliver Can load Can observe Can deliver Can load Can observe Can deliver Stop/Reset Start/Reset Start
17.
Job schedulersArchitecture proposal Jobschedulers Loaders Alltogether Read Write Repositories Pojo notifies notifies System Scheduler schedule job Preconditions check Sync Service trigger met Sync task run
task
18.
RepositoriesArchitecture proposal Jobschedulers Loaders Alltogether Read Write Repositories Data Repositorynotifies Database Repository Read Write Other sources Network repository Read Write
19.
Architecture proposal Jobschedulers Loaders Overall diagram Alltogether Read Write Repositories Data Repository Notifies Asks for data Loader Inits Provides data System Scheduler Schedule
sync Sync Service Sync task Conditions metRun task Changes data Read Write Read Write Views/ MVP/ Interactors Start App SDK write
20.
Architecture proposal Jobschedulers Loaders Alltogether Read Write Repositories SDKLoader Loader Loader Overall diagram App Layer Android SDK Entry Entry Entry Entry
21.
Architecture proposal Jobschedulers Loaders Read diagram Alltogether Read Write Repositories
22.
Architecture proposal Jobschedulers Loaders Write diagram Alltogether Read Write Repositories online
23.
USECASE Section 3
24.
Usecase Onlinesample Models offlineSample Articles and comments
25.
Usecase Onlinesample Models offlineSample
26.
Usecase Onlinesample Models offlineSample
27.
SHOWMETHECODE Section 4
28.
Classes PostActivity PostLoader PostRepository PostDAO CommentDAO PostService SyncServiceSynchronizeTask write
29.
PostActivity:init private PostRepository mRepository; private
Executor mExecutor; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mRepository = DemosApplication.instance() .demoSdk() .postRepository(); mExecutor = Executors.newSingleThreadExecutor(); startLoading(); PostLoader.init(getSupportLoaderManager(), this); } Views
30.
PostActivity:loaddata @Override public Loader<List<Post>> onCreateLoader(int
id, Bundle args) { return new PostLoader(this, DemosApplication.instance().demoSdk()); } @Override public void onLoadFinished(Loader<List<Post>> loader, List<Post> data) { stopLoading(); setAdapter(data); } @Override public void onLoaderReset(@NonNull Loader<List<Post>> loader) { //Do nothing } Views
31.
PostActivity:Refresh @Override public void onRefresh()
{ startLoading(); PostLoader.getLoader(getSupportLoaderManager()).forceLoad(); } Views
32.
PostActivity:Createpost @Override public void onPostCreated(@NonNull
final Post post) { startLoading(); mExecutor.execute(() -> { try { mRepository.localCreate(post); } catch (RepositoryException e) { showError(); } }); } Views
33.
PostActivity:Deletepost @Override public void onDeleted(@NonNull
final Post post) { startLoading(); mExecutor.execute(() -> { try { mRepository.localDelete(post); } catch (RepositoryException e) { showError(); } }); } Views
34.
postloader @Override public List<Post> loadInBackground()
{ setData(mDemoSdk.postRepository().posts()); return getData(); } @Override public void registerListener() { if (mObserver == null) { mObserver = SyncService.listenForUpdates(this); } } @Override public void unregisterListener() { if (mObserver != null) { SyncService.removeUpdateListener(this, mObserver); } } loader
35.
Postrepository:allposts @WorkerThread public List<Post> posts()
{ Response<List<Post>> postsResponse = mPostService.posts().execute(); if (postsResponse.isSuccessful()) { List<Post> posts = postsResponse.body(); mPostDao.deleteAll(); mPostDao.save(posts); } return mPostDao.posts(); } datasource
36.
Postrepository:Save @WorkerThread public void localCreate(@NonNull
Post post) { mPostDao.save(post); SyncService.triggerSync(mContext); } @WorkerThread public void remoteCreate(@NonNull Post post) { Response<Post> postResponse = mPostService.create(Post.builder(post) .internalId(null) .needsSync(false) .build()).execute(); if (postResponse.isSuccessful()) { mPostDao.save(Post.builder(postResponse.body()) .internalId(post.internalId()) .build()); } } datasource
37.
Postrepository:DELETE @WorkerThread public void localDelete(@NonNull
Post post) { long now = new Date().getTime(); if (post.isNew() && post.isStoredLocally()) { mPostDao.delete(post.internalId()); SyncService.notifyChange(mContext); } else { mPostDao.save(Post.builder(post) .deletedAt(now) .updatedAt(now) .needsSync(true).build()); SyncService.triggerSync(mContext); } } @WorkerThread public void remoteDelete(@NonNull Post post) { if (mPostService.deletePost(post.id()).execute().isSuccessful() && post.isStoredLocally()) { for (Comment comment : mCommentDao.comments(post.internalId())) { remoteDelete(comment); } mPostDao.delete(post.internalId()); } } datasource
38.
Syncservice:trigger public static void
triggerSync(@NonNull Context context) { SyncService.notifyChange(context); ComponentName component = new ComponentName(context, SyncService.class); JobInfo info = new JobInfo.Builder(SYNC_SERVICE_ID, component) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .build(); getScheduler(context).schedule(info); } sync
39.
Syncservice:Listenforupdates public static BroadcastReceiver
listenForUpdates(@NonNull Loader loader) { IntentFilter filter = new IntentFilter(CHANGE_SYNC_INTENT_ACTION); SyncServiceReceiver receiver = new SyncServiceReceiver(loader); LocalBroadcastManager.getInstance(loader.getContext()) .registerReceiver(receiver, filter); return receiver; } public static void removeUpdateListener(@NonNull Loader loader, @NonNull BroadcastReceiver observer) { LocalBroadcastManager.getInstance(loader.getContext()) .unregisterReceiver(observer); } public static void notifyChange(@NonNull Context context) { LocalBroadcastManager.getInstance(context).sendBroadcast(getCompletionIntent()); } sync
40.
Syncservice:DotheJOB @Override public boolean onStartJob(JobParameters
params) { DemoSdk sdk = DemoSdk.Factory.instance(); boolean willExecute = true; if (sdk != null) { mRunningSyncTask = new SynchronizeTask(sdk, this); mRunningSyncTask.execute(params); } else { willExecute = false; } return willExecute; } sync @Override public boolean onStopJob(JobParameters params) { boolean reschedule = false; if (mRunningSyncTask != null) { mRunningSyncTask.cancel(true); reschedule = true; } return reschedule; }
41.
Synctask:Sync @Override protected JobParameters doInBackground(JobParameters...
params) { syncPosts(); syncComments(); return params[0]; } sync private void syncPosts() { List<Post> posts = mSdk.postRepository().localPendingPosts(); for (Post post : posts) { if (post.isNew()) { mSdk.postRepository().remoteCreate(post); } else if (post.isDeleted()) { mSdk.postRepository().remoteDelete(post); } } }
42.
Synctask:NOTIFY @Override protected void onPostExecute(JobParameters
jobParameters) { super.onPostExecute(jobParameters); SyncService.notifyChange(mSyncService); mSyncService.jobFinished(jobParameters, mNeedsResync); } sync
43.
Andthereyougo!yourappworksoffline
44.
SOURCECodeavailable https://github.com/droidpl/offline-architecture @droidpl
45.
Conclusions Offline matters because UX matters It
is part of the architecture Not so much effort with the right choice Unique dataflow Don’t reinvent the wheel - Android has it -
46.
…………………… Wearehiring! Ask
47.
…………………… Q&A
48.
…………………… Thankyou!