Peristence as the Single Source of Truth
CC Attribution 3.0 Unported:
You are free to use, adapt and copy, distribute and transmit the work or content in adapted or unchanged form for any legal purpose as long as the work is attributed to the author in the manner specified by the author or licensor.
Narayan, Saket
Simple is better than complex. Complex is better than complicated. In the world of Android, where all sources of information and user interactions are asynchronous, UI state can become hard to reason about, especially in large apps. This is not only difficult to maintain but also increases complexity with the addition of every new feature. This talk will explore the idea of “persistence as the single source of truth” as a way to simplify state management, where persistent storage becomes the source for all data changes, and the UI is reduced to a reactive representation of persisted data. This forces the flow of data to be unidirectional which makes it easy to follow. The pattern is architecture agnostic and can be used in any MV-anything.We’ll see other benefits of persistence where screens can stop exchanging data through serialized Bundles, get state restoration across Activity recreations for free, and deliver offline support out-of-the-box where all UI interactions receive immediate feedback and the user is never forced to wait.
 Hello, everyone. Good afternoon. I hope you're having a great drive. I work with a studio called uncommon far far away in Bangalore, India. We are a smaller team of 20 people who specialize in building Android applications for companies of all scales and sizes. Over the next 40 minutes, I'm going to talk about a design pattern that has completely changed the way I write for Android. While working on my slides for this talk, I'm struggling with the introduction because the slides between the introduction and the ending are always easy. You just dump whatever you want to talk about. But starting a story is always difficult. So naturally, I was procrastinating by browsing Twitter. And I came across a tweet by someone called Matt who works at Trello. And Matt was appreciating how useful the Trello app is because of its offline first design. And especially because it's still useful when Matt is traveling with bad connectivity. And I realized that this is it. This is what my talk is about. This is what I want to share with you all, that offline first apps are the best kind of apps. These apps are designed around the idea that getting blocked by progress indicators is not a good experience. And the user should be able to both produce and consume content in your application without getting blocked anywhere, regardless of how good or bad the connectivity is. By this term, offline first, I'm not referring to apps that were designed for countries with bad or actually non-existent Internet. It's funny because this is what people usually think about when I talk about offline first apps. But in reality, what connectivity is everywhere? I don't know about Berlin, but in Bangalore, in my city, we have a one-Gbps connection at our office. That's a lot of megabytes per second. But if you walk 10 meters away from the office, the connectivity, the phone starts struggling with connectivity with mobile data. And I'm sure this is the case everywhere, especially if you're traveling. So I think it's really important to design applications that are agnostic to connectivity that can work asynchronously with the network. So over the past few months, I've realized that simplifying state management is the key to enabling offline first design. However, state management is difficult. And one of my favorite ways of explaining why state management is difficult is by using these slides from Jake Warton's talk called Managing State with RxR. He explains that state management is difficult in Android because a normal Android app has to deal with many sources of information. You have network calls. You have database operations. You also have the user interacting with the UI, sending button clicks, text changes, etc. And on top of all this, Android as a platform is inherently asynchronous. You have push messages. You have config changes. You have life cycle functions coming in any time. So with all this, driving the UI from multiple sources becomes very difficult. It becomes exponentially difficult to manage with the number of sources you have. But fortunately, a large portion of this complication can be solved by reducing the number of sources to just one. That is persistence. What this essentially means is that we can drive the UI from persistence. And the state of the app just becomes a dumb representation of persistent data. Now, this idea of using persistence is not new. I'm sure you have all used it in one form or another. But with this talk, I want to propose a cohesive form of these rough ideas called persistence as a single source of truth. I'm going to use the term BST because saying persistence as a single source of truth is too long. It takes a lot of time. Now, as I said in the beginning, this design has changed the way I write for Android. But I understand that you are not going to believe me without seeing any proofs. So I'm going to use this great advice by Chet Haas that you can make the audience believe anything if you show them some graphs. And I got graphs. This graph shows my productivity over time. As you can see, my productivity increased a lot in the beginning. It continued to increase exponentially like a hockey stick curve. Strangely, the hockey stick curve never stopped. It kind of went back in time. Not sure why. Another graph showing my efficiency over time. More graphs. And more graphs. I see someone taking notes in the audience. I'm not sure what you are noting down. But simple to understand, right? So now that I've proved my point and you're going to believe whatever I'm going to say in the next 35 minutes, let's see a few examples to see how to understand how persistence, PST works. I've got three use cases as examples with me. We'll see how these use cases are commonly implemented. We'll see the problems associated with them. And we'll also see how PST can simplify these problems. So that's the first example. Let's imagine we're building a login screen. It's a very basic example. I'm sure they've all written a login screen at least once in our lives. Now, if I were writing a login screen two years ago, this is how I'd be doing it. And my code will first, when the user types their username, password, and hits the submit button, this code will make the login API call. And when our response is received, it will save the token into database and then start the next activity in the visual hierarchy. In case the screen is getting destroyed by, say, an orientation change, we cancel our network call as soon as that happens. Now, there are two major problems here. The first problem is that our screen does not support orientation changes at all. The login call is canceled as soon as the screen is destroyed. And this is sort of understandable because unless you have a properly thought of architecture in place, it's difficult to support orientation changes seamlessly. And this is also why it's very common that the first thing we developers do when writing a new activity is open the manifest and disable orientation changes. The second problem is something that I see way too often, where applications open a new screen even when they're not in foreground. For example, a very common scenario is when the login call is taking too long to complete, so you decide to switch to some other application. But when the login call eventually succeeds, the app decides to open the next activity even without checking if it's still in foreground. And this is very confusing because, hey, you're not sure why this activity is here. So by using persistence, by driving the UI from persistence, you can not only solve these problems, but you can also make the state management much easier. Let's go to our code to behave that way. The first step is to stop communicating with the network layer directly and move the logic of making the login call to a separate class, say, auth repository that lives outside the screen's lifecycle. And for this screen, it becomes a far and forget call. There's no subscription for the result happening here. The auth repository handles making the login call, and it also handles saving the response to database when the call succeeds. Back to the screen. Rather than directly communicating with the network layer, the screen instead listens to changes in the database. This class auth repository, it exposes a stream which emits every time. There's a new session available in persistence. By the way, I'm using RxHour here, and although I understand that not everyone in this audience might be using RxHour, I still think that my code is pretty straightforward to understand. So there should be nothing confusing regardless of whether or not you're using RxHour. So when the screen is starting over the first time and we subscribe to user session function inside on start, we receive a value from auth repository saying that there is no session available right now. When the submit button is clicked eventually, the repository will send an in-flight status where we show a progress indicator to the user. And when the call eventually succeeds, we can open the next activity or the next fragment or whatever. We'll also cancel the network call as soon as on stop is received. If the screen is resumed later, we restart a subscription to auth repository. Now, this gives us a huge advantage because the UI only works while it's in foreground. As soon as on stop is called, we dispose of RxChain and the UI suspends itself. So the next activity will only open if the app is still in foreground. And the network call can still continue to go in background. As a next use case, let's imagine we have a list of submissions on Reddit. And when a submission is clicked, we want to show its details, its comments in a new screen. Now, again, if I were writing this app two years ago, the first thing I would do is serialize a clicked submission and pass it to the common screen. The common screen with some deserialized received object and dump it on the UI. Let's also imagine that the user can also vote on submissions. The obviously wrong way to handle vote clicks would be to show a blocking progress dialogue immediately. Then make the actual network call. When the call succeeds and our response is received, we hide the progress dialogue, update the UI, and then save the new values and data base. The most obvious problem we see here is that the UI is being driven from multiple sources of truth. This is not good. First, we update the UI from on create, from values received from the caller. Second, we update the UI when a vote button is clicked. When the UI is driven from multiple sources of information, we need to somehow synchronize these calls together to avoid staleness of UI. This becomes very complex. The second problem is that we are also forced to save state manually. This is a manual labor because every time the screen is getting destroyed, we have to somehow save the state. Then when the screen is starting again, we have to check for both the restore data and the data being sent from the caller and then decide which one to show. I think a much better way of doing this would be to avoid passing the entire, whoops, I accidentally pressed the escape button. A much better way of doing this would be to avoid passing the entire object around. Instead, we only pass the ID. Similarly, the next screen, the comment screen, uses this ID for subscribing to a repository class, a data provider. This repository exposes a stream for observing changes to the submission in persistence. Whenever we receive an updated submission from the stream, we simply dump the new values on the UI. The advantage we get here is that manual saving of state is no longer required. Our persistence layer will always give the latest value whenever the screen is recreated. Now, when the vote button is clicked, instead of again communicating with the network layer directly, we offload the entire logic to a repository that sits outside the lifecycle. This function in the screen becomes a fire and forget call. It does not care about the result. Every time the user uploads the submission by clicking on the vote button, the screen automatically receives updates from persistence with new data. On the repository side, whenever vote is called, it handles making the network call and saving the new values to the database. So whenever a vote login call goes through and our response is received, it saves the values in the database and whoever is subscribing to the stream can automatically receive the updates. We see multiple obvious events here. The first advantage is that there's only one source of information. So debugging the UI state becomes very trivial. If you're ever confused about what's happening in your UI, you can put a break point or a login in one place and not five different places. The second advantage is that passing data around by serializing them is not required anymore. You can choose to not use pass labels. The third advantage is that the UI automatically receives updates. This works even when the changes are happening outside the screen. For example, it's possible that there's a notification visible with the same submission. It has a vote button. If the user clicks on the vote button in the notification, the screen will also get updated automatically. We can also make some improvements with the user experience here. For example, instead of waiting for the network call to succeed, we can assume that this call will eventually succeed. And with this assumption, we update the vote immediately. And this happens even before the actual network call is made. The assumption here is that the call will happen in the future someday and we don't care when that happens. What this gives us is that the UI receives immediate feedback. When the user clicks on the vote button, the vote goes up by one immediately. There's no waiting for the call to happen. So it's a thumb of rule. I think it's a good practice to assume that if a network call does not involve any extra checks on the server, you should assume that it will pass. This brings us to the next use case, the comment screen. So let's imagine that we have a comment screen where the user can post messages. It's something that we regularly do, post updates to social media. And I'm going to show you how some very popular applications handle this. I've got three applications here. The first app is Relay for Reddit. Second app is Sync for Reddit. And the third app is Flamingo for Twitter. I'll start with Relay. Relay is a quite nicely designed application for Reddit. But its commenting experience is my least favorite part of it. I have a video here. I'm hoping it's visible. When you compose a new comment, Relay shows a blocking progress dialogue. So you're forced to wait until the call goes through. And in case you tap outside, which is possible, it will cancel the network call. And on top of all this, the network dialogue is not retained across conflict changes. So if you happen to accidentally trigger an orientation change, which is common, say you are lying on your bed and watching cat videos, it will happen. And when that happens, the dialogue is gone. Another very popular app, Sync for Reddit, is slightly better than Relay. I think the font size is too small, but I'm just typing some words and posting a reply. When you post a reply in Sync, it does not show any blocking progress dialogue. I don't know if you can see it. There's a toast message at the bottom. Sync only shows toast messages for communicating progress. But it does not block you from using the application while the call is ongoing. So that's slightly better. It's acceptable, but not the best possible way. And in case of failures, Sync displays error notifications. But the notifications are easy to accidentally dismiss them. It's possible that you made an accidental swipe or your phone got restarted or it died because of low battery. And this is very dangerous. You should never lose the user's progress. The third app is one of my favourites app on Android. It's called Flamingo by Sam Rustin. I hope I've known his name right. Flamingo is kind of similar to Reddit. It's kind of similar to Sync. When you post a tweet, it shows a notification for communicating progress and failures. And unlike Sync, it actually saves your failed tweets to drafts. So you can always access them later. They're not lost ever. This is not very seamless, but it's still acceptable because you're not blocked from browsing Twitter while your tweet is being posted. But I think there's a lot of room for improvements here. I think that a much better experience for the user is to remove the notion of the network entirely. The user does not have to care or should not care about the network at all. Here's the same Reddit post in a site project I've been working on. When you compose a new reply and hit the send button, the comment shows up immediately on the UI. So you don't care if the call has been made or will be made. The notion of waiting on the network to see your progress, to see your comment is gone entirely. And I think this is much better because you don't have to wait at all. And since everything gets saved to database first, you never lose the user's progress even in case the API fails. The interesting part of this is that it's not even difficult to create this. Let's see how easy this is using persistence. In the comments screen, we subscribe to changes in submission from the database. And whenever we receive a new update from database, we update the UI accordingly. Next, when the user enters a new comment and hits the send button, we offload the entire logic of making the reply API call to a separate class that sits outside the lifecycle. And this class called submission repository. It handles making the network call for posting this reply. If a success response is received, it saves the status of this comment as saved, as synced. And in case the network error is encountered, we save the status as failed. But before we even make this network call, the first thing we do is save the comment as in-flight so that the UI is updated immediately. What this gives us is that the user can see their comment immediately on the UI. And to give you a recap of what we're doing in this R exchange, all we're doing is saving the status of the network call at every step of the call. And we save the status not just when the call succeeds, but even before it even starts. Similarly, when rendering this comment, we simply dump the comments posted status on the UI. We check the synced status of the comment. And if it's in-flight, we show a progress label. If it's synced, we show the timestamp when the comment was posted. And if it failed, we let the user retry posting it. Now, because we're all, we're persisting all comments in database, handling failures also becomes very easy. If we want to resend all failed comments, it's as easy as simply inspecting the database, finding all unsent comments, and resending them. This can happen inside a broadcast receiver, like a boot-complete receiver, which starts every time your phone is restarted. So you resend all failed messages on every phone restart. Or you could do this inside your application class, so every time your application restarts, you resend all messages. Or the best of all, you could use work manager slash job scheduler, which will ensure that your resending is done in a battery-efficient manner. This next section is something I added at the last moment, because I realized it's also important to talk about this. It's called common excuses that people gave for avoiding persistence. I added this slide because lately I've met a lot of people, a surprisingly high number of people and companies, including art clients, who have tried their best to avoid SQLite while spending either the same amount of effort or higher on something else. The most common reason is that setting up SQLite and using it is verbose and time-consuming, which is true. Plain SQLite is time-consuming. It provides a lot of boilerplate code. And I'm sure you've all seen this example on the official documentation. It's scary. However, now that we have room, this is no longer an excuse. I've been using room for a while now, and after having tried many other libraries, many OVRMs, many object-based databases like Realm, et cetera, I would super recommend room to everyone. The reason I like room so much is because it's dead simple to use. All you have to do is, for creating a new table, all you have to do is write a data class, a sympojo, and mark it as an entity. Next, you define queries how you want to access the table, and that's it. Room handles everything else for us. It's also worth mentioning that room verifies SQL statements at compile time. This is a huge reason why I personally feel a lot more confident when using room because if my code compiles, I can be sure that it will work. By the way, if you're interested in learning more about room, there's a talk happening in this room at 4.35 p.m. I think you should definitely attend the talk. The second reason people claim to avoid SQLite is because they think that reading from SQLite is slow, or reading from disk in general is slow. No, it's not. Unless you're reading many thousands of rows from your database, a SQL anomaly query will not take more than a few milliseconds to complete. And even if it does, let's not forget that when you start a new activity, Android takes about 250 milliseconds to open the activity into foreground. So even if your database calls it slow, it will still be done. Your UI will still be set up much before the user can actually start interacting with the screen. This is just this last excuse usually comes from people who are used to using shared preferences for dumping all their data. Please do not do this. Do not do this. Actually it's still persistent, it's still disk, but what I mean from the slide is that you should not be using shared preferences for dumping your app's data. And one of our recent clients that we worked with, they were dumping their logs, they were dumping their user's data, they were dumping their network responses, analytics, everything into shared preferences. And for those unaware, shared preferences is a flat file-based system that rewrites the entire file whenever you update something. So it's optimized for small bits of data, but not multiple megabytes of data. You should not do that. Also, now that we have room, again, there's no excuse for not using SQLite. It's not synchronous, sure, but your app should not expect to always run synchronously unless you want to seed junks. And that's pretty much about it. I'll give you a small recap of what we saw so far. The most obvious advantage that we see with persistence is that our state becomes a dump, representation of persistent data. The rendering of UI becomes extremely simple because it just dumps whatever we have in persistence without caring about the state. Additionally, the UI also automatically updates itself, so whenever a new state is available, the UI also gets updated automatically. The next advantage is that we get freedom from manual saving of state and passing data around as serialized bundles. So we get config changes support for free. There's nothing to do extra. That also becomes independent of the network status. Even if the connectivity is bad, your app will still work fine. All communications happen asynchronously by default. This is my favorite part of the design pattern, that the app feels fast. All UI interactions, they happen, they all receive feedback immediately. And there are no blocking progress dialogs shown, so the user receives instant gratification. For example, if your user types a comment and hits the send button, it shows up immediately on the UI. There's no notion of waiting for something to happen. So the perceived speed of your application, it feels faster even if it's doing the same job. Before I leave, I want to share this excellent library by New York Times. New York Times large friendly mic. I don't know how to pronounce his surname, so I will use his user name, friendly mic. Store has been incredibly useful for designing an offline first application for me because it handles implementing the in memory cache. It handles implementing the disk cache. And if the cache is empty, it also handles coordinating with the network layer. It does everything for you. It's dead simple to use, and you should definitely try it out at least once. That's all. Thank you so much for attending this talk.
 Hello, everyone. Good afternoon. I hope you're having a great drive. I work with a studio  called uncommon far far away in Bangalore, India. We are a smaller team of 20 people  who specialize in building Android applications for companies of all scales and sizes. Over  the next 40 minutes, I'm going to talk about a design pattern that has completely changed  the way I write for Android. While working on my slides for this talk, I'm struggling  with the introduction because the slides between the introduction and the ending are always  easy. You just dump whatever you want to talk about. But starting a story is always difficult.  So naturally, I was procrastinating by browsing Twitter. And I came across a tweet by someone  called Matt who works at Trello. And Matt was appreciating how useful the Trello app  is because of its offline first design. And especially because it's still useful when  Matt is traveling with bad connectivity. And I realized that this is it. This is what  my talk is about. This is what I want to share with you all, that offline first apps are the  best kind of apps. These apps are designed around the idea that getting blocked by progress  indicators is not a good experience. And the user should be able to both produce and consume  content in your application without getting blocked anywhere, regardless of how good or  bad the connectivity is. By this term, offline first, I'm not referring to apps that were  designed for countries with bad or actually non-existent Internet. It's funny because  this is what people usually think about when I talk about offline first apps. But in reality,  what connectivity is everywhere? I don't know about Berlin, but in Bangalore, in my city,  we have a one-Gbps connection at our office. That's a lot of megabytes per second. But  if you walk 10 meters away from the office, the connectivity, the phone starts struggling  with connectivity with mobile data. And I'm sure this is the case everywhere, especially  if you're traveling. So I think it's really important to design applications that are  agnostic to connectivity that can work asynchronously with the network. So over the past few months,  I've realized that simplifying state management is the key to enabling offline first design.  However, state management is difficult. And one of my favorite ways of explaining why  state management is difficult is by using these slides from Jake Warton's talk called  Managing State with RxR. He explains that state management is difficult in Android because  a normal Android app has to deal with many sources of information. You have network calls.  You have database operations. You also have the user interacting with the UI, sending  button clicks, text changes, etc. And on top of all this, Android as a platform is inherently  asynchronous. You have push messages. You have config changes. You have life cycle functions  coming in any time. So with all this, driving the UI from multiple sources becomes very  difficult. It becomes exponentially difficult to manage with the number of sources you have.  But fortunately, a large portion of this complication can be solved by reducing the number of sources  to just one. That is persistence. What this essentially means is that we can drive the  UI from persistence. And the state of the app just becomes a dumb representation of persistent  data. Now, this idea of using persistence is not new. I'm sure you have all used it in  one form or another. But with this talk, I want to propose a cohesive form of these rough  ideas called persistence as a single source of truth. I'm going to use the term BST because  saying persistence as a single source of truth is too long. It takes a lot of time. Now,  as I said in the beginning, this design has changed the way I write for Android. But I  understand that you are not going to believe me without seeing any proofs. So I'm going  to use this great advice by Chet Haas that you can make the audience believe anything  if you show them some graphs. And I got graphs. This graph shows my productivity over time.  As you can see, my productivity increased a lot in the beginning. It continued to increase  exponentially like a hockey stick curve. Strangely, the hockey stick curve never stopped. It kind  of went back in time. Not sure why. Another graph showing my efficiency over time. More  graphs. And more graphs. I see someone taking notes in the audience. I'm not sure what you  are noting down. But simple to understand, right? So now that I've proved my point and  you're going to believe whatever I'm going to say in the next 35 minutes, let's see  a few examples to see how to understand how persistence, PST works. I've got three use  cases as examples with me. We'll see how these use cases are commonly implemented. We'll  see the problems associated with them. And we'll also see how PST can simplify these  problems. So that's the first example. Let's imagine we're building a login screen. It's  a very basic example. I'm sure they've all written a login screen at least once in our  lives. Now, if I were writing a login screen two years ago, this is how I'd be doing it.  And my code will first, when the user types their username, password, and hits the submit  button, this code will make the login API call. And when our response is received, it  will save the token into database and then start the next activity in the visual hierarchy.  In case the screen is getting destroyed by, say, an orientation change, we cancel our  network call as soon as that happens. Now, there are two major problems here. The first  problem is that our screen does not support orientation changes at all. The login call  is canceled as soon as the screen is destroyed. And this is sort of understandable because  unless you have a properly thought of architecture in place, it's difficult to support orientation  changes seamlessly. And this is also why it's very common that the first thing we developers  do when writing a new activity is open the manifest and disable orientation changes.  The second problem is something that I see way too often, where applications open a new  screen even when they're not in foreground. For example, a very common scenario is when  the login call is taking too long to complete, so you decide to switch to some other application.  But when the login call eventually succeeds, the app decides to open the next activity  even without checking if it's still in foreground. And this is very confusing because, hey, you're  not sure why this activity is here. So by using persistence, by driving the UI from persistence,  you can not only solve these problems, but you can also make the state management much  easier. Let's go to our code to behave that way. The first step is to stop communicating  with the network layer directly and move the logic of making the login call to a separate  class, say, auth repository that lives outside the screen's lifecycle. And for this screen,  it becomes a far and forget call. There's no subscription for the result happening here.  The auth repository handles making the login call, and it also handles saving the response  to database when the call succeeds. Back to the screen. Rather than directly communicating  with the network layer, the screen instead listens to changes in the database. This class  auth repository, it exposes a stream which emits every time. There's a new session available  in persistence. By the way, I'm using RxHour here, and although I understand that not everyone  in this audience might be using RxHour, I still think that my code is pretty straightforward  to understand. So there should be nothing confusing regardless of whether or not you're  using RxHour. So when the screen is starting over the first time and we subscribe to user  session function inside on start, we receive a value from auth repository saying that there  is no session available right now. When the submit button is clicked eventually, the repository  will send an in-flight status where we show a progress indicator to the user. And when  the call eventually succeeds, we can open the next activity or the next fragment or whatever.  We'll also cancel the network call as soon as on stop is received. If the screen is resumed  later, we restart a subscription to auth repository. Now, this gives us a huge advantage because  the UI only works while it's in foreground. As soon as on stop is called, we dispose of  RxChain and the UI suspends itself. So the next activity will only open if the app is  still in foreground. And the network call can still continue to go in background. As  a next use case, let's imagine we have a list of submissions on Reddit. And when a submission  is clicked, we want to show its details, its comments in a new screen. Now, again, if I  were writing this app two years ago, the first thing I would do is serialize a clicked submission  and pass it to the common screen. The common screen with some deserialized received object  and dump it on the UI. Let's also imagine that the user can also vote on submissions.  The obviously wrong way to handle vote clicks would be to show a blocking progress dialogue  immediately. Then make the actual network call. When the call succeeds and our response  is received, we hide the progress dialogue, update the UI, and then save the new values  and data base. The most obvious problem we see here is that the UI is being driven from  multiple sources of truth. This is not good. First, we update the UI from on create, from  values received from the caller. Second, we update the UI when a vote button is clicked.  When the UI is driven from multiple sources of information, we need to somehow synchronize  these calls together to avoid staleness of UI. This becomes very complex. The second  problem is that we are also forced to save state manually. This is a manual labor because  every time the screen is getting destroyed, we have to somehow save the state. Then when  the screen is starting again, we have to check for both the restore data and the data being  sent from the caller and then decide which one to show. I think a much better way of  doing this would be to avoid passing the entire, whoops, I accidentally pressed the escape  button. A much better way of doing this would be to avoid passing the entire object around.  Instead, we only pass the ID. Similarly, the next screen, the comment screen, uses this  ID for subscribing to a repository class, a data provider. This repository exposes a  stream for observing changes to the submission in persistence. Whenever we receive an updated  submission from the stream, we simply dump the new values on the UI. The advantage we  get here is that manual saving of state is no longer required. Our persistence layer will  always give the latest value whenever the screen is recreated. Now, when the vote button  is clicked, instead of again communicating with the network layer directly, we offload  the entire logic to a repository that sits outside the lifecycle. This function in the  screen becomes a fire and forget call. It does not care about the result. Every time  the user uploads the submission by clicking on the vote button, the screen automatically  receives updates from persistence with new data. On the repository side, whenever vote  is called, it handles making the network call and saving the new values to the database.  So whenever a vote login call goes through and our response is received, it saves the  values in the database and whoever is subscribing to the stream can automatically receive the  updates. We see multiple obvious events here. The first advantage is that there's only one  source of information. So debugging the UI state becomes very trivial. If you're ever  confused about what's happening in your UI, you can put a break point or a login in one  place and not five different places. The second advantage is that passing data around by serializing  them is not required anymore. You can choose to not use pass labels. The third advantage  is that the UI automatically receives updates. This works even when the changes are happening  outside the screen. For example, it's possible that there's a notification visible with the  same submission. It has a vote button. If the user clicks on the vote button in the  notification, the screen will also get updated automatically. We can also make some improvements  with the user experience here. For example, instead of waiting for the network call to  succeed, we can assume that this call will eventually succeed. And with this assumption,  we update the vote immediately. And this happens even before the actual network call is made.  The assumption here is that the call will happen in the future someday and we don't  care when that happens. What this gives us is that the UI receives immediate feedback.  When the user clicks on the vote button, the vote goes up by one immediately. There's no  waiting for the call to happen. So it's a thumb of rule. I think it's a good practice  to assume that if a network call does not involve any extra checks on the server, you  should assume that it will pass. This brings us to the next use case, the comment screen.  So let's imagine that we have a comment screen where the user can post messages. It's something  that we regularly do, post updates to social media. And I'm going to show you how some  very popular applications handle this. I've got three applications here. The first app  is Relay for Reddit. Second app is Sync for Reddit. And the third app is Flamingo for Twitter.  I'll start with Relay. Relay is a quite nicely designed application for Reddit. But its commenting  experience is my least favorite part of it. I have a video here. I'm hoping it's visible.  When you compose a new comment, Relay shows a blocking progress dialogue. So you're forced  to wait until the call goes through. And in case you tap outside, which is possible, it  will cancel the network call. And on top of all this, the network dialogue is not retained  across conflict changes. So if you happen to accidentally trigger an orientation change,  which is common, say you are lying on your bed and watching cat videos, it will happen.  And when that happens, the dialogue is gone. Another very popular app, Sync for Reddit,  is slightly better than Relay. I think the font size is too small, but I'm just typing  some words and posting a reply. When you post a reply in Sync, it does not show any blocking  progress dialogue. I don't know if you can see it. There's a toast message at the bottom.  Sync only shows toast messages for communicating progress. But it does not block you from using  the application while the call is ongoing. So that's slightly better. It's acceptable,  but not the best possible way. And in case of failures, Sync displays error notifications.  But the notifications are easy to accidentally dismiss them. It's possible that you made  an accidental swipe or your phone got restarted or it died because of low battery. And this  is very dangerous. You should never lose the user's progress. The third app is one of my  favourites app on Android. It's called Flamingo by Sam Rustin. I hope I've known his name  right. Flamingo is kind of similar to Reddit. It's kind of similar to Sync. When you post  a tweet, it shows a notification for communicating progress and failures. And unlike Sync, it  actually saves your failed tweets to drafts. So you can always access them later. They're  not lost ever. This is not very seamless, but it's still acceptable because you're not  blocked from browsing Twitter while your tweet is being posted. But I think there's a lot  of room for improvements here. I think that a much better experience for the user is to  remove the notion of the network entirely. The user does not have to care or should not  care about the network at all. Here's the same Reddit post in a site project I've been working  on. When you compose a new reply and hit the send button, the comment shows up immediately  on the UI. So you don't care if the call has been made or will be made. The notion of waiting  on the network to see your progress, to see your comment is gone entirely. And I think  this is much better because you don't have to wait at all. And since everything gets  saved to database first, you never lose the user's progress even in case the API fails.  The interesting part of this is that it's not even difficult to create this. Let's see  how easy this is using persistence. In the comments screen, we subscribe to changes in  submission from the database. And whenever we receive a new update from database, we  update the UI accordingly. Next, when the user enters a new comment and hits the send  button, we offload the entire logic of making the reply API call to a separate class that  sits outside the lifecycle. And this class called submission repository. It handles making  the network call for posting this reply. If a success response is received, it saves  the status of this comment as saved, as synced. And in case the network error is encountered,  we save the status as failed. But before we even make this network call, the first thing  we do is save the comment as in-flight so that the UI is updated immediately. What this  gives us is that the user can see their comment immediately on the UI. And to give you a recap  of what we're doing in this R exchange, all we're doing is saving the status of the network  call at every step of the call. And we save the status not just when the call succeeds,  but even before it even starts. Similarly, when rendering this comment, we simply dump  the comments posted status on the UI. We check the synced status of the comment. And if it's  in-flight, we show a progress label. If it's synced, we show the timestamp when the comment  was posted. And if it failed, we let the user retry posting it. Now, because we're all,  we're persisting all comments in database, handling failures also becomes very easy. If  we want to resend all failed comments, it's as easy as simply inspecting the database,  finding all unsent comments, and resending them. This can happen inside a broadcast receiver,  like a boot-complete receiver, which starts every time your phone is restarted. So you  resend all failed messages on every phone restart. Or you could do this inside your application  class, so every time your application restarts, you resend all messages. Or the best of all,  you could use work manager slash job scheduler, which will ensure that your resending is done  in a battery-efficient manner. This next section is something I added at the last moment,  because I realized it's also important to talk about this. It's called common excuses  that people gave for avoiding persistence. I added this slide because lately I've met  a lot of people, a surprisingly high number of people and companies, including art clients,  who have tried their best to avoid SQLite while spending either the same amount of effort  or higher on something else. The most common reason is that setting up SQLite and using  it is verbose and time-consuming, which is true. Plain SQLite is time-consuming. It provides  a lot of boilerplate code. And I'm sure you've all seen this example on the official documentation.  It's scary. However, now that we have room, this is no longer an excuse. I've been using  room for a while now, and after having tried many other libraries, many OVRMs, many object-based  databases like Realm, et cetera, I would super recommend room to everyone. The reason I like  room so much is because it's dead simple to use. All you have to do is, for creating a  new table, all you have to do is write a data class, a sympojo, and mark it as an entity.  Next, you define queries how you want to access the table, and that's it. Room handles everything  else for us. It's also worth mentioning that room verifies SQL statements at compile time.  This is a huge reason why I personally feel a lot more confident when using room because  if my code compiles, I can be sure that it will work. By the way, if you're interested  in learning more about room, there's a talk happening in this room at 4.35 p.m. I think  you should definitely attend the talk. The second reason people claim to avoid SQLite  is because they think that reading from SQLite is slow, or reading from disk in general  is slow. No, it's not. Unless you're reading many thousands of rows from your database,  a SQL anomaly query will not take more than a few milliseconds to complete. And even if  it does, let's not forget that when you start a new activity, Android takes about 250 milliseconds  to open the activity into foreground. So even if your database calls it slow, it will still  be done. Your UI will still be set up much before the user can actually start interacting  with the screen. This is just this last excuse usually comes from people who are used to  using shared preferences for dumping all their data. Please do not do this. Do not do this.  Actually it's still persistent, it's still disk, but what I mean from the slide is that  you should not be using shared preferences for dumping your app's data. And one of our  recent clients that we worked with, they were dumping their logs, they were dumping their  user's data, they were dumping their network responses, analytics, everything into shared  preferences. And for those unaware, shared preferences is a flat file-based system that  rewrites the entire file whenever you update something. So it's optimized for small bits  of data, but not multiple megabytes of data. You should not do that. Also, now that we  have room, again, there's no excuse for not using SQLite. It's not synchronous, sure,  but your app should not expect to always run synchronously unless you want to seed junks.  And that's pretty much about it. I'll give you a small recap of what we saw so far. The  most obvious advantage that we see with persistence is that our state becomes a dump, representation  of persistent data. The rendering of UI becomes extremely simple because it just dumps whatever  we have in persistence without caring about the state. Additionally, the UI also automatically  updates itself, so whenever a new state is available, the UI also gets updated automatically.  The next advantage is that we get freedom from manual saving of state and passing data  around as serialized bundles. So we get config changes support for free. There's nothing  to do extra. That also becomes independent of the network status. Even if the connectivity  is bad, your app will still work fine. All communications happen asynchronously by default.  This is my favorite part of the design pattern, that the app feels fast. All UI interactions,  they happen, they all receive feedback immediately. And there are no blocking progress dialogs  shown, so the user receives instant gratification. For example, if your user types a comment  and hits the send button, it shows up immediately on the UI. There's no notion of waiting for  something to happen. So the perceived speed of your application, it feels faster even  if it's doing the same job. Before I leave, I want to share this excellent  library by New York Times. New York Times large friendly mic. I don't know how to pronounce  his surname, so I will use his user name, friendly mic. Store has been incredibly useful  for designing an offline first application for me because it handles implementing the  in memory cache. It handles implementing the disk cache. And if the cache is empty, it  also handles coordinating with the network layer. It does everything for you. It's dead  simple to use, and you should definitely try it out at least once. That's all. Thank you  so much for attending this talk.
_DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_0_863.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_864_2039.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_2040_2915.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_2916_3083.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_3084_4031.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_4032_4667.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_4668_6335.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_6336_6767.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_6768_7847.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_7848_8483.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_8484_8663.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_8664_8807.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_8808_9179.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_9180_10163.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_10164_16847.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_16848_16931.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_16932_17171.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_17172_20291.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_20292_23303.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_23304_27059.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_27060_27647.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_27648_27695.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_27696_28055.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_28056_28187.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_28188_28547.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_28548_28631.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_28632_28787.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_28788_30191.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_30192_30419.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_30420_30647.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_30648_30827.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_30828_31055.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_31056_31223.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_31224_31271.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_31272_31979.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_31980_32303.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_32304_32603.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_32604_37295.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_37296_38147.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_38148_38507.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_38508_38747.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_38748_43883.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_43884_44135.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_44136_46619.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_46620_47759.png _DCBerlin18_207_Narayan_Peristence_as_the_Single_S_vH0m8TqvR4M_47760_47771.png