-
Notifications
You must be signed in to change notification settings - Fork 28.9k
Closed
flutter/packages
#4159Closed
Copy link
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listp: shared_preferencesPlugin to read and write Shared PreferencesPlugin to read and write Shared Preferencespackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.
Description
As discussed in this StackOverflow question, the getInstance()
method in the SharedPreferences
plugin is not reliable when called from both a microtask and regular code. The problem seems to be that _instance == null
will be true until await _getSharedPreferencesMap();
has returned, and therefor it's possible to get two different instances from this singleton factory.
static Future<SharedPreferences> getInstance() async {
if (_instance == null) {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
_instance = SharedPreferences._(preferencesMap);
}
return _instance;
}
Steps to Reproduce
@override
void initState() {
initAsync();
super.initState();
}
initAsync() async {
scheduleMicrotask(() async {
print("microtask: START ");
TestClass microtask = await TestClass.getInstance("microtask");
print("microtask: FINISH (instance: ${microtask.hashCode})");
});
print("initAsync_1: START");
TestClass initAsync_1 = await TestClass.getInstance("initAsync_1");
print("initAsync_1: FINISH (instance: ${initAsync_1.hashCode})");
Future.delayed(Duration(seconds: 3)).then((_) async {
print("initAsync_2: START");
TestClass initAsync_2 = await TestClass.getInstance("initAsync_2");
print("initAsync_2: FINISH (instance: ${initAsync_2.hashCode})");
});
}
class TestClass {
static TestClass _instance;
static Future<TestClass> getInstance(String call) async {
if (_instance == null) {
print("$call: without instance before");
await Future.delayed(Duration(seconds: 3));
_instance = TestClass();
print("$call: without instance after");
} else {
print("$call: with instance");
}
return _instance;
}
}
Logs
initAsync_1: START
initAsync_1: without instance before
microtask: START
microtask: without instance before
initAsync_1: without instance after
initAsync_1: FINISH (instance: 674207757)
microtask: without instance after
microtask: FINISH (instance: 1059788553)
initAsync_2: START
initAsync_2: with instance
initAsync_2: FINISH (instance: 1059788553)
Possible solution
One possible solution would be to use a Completer
:
static Future<SharedPreferences> getInstance() async {
if( _initialization != null && !_initialization.isCompleted ) {
await _initialization.future;
}
if (_instance == null) {
_initialization = Completer<void>();
try {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
_instance = SharedPreferences._(preferencesMap);
}
finally{
_initialization.complete();
}
}
return _instance;
}
dfdgsdfg, orswen, sanekyy and neybero
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listp: shared_preferencesPlugin to read and write Shared PreferencesPlugin to read and write Shared Preferencespackageflutter/packages repository. See also p: labels.flutter/packages repository. See also p: labels.