Capture of data in Flutter

Data sources in APP can be roughly divided into two parts: one is local, including files and databases; The other is to obtain data from afar via the Internet. Today we will learn how Flutter obtains both kinds of data.

How does Dark single thread implement asynchrony

Before learning about local and network data acquisition, learn how Flutter handles asynchronous processing.

Asynchronous: In contrast to synchronous, the next task is executed directly without waiting for the completion of the task.

First we learned that Dark is single threaded. How does a single thread perform asynchrony?

Because apps spend most of their time waiting. For example, waiting for users to click, waiting for network requests to return, waiting for file IO results, and so on. And these wait behaviors are not blocking. For example, for network requests, the Socket itself provides a select model to query asynchronously; And file IO, the operating system also provides event-based callback mechanism. Therefore, based on these characteristics, the single-threaded model can do other things while waiting, and then do the corresponding processing when the response is actually needed. Because waiting isn’t blocked, it feels like we’re doing more than one thing at a time. But there’s only one thread handling your business.

As in most languages, completing asynchronous tasks is done in an Event Loop. Events that need to be responded to on the main thread are queued, and messages are continually fetched from the queue when the main thread is idle, and then completed on the main thread.

In Dark, there are actually two queues: an Event Queue and a Microtask Queue. The priority of the microtask queue is higher than that of the event queue. Therefore, the microtask queue is checked first every time. If there are any microtask queues, they will be executed first.

Now that you know about Dark’s asynchronous processing mechanism, let’s see how it works in your code.

Dart provides a layer of encapsulation for the task creation of the Event Queue, called the Future. It is also easy to understand from the name that it represents a task to be completed at a future time. Putting a function body into the Future completes the transition from synchronous to asynchronous.

void syncDemo1() { Future(() => print("print future1")); //a print("print 1"); //b Future(() => print("print 2"))//c .. then((value) => print("print 3"))//d .. then((value) => print("print 4")); //e }Copy the code
print 1
print future1
print 2
print 3
print 4
Copy the code

The order of the calls is:

  • A Adds the task to the task queue.
  • B In the main thread, execute execute;
  • C Add the vm to the task queue. When there are no more tasks in the main thread, fetch a from the task queue and execute it
  • After executing a, the main thread does not have any tasks, and c is still executed from the task queue. Then the following functions will be executed synchronously.

An asynchronous function

For an asynchronous function, its internal execution does not end when it returns, so it needs to return a Future object for the caller to use. The caller, based on the Future object, decides:

  • Register a THEN on the Future object and wait for the Future to be processed asynchronously.
  • Or wait synchronously for the Future implementation to end. For a Future object returned by an asynchronous function, if the caller decides to wait synchronously, the await keyword is used at the invocation and the async keyword is used in the function body at the invocation.
void syncFunDemo() async{ Future<String> future() => Future<String>.delayed(Duration(seconds: 3),()=>"hello 2021"); / / the future. Then ((value) = > print (" access to the asynchronous data $value ")); Print (" get async data "+ (await future()))); // synchronous wait}Copy the code

Why async?

Because await in Dart is not blocking wait, it is asynchronous wait. Dart treats the calling body’s function as an asynchronous function, putting the context of the waiting statement into the Event Queue. Once the result is available, the Event Loop takes it out of the Event Queue and waits for the code to continue.

Network requests in a Flutter

After learning about asynchronous operations, strike while the iron is hot and learn about network requests in FLUTTER.

When the network interacts with server data, it is inevitable to use three concepts: location, transport and application.

Location, which defines how to accurately find one or more hosts (IP addresses) on the network; Transmission is mainly responsible for efficient and reliable data communication (namely TCP and UDP protocol) after finding the host. The application, on the other hand, is responsible for identifying the content of communication between the two parties (i.e., HTTP protocol).

In general network request framework, HTTP network call can be divided into the following parts in sequence:

  • Create network call instance Client and set common request behavior (such as timeout);
  • Construct the URI and set the request header and body.
  • Initiate a request and wait for a response;
  • Decode the contents of the response.

In Flutter, Http network programming can be implemented in three ways: DART: IO HttpClient implementation, DART native Http request library implementation, and dio implementation.

HttpClient

Dart native HTTP request library implementation

Third party library DIO implementation

HttpClient and HTTP are simple to use, but they expose relatively weak customization capabilities, many common functions are not supported (or extremely cumbersome implementation), such as request cancellation, custom interceptor, Cookie management, etc. Therefore, for complex network request behavior, I recommend using dio, which is currently popular in the Dart community, to initiate network requests.

  • Join DIO dependency
Dio: 3.0.10Copy the code
  • Simple GET requests
Void getRequest() async {// Create network call example Dio Dio = Dio(); / / set the request URI and the initiating var response after the user-agent = await dio. Get (" https://wanandroid.com/wxarticle/chapters/json ", the options: Options(headers: {"user-agent": "Custom-UA"})); If (response.statusCode == httpStatus.ok) {print(response.data.tostring ()); } else { print("Error: ${response.statusCode}"); }}Copy the code
  • Make multiple requests simultaneously
// Initiate multiple requests simultaneously void getRequest2() async {// Create network call example Dio Dio = Dio(); / / launched two parallel at the same time request List < Response > responseX = await Future. Wait ([dio. Get (" https://wanandroid.com/wxarticle/chapters/json "), dio.get("https://www.wanandroid.com/article/list/0/json") ]); Print ("Response1: ${responseX[0].tostring ()}"); Print ("Response2: ${responseX[1].tostring ()}"); }Copy the code
  • Adding interceptors
// Add interceptor void getRequest3() async {// Create network call example Dio Dio = Dio(); Dio.interceptortors. add(InterceptorsWrapper(onRequest: (RequestOptions options) {// Add user-agent options.headers["user-agent"] = "custom-UA "; print("interceptor request ${options.uri}"); print("interceptor request ${options.headers}"); print("interceptor request ${options.data}"); // Release request return options; }, onResponse: (Response response) { print("interceptor response ${response.data.toString()}"); })); / / add try catch, prevent request error try {await dio. Get (" https://wanandroid.com/wxarticle/chapters/json "); } catch (e) { print(e); }}Copy the code

The above are simple dio network request examples, more advanced usage, you can go to GitHub to see.

Json parsing

After completing the network request, we can’t use the data directly. We have to parse the data returned by the server first. Json is a common data format for both server and client transfer.

After retrieving the JSON data returned by the server, how do you parse it?

Because Flutter does not support runtime reflection, there are no libraries like Gson and Mantle that automatically parse JSON to reduce parsing costs. With Flutter, JSON parsing is completely manual, giving developers a bit more work to do, but it’s also relatively flexible to use.

Automatic parsing: The process of parsing JSON strings into custom objects using the JSON decoder built into the DART: Convert library. With this approach, we first need to pass the JSON string to the json. decode method to parse into a Map, which we then pass to the custom class for attribute assignment.

  • Json parsing
Class Student{// Attribute id, name and score String id; String name; int score; Student({this.id, this.name, this.score}); // Student(map <String, dynamic> parsedJson){return Student(id: parsedJson['id'], name : parsedJson['name'], score : parsedJson ['score'] ); }} void parseJson1 () {var jsonString = "" "{" id ":" 1234 ", "name" : "zhou knot", "score" : 95} "" "; //jsonString is JSON text final jsonResponse = json.decode(jsonString); Student student = Student.fromJson(jsonResponse); print(student.name); }Copy the code

Json data parsing generation class, there is a JSON parsing factory; If there are more layers nested inside, it is estimated that parsing is also very tired, so it can be handed over to plug-ins. Search for FlutterJsonBeanFactory in Android Studio plugins to install the plug-in, and then restart Studio.

Var jsonString = """{"id":"123", "name":" 3", "name":" 3", "score" : 95}"""; //jsonString is JSON text final jsonResponse = json.decode(jsonString); UserEntity student = JsonConvert.fromJsonAsT(jsonResponse); print(student.name); }Copy the code

The FlutterJsonBeanFactory plugin does a lot of things for us and allows us to focus on development.

Local data management in Flutter

We learned about dart network requests and JSON parsing, and learned how to fetch data from a remote location. Let’s look at how Flutter handles local data.

Flutter provides three data persistence methods: files, SharedPreferences, and databases.

file

A file is an ordered collection of information with a filename that is stored at a specified path on some medium, such as a disk. By definition, to persist data as a file, we first need to determine one thing: where to put the data? This means defining where to store files.

Flutter provides two directories to store files, namely the Temporary directory and the Documents directory:

  • A temporary directory is a directory that the operating system can erase at any time. It is usually used to store temporary cache data that is not important. This directory corresponds to the value returned by NSTemporaryDirectory on iOS and getCacheDir on Android.
  • The document directory is a directory that is cleared only when an application is deleted, and is usually used to store important data files generated by the application. On iOS, this directory corresponds to NSDocumentDirectory, while on Android it corresponds to the AppData directory.
// To read files, rely on path_provider: ^2.0.1 Class LocalDataDemo extends StatelessWidget {@override Widget build(BuildContext context) {return Scaffold( AppBar: appBar (children: [ListTile(title: Text(" read "),), body: ListView(children: [ListTile(title: Text(" read ")), onTap: () => Navigator.pushNamed(context, "LocalFileDemo"), ),ListTile( title: Text("SharePreferenceDemoDemo"), onTap: () => Navigator.pushNamed(context, "SharePreferenceDemo"), ) ], ), ); } class LocalFileDemo extends StatelessWidget {Future<File> get _localFile async {final directory = await getApplicationDocumentsDirectory(); final path = directory.path; return File('$path/test.txt'); } Future<File> writeContent(String content) async {final File = await _localFile; return file.writeAsString(content); } Future<String> readContent() async {try {final file = await _localFile; String contents = await file.readAsString(); return contents; } catch (e) { return ""; }} @override Widget build(BuildContext context) {return Scaffold(appBar: appBar (title: Text(" Local file read and write "),), body: Center( child: Column( children: [ MaterialButton( onPressed: () { writeContent("hello jay 2021"); }, child: Text(" write data "),), MaterialButton(onPressed: () Async {print(" read data from file: ${await readContent()}");}, child: Text(" read data "),)],),),); }}Copy the code

sharePreference

Add the dependency shared_preferences: ^2.0.5 before using it, and it’s easy to use.

// sharePreference is suitable for storing key-value pairs with a small amount of data, relying on shared_preferences first: ^2.0.5 Class SharePreferenceDemo extends StatelessWidget {String spName = "sp_name"; _spSaveString() async { SharedPreferences.setMockInitialValues({}); // Need to add, Otherwise an error No implementation found for method getAll on channel plugins. The flutter. IO/shared_preferences var sharePreference = await SharedPreferences.getInstance(); await sharePreference.setString(spName, "this is sharePreference"); } _readSpString() async { var sharePreference = await SharedPreferences.getInstance(); Print (" this is from sharePreference read data: ${sharePreference. Get string (spName)} "); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("sharePreferenceDemo"), ), body: Center( child: Column( children: [ MaterialButton( onPressed: () {_spSaveString();}, child: Text(" write data "),), The MaterialButton(onPressed: () {_readSpString();}, child: Text(" read data "),)],),),); }}Copy the code

Of course, sharePreference provides storage for basic data types, so for more apis, see GitHub for sharePreference

The database

In addition to the above two storage methods, Flutter also provides database storage, which is suitable for persisting large amounts of formatted data and updating the data at a high frequency. Databases offer a faster and more flexible solution for data reading and writing than files and SharedPreferences.

Let’s look at a small example to understand the use of a database; You need to rely on SQflite first: ^1.3.0

Refer to the link

www.jianshu.com/p/2eafae001…