Flutter Mobile App Rapid Development Practice — State Management, Internationalization, Data Persistence, and Performance Optimization (Part 2)
Continuing from the previous article: Behind Minimalist Poetry: Flutter Mobile App Rapid Development Practice — State Management, Internationalization, Data Persistence, and Performance Optimization (Part 1) The previous article covered project structure, state management, and internationalization. This article continues with data persistence and rough performance optimization. Data Persistence Currently, the more commonly used and mature data persistence solutions in Flutter are shared_preferences and SQFLite. One is an ini config file, the other is an SQLite database. For small apps I tend to use only the former,
目录5 个章节
Continuing from the previous article: Behind Minimalist Poetry: Flutter Mobile App Rapid Development Practice — State Management, Internationalization, Data Persistence, and Performance Optimization (Part 1)
The previous article covered project structure, state management, and internationalization. This article continues with data persistence and rough performance optimization.
Data Persistence
Currently, the more commonly used and mature data persistence solutions in Flutter are shared_preferences and SQFLite. One is an ini config file, the other is an SQLite database. For small apps I tend to use only the former—it's more convenient. Sqflite works, but Flutter still doesn't have a really good ORM framework, so you have to write a lot of SQL, which is painful.
So below I'll introduce the simple usage of shared_preferences. For details, see the official site: https://pub.flutter-io.cn/packages/shared_preferences
Add the dependency:
dependencies:
shared_preferences: ^0.5.6
Here's a simple example from the official site:
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
await prefs.setInt('counter', counter);
Just copying the official example isn't enough, of course. What I want to talk about for data persistence is this—the approach used in this project.
Remember the json model module from the previous article? The object serialization and deserialization it provides is very useful for persistence. When saving data, convert the model class to JSON and store it in SharedPreferences. For example:
/// 持久化Profile信息
static saveProfile() => _prefs.setString("profile", jsonEncode(profile.toJson()));
When the app starts, read the JSON data from SharedPreferences and use the model class's JSON constructor to create (deserialize) a model object. Example:
_prefs = await SharedPreferences.getInstance();
var _profile = _prefs.getString("profile");
if (_profile != null) {
try {
profile = Profile.fromJson(jsonDecode(_profile));
// 防止读取出来是null,用于从版本已有profile数据的升级,如果是全新安装的没有这个问题
profile.darkMode = profile.darkMode ?? false;
} catch (e) {
print(e);
}
}
That completes the data persistence work—very simple.
Related Configuration
Next, a bit about project configuration. Whether it's Flutter development or Android development, it's all Google's technology. Google, you know—the servers are all blocked, and most of the Gradle Maven sources are inaccessible too, so compiling an Android APK is always slow. We need to set up domestic mirrors.
Modify android/build.gradle and change the default google() and jcenter() to the following addresses.
repositories {
// google()
// jcenter()
maven { url 'https://maven.aliyun.com/repository/google' }
maven { url 'https://maven.aliyun.com/repository/jcenter' }
maven { url 'http://maven.aliyun.com/nexus/content/groups/public' }
}
Also modify the android/gradle/wrapper/gradle-wrapper.properties file.
#Wed Dec 11 15:10:31 HKT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
Change the last line to a domestic server, e.g.: distributionUrl=https\://downloads.gradle-dn.com/distributions/gradle-5.1.1-all.zip—just change the domain.
There's also pub.dev, the Dart package publishing site, which is inaccessible in China, but we have a domestic mirror: https://pub.flutter-io.cn/
Thanks to the folks in the domestic open-source community for their efforts!
There's also migrating to AndroidX, which is another pitfall. You can refer to: Flutter Development: Migrating a Flutter App to AndroidX
Flutter's "Multithreading"
Next, some practices around performance optimization. I put multithreading in quotes in the heading because Dart is single-threaded, similar to JS. In Flutter, the UI and other operations all run on the same thread. If there's a time-consuming operation on the main thread, the UI freezes. Flutter displays at 60 fps by default, so if an operation can't finish within 1/60 of a second, it will affect UI refresh and produce a noticeable stutter.
So how do we solve this? Dart introduced something called Isolate, similar to a thread, but it cannot share memory with the main thread. To share data, you must pass arguments and get return values by copying memory. Compared to multithreading, there are still quite a few limitations in usage.
I won't go into too much detail about Isolates. For details, please see the official docs and experts' blogs. Here I'll record my own practice.
As mentioned earlier, switching between the main thread and an isolate has a high overhead, so to use multithreading while also improving performance, we need to reduce isolate creation. We can use a "thread pool" for load balancing, defining a LoadBalancer in code to store our isolates.
Future<LoadBalancer> loadBalancer = LoadBalancer.create(4, IsolateRunner.spawn);
When using it, create a reusable isolate through the load balancer:
final lb = await loadBalancer;
lb.run(Isolate方法, 参数);
The isolate method is a static method that takes one argument. Since it can only take one argument, if you need to pass multiple arguments you have to define your own protocol. You can write your own class, or pass a dict or list. I prefer passing a dict so I don't have to define a separate class. The result after execution is returned via the return value; I usually also return a dict (map).
Note: Isolates cannot access the main thread's memory. Accessing variables from elsewhere inside an isolate is invalid. Please exchange data by passing arguments and getting return values.
Also, you currently cannot use the print function to print logs, nor output logs to Android logcat via a channel. It's said to be a Flutter bug. The only way is to use the logging tool in the official DevTools. See: https://flutter.dev/docs/testing/code-debugging
To Be Continued
I've written quite a lot without realizing it. There are still some miscellaneous bits left, plus more on performance optimization and app size optimization that I'm still researching and summarizing. I'll continue in the next article~
Welcome to Discuss
For questions, please leave a message on the WeChat official account backend. I'll reply to every message~
- WeChat official account: 画星星高手
- Coding livestream: https://live.bilibili.com/11883038
- Zhihu: https://www.zhihu.com/people/dealiaxy
- Jianshu: https://www.jianshu.com/u/965b95853b9f
_EOF_
Flutter Mobile App Rapid Development Practice — State Management, Internationalization, Data Persistence, and Performance Optimization (Part 2)
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
有模有样解决Flutter里Webview无法访问HTTP页面的问题
探索过程 Android9(好像是吧)开始谷歌就默认不让开发者访问不安全HTTP内容了,如果非要用HTTP,那必须在networkSecurityConfig里配置cleartextTrafficPermitted才行。谷歌这个设计的出发点本是好的,HTTPS固然更安全咱都懂,但有时候这个后端接口你选择不了要HTTP还是HTTPS,比如要使用没有提供HTTPS的第三方服务,或者是本文的这种情况:在
2021-12-18 · Flutter · 约 1 分钟
Flutter编译失败:java-nio-file-NoSuchFileException--build-app-intermediates
FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ':app:mergeExtDexDebug'. A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$Acti
2021-12-18 · Flutter · 约 1 分钟
使用Flutter设计一个好看的_我_页面
近期遇到一些很烦的琐事,状态比较down,很多原本计划好的事情都耽搁了,实在是难顶…… 看到后台一直有朋友问怎么博客和公众号没有更新,所以我忙完得闲就来更了! 前言 起因是最近重拾以前的旧项目(业余做的,打算有机会自己运营起来)继续完善,然后发现有些界面设计太粗糙了,实在不满意~~(虽然是因为本来功能就不多)~~,然后就着手开始优化~ 应该会写成一系列文章,今天就从App的"我"页面开始吧~ 效果
2021-12-18 · Flutter · 约 1 分钟
Windows上怎么配置flutter开发环境?这篇文章手把手教你
前言 在Windows上做开发,一个字:坑。 但是有时候外出只能带着笔记本,Linux在一些笔记本上不太兼容,驱动支持不完美,而且电源管理不够好,所以无奈的情况下只能用Windows暂时顶一下,就像现在我要在Windows上搭建Flutter的开发环境一样,太坑了。 进入正题 先安装Android SDK 首先,你要有一种可以下载到Android SDK的方式,可以是梯子,或者是国内镜像,具体我就
2023-03-05 · Flutter · 约 1 分钟
讨论
共 0 条加载评论中…