hive 连接远程数据源
When developing apps, it is common to have communication with a remote data source. However, relying on the user constantly having a network connection in order to be able to display content can sometimes create a poor user experience. Therefore it is often desirable to have the app store data locally so that it can remain responsive and most importantly… the users are kept happy!
开发应用程序时,通常与远程数据源进行通信。 但是,依赖于用户不断具有网络连接以便能够显示内容有时会导致不良的用户体验。 因此,通常希望使应用程序在本地存储数据,以使其保持响应状态,最重要的是……让用户感到满意!
For tackling this challenge, we are going to use the repository pattern, which will mean that our Flutter application will be structured like this:
为了应对这一挑战,我们将使用存储库模式,这意味着我们的Flutter应用程序将采用以下结构:
The BLoC package for Flutter will be our state management approach and will be responsible for containing our business logic (I wrote another article here where I delve into the BLoC pattern in greater detail), and we will be using Hive as our local data persistence method. It is worth noting however that it could be worth exploring SQLite based approaches for data that requires more complex relationship representation.
Flutter的BLoC软件包将是我们的状态管理方法,并将负责包含我们的业务逻辑(我在这里写了另一篇文章, 在其中更详细地研究了BLoC模式),我们将使用Hive作为本地数据持久性方法。 。 但是,值得注意的是,可能需要探索基于SQLite的方法来处理需要更复杂的关系表示的数据。
When creating clean, testable and maintainable apps, it is important for us to separate our concerns.
在创建干净,可测试和可维护的应用程序时,对我们来说分开我们的关注点很重要。
Therefore the BLoCs are going to be able to communicate with the repositories via Dependency Injection (DI) using the RepositoryProvider approach offered in the BLoC package.
因此,BLoC将能够使用BLoC包中提供的RepositoryProvider方法通过依赖注入(DI)与存储库进行通信。
Our goal here is going to be to create a repository that will be responsible for interacting with User objects that will expose functionality for both adding and retrieving Users. The User model that we are going to use is very basic, as it will only consist of an id and a name and will be marked up appropriately to be able to be stored using Hive.
我们的目标是创建一个存储库,该存储库将负责与User对象进行交互,从而公开用于添加和检索User的功能 。 我们将要使用的User模型是非常基本的,因为它将仅包含一个ID和一个名称,并且将被适当地标记以便能够使用Hive进行存储。
We are going to begin this solution by creating a generic repository interface that defines the core functionality that our repositories will have and can be implemented by any object of type T. Defining this as an interface allows for it to easily be mocked when testing the app.
我们将通过创建一个通用的存储库接口来开始此解决方案,该接口定义了我们的存储库将具有的核心功能,并且可以由任何类型T的对象来实现。将其定义为接口可以在测试应用程序时轻松对其进行模拟。
For the sake of simplifying the creation of local data caching for future objects, we are also going to create a Hive implementation of our repository interface that will deal with these core operations.
为了简化为将来对象创建本地数据缓存的过程,我们还将创建存储库接口的Hive实现,以处理这些核心操作。
This works by interacting with a Hive Box and has a check in place each time to ensure that the Box is open before attempting to perform any operations. It is important therefore that when constructing a HiveRepository that we provide it with an opened Box of type T, as this will be used for the data interactions.
这可以通过与Hive Box进行交互来进行,并且每次都进行检查以确保Box在尝试执行任何操作之前处于打开状态。 因此,重要的是在构造HiveRepository时 ,我们为其提供一个打开的T型Box ,因为它将用于数据交互。
We are now going to build an implementation that will be used for interacting with the remote data source. This is just going to be a mock implementation that simulates API responses and will represent network latency via the addition of an artificial delay before performing actions.
现在,我们将构建一个实现,该实现将用于与远程数据源进行交互。 这将是模拟API响应的模拟实现,并通过在执行操作之前添加人为延迟来表示网络延迟。
Finally this is all going to be brought together by making a repository that coordinates both our local and remote repositories such that it only exposes a single implementation for the BLoC to work with. This approach is inspired by the Repository package.
最后,通过构建一个可协调我们本地和远程存储库的存储库,将所有这些内容整合在一起,以便仅公开一个实现BLoC的实现。 此方法受Repository软件包的启发。
Our repository will work by accepting multiple instances of IRepository
我们的存储库将通过接受IRepository
For each of the operations requested by the user, it will determine how to handle the request and act accordingly. For example, when requesting a user that is not available locally and there is an internet connection, the user will be fetched from the remote source, then stored locally before finally being returned back from the repository. This means that any future requests will be able to be resolved using the locally stored value! It is important to note however that in some use cases it is possible that we would want to update our local values with the remote data, meaning a bit of extra logic would be required here to handle this.
对于用户请求的每个操作,它将确定如何处理请求并采取相应的行动。 例如,当请求本地不可用的用户并且存在Internet连接时,将从远程源获取该用户,然后将其存储在本地,然后最终从存储库中返回。 这意味着将来的任何请求都可以使用本地存储的值来解决! 但是,请务必注意,在某些用例中,我们可能想用远程数据更新本地值,这意味着在这里需要一些额外的逻辑来处理此问题。
Error handling is another important consideration to be made here, especially when interacting with a remote data source. It is preferable in these situations to throw custom exceptions with optional payloads in order for the BLoC to handle it accordingly.
错误处理是此处要考虑的另一个重要考虑因素,尤其是在与远程数据源进行交互时。 在这些情况下,最好使用可选的有效负载抛出自定义异常,以便BLoC相应地对其进行处理。
We are going to implement an example of this, by creating a NoConnectionException that will be thrown if the user does not have a network connection. For example if a User is requested that is not stored locally and the user has no connection, this exception will be thrown.
我们将通过创建NoConnectionException来实现此示例,如果用户没有网络连接,则将抛出该异常。 例如,如果请求的用户不在本地存储并且该用户没有连接,则将抛出此异常。
As stated earlier, we are going to create an App wide RepositoryProvider that will contain our injectable repositories and will expose them to the business logic via an interface.
如前所述,我们将创建一个应用程序级RepositoryProvider ,其中将包含我们的可注入存储库,并将通过接口将其公开给业务逻辑。
Here we will follow a similar pattern to earlier by registering our UserRepository as an IRepository
在这里,我们将采用与之前类似的模式,将我们的UserRepository注册为IRepository
The UserRepository will then be constructed using implementations of both the remote and local repositories, ensuring that we open and initialise our Hive Box before passing it to our local repository implementation.
然后,将使用远程和本地存储库的实现来构造UserRepository ,以确保在将其传递给本地存储库实现之前,我们先打开并初始化Hive Box 。
Then for the hasConnection function, we are going to use a mockable NetworkConnectivityService that will be able to determine if the user is connected to a network. For now, this will always return True.
然后对于hasConnection函数,我们将使用可模拟的NetworkConnectivityService ,它将能够确定用户是否连接到网络。 现在,它将始终返回True。
It is now time for our business logic to be able to work with our new repository!
现在是时候让我们的业务逻辑能够使用我们的新存储库了!
The state that this BLoC will yield will contain the Users retrieved from the repository, an isFetching boolean that will inform the UI that operations are taking place and finally a hasNetworkError boolean that will be used in case of the aforementioned NoConnectionException.
此BLoC产生的状态将包含从存储库中检索到的Users ,一个isFetching布尔值(该值将通知UI正在进行操作),最后一个hasNetworkError布尔值(在上述NoConnectionException的情况下将使用)。
Our BLoC will be constructed with an implementation of IRepository
我们的BLoC将使用IRepository
When handling this request, it will first set the isFetching flag such that the UI can show a loading status of some sort. It will then attempt to retrieve the next User from the repository, but will perform this operation within a try…catch and if a network exception is caught, a hasNetworkError flag will be set. If the User has been successfully retrieved from the repository however, it will be added to the state.
处理此请求时,它将首先设置isFetching标志,以便UI可以显示某种加载状态。 然后它将尝试从存储库中检索下一个用户 ,但是将在try…catch内执行此操作,如果捕获到网络异常,将设置hasNetworkError标志。 但是,如果已成功从存储库中检索到用户 ,则会将其添加到状态中。
Finally, we are going to build a User Interface (UI) to interact with this BLoC. This UI will be a single screen that contains a button that will be used to fetch Users from the repository one at a time via the BLoC. Then after the new Users have been retrieved, a list below the button will be populated with the names of each one.
最后,我们将构建一个用户界面(UI)来与此BLoC进行交互。 此UI将是一个包含一个按钮的屏幕,该按钮将用于一次通过BLoC从存储库中获取用户 。 然后,在检索到新用户之后,将在按钮下方的列表中填充每个用户的名称。
The UI will be responsible for using the RepositoryProvider to request an implementation of IRepository
UI将负责使用RepositoryProvider来请求IRepository
As expected, on first run of the app each of the requests that we make will require a call to the network to be made and there will be a short delay each time. We are going to initially load 3 Users from the repository and display them on the screen to demonstrate this.
不出所料,在首次运行该应用程序时,我们发出的每个请求都需要拨打网络电话,并且每次都会有短暂的延迟。 我们将首先从存储库中加载3个用户 ,并将其显示在屏幕上进行演示。
However if we then reload the app after loading in these initial 3 Users, our first 3 requests to fetch the Users will return much faster!
但是,如果我们在加载了最初的3个Users之后重新加载应用程序,则我们提取用户的前3个请求将返回得更快!
This is because the UserRepository is now instead reading from the local repository and doesn’t need to make the network call at all!
这是因为UserRepository现在改为从本地存储库中读取,根本不需要进行网络调用!
…and that’s it! We now have a repository implementation that can coordinate between multiple data sources and will cache remote data locally!
…就是这样! 现在,我们有了一个存储库实现,可以在多个数据源之间进行协调,并将本地缓存远程数据!
Thanks for reading this article.
感谢您阅读本文。
A repository containing this example can be found at: https://github.com/luketg8/Repository_Example
可以在以下位置找到包含此示例的存储库: https : //github.com/luketg8/Repository_Example
https://www.linkedin.com/in/luketg8/
https://www.linkedin.com/in/luketg8/
翻译自: https://levelup.gitconnected.com/a-practical-approach-to-caching-remote-data-using-hive-in-flutter-b2bcff5bfdef
hive 连接远程数据源