Skip to content

Commit 3cbb5e2

Browse files
authored
Persist DartCallbackCache contents across launches (flutter#5947)
* Updated DartCallbackCache to write callback cache to disk which is restored on engine startup * Ensure cache isn't moved off disk in iOS
1 parent 953570a commit 3cbb5e2

15 files changed

+227
-56
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArgument
502502
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h
503503
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h
504504
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm
505+
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h
505506
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
506507
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h
507508
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm

lib/ui/plugins/callback_cache.cc

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,39 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
#include "flutter/lib/ui/plugins/callback_cache.h"
5+
#include <fstream>
6+
#include <iterator>
7+
8+
#include "flutter/fml/build_config.h"
69
#include "flutter/fml/logging.h"
10+
#include "flutter/fml/paths.h"
11+
#include "flutter/lib/ui/plugins/callback_cache.h"
12+
#include "third_party/rapidjson/rapidjson/document.h"
13+
#include "third_party/rapidjson/rapidjson/stringbuffer.h"
14+
#include "third_party/rapidjson/rapidjson/writer.h"
715
#include "third_party/tonic/converter/dart_converter.h"
816

17+
using rapidjson::Document;
18+
using rapidjson::StringBuffer;
19+
using rapidjson::Writer;
920
using tonic::ToDart;
1021

1122
namespace blink {
1223

24+
static const char* kHandleKey = "handle";
25+
static const char* kRepresentationKey = "representation";
26+
static const char* kNameKey = "name";
27+
static const char* kClassNameKey = "class_name";
28+
static const char* kLibraryPathKey = "library_path";
29+
static const char* kCacheName = "flutter_callback_cache.json";
1330
std::mutex DartCallbackCache::mutex_;
31+
std::string DartCallbackCache::cache_path_;
1432
std::map<int64_t, DartCallbackRepresentation> DartCallbackCache::cache_;
1533

34+
void DartCallbackCache::SetCachePath(const std::string& path) {
35+
cache_path_ = fml::paths::JoinPaths({path, kCacheName});
36+
}
37+
1638
Dart_Handle DartCallbackCache::GetCallback(int64_t handle) {
1739
std::lock_guard<std::mutex> lock(mutex_);
1840
auto iterator = cache_.find(handle);
@@ -34,6 +56,7 @@ int64_t DartCallbackCache::GetCallbackHandle(const std::string& name,
3456

3557
if (cache_.find(hash) == cache_.end()) {
3658
cache_[hash] = {name, class_name, library_path};
59+
SaveCacheToDisk();
3760
}
3861
return hash;
3962
}
@@ -48,6 +71,82 @@ DartCallbackCache::GetCallbackInformation(int64_t handle) {
4871
return nullptr;
4972
}
5073

74+
void DartCallbackCache::SaveCacheToDisk() {
75+
// Cache JSON format
76+
// [
77+
// {
78+
// "hash": 42,
79+
// "representation": {
80+
// "name": "...",
81+
// "class_name": "...",
82+
// "library_path": "..."
83+
// }
84+
// },
85+
// {
86+
// ...
87+
// }
88+
// ]
89+
StringBuffer s;
90+
Writer<StringBuffer> writer(s);
91+
writer.StartArray();
92+
for (auto iterator = cache_.begin(); iterator != cache_.end(); ++iterator) {
93+
int64_t hash = iterator->first;
94+
DartCallbackRepresentation cb = iterator->second;
95+
writer.StartObject();
96+
writer.Key(kHandleKey);
97+
writer.Int64(hash);
98+
writer.Key(kRepresentationKey);
99+
writer.StartObject();
100+
writer.Key(kNameKey);
101+
writer.String(cb.name.c_str());
102+
writer.Key(kClassNameKey);
103+
writer.String(cb.class_name.c_str());
104+
writer.Key(kLibraryPathKey);
105+
writer.String(cb.library_path.c_str());
106+
writer.EndObject();
107+
writer.EndObject();
108+
}
109+
writer.EndArray();
110+
111+
std::ofstream output(cache_path_);
112+
output << s.GetString();
113+
output.close();
114+
}
115+
116+
void DartCallbackCache::LoadCacheFromDisk() {
117+
std::lock_guard<std::mutex> lock(mutex_);
118+
119+
// Don't reload the cache if it's already populated.
120+
if (!cache_.empty()) {
121+
return;
122+
}
123+
std::ifstream input(cache_path_);
124+
if (!input) {
125+
return;
126+
}
127+
std::string cache_contents{std::istreambuf_iterator<char>(input),
128+
std::istreambuf_iterator<char>()};
129+
Document d;
130+
d.Parse(cache_contents.c_str());
131+
if (d.HasParseError() || !d.IsArray()) {
132+
FML_LOG(WARNING) << "Could not parse callback cache, aborting restore";
133+
// TODO(bkonyi): log and bail (delete cache?)
134+
return;
135+
}
136+
const auto entries = d.GetArray();
137+
for (auto it = entries.begin(); it != entries.end(); ++it) {
138+
const auto root_obj = it->GetObject();
139+
const auto representation = root_obj[kRepresentationKey].GetObject();
140+
141+
const int64_t hash = root_obj[kHandleKey].GetInt64();
142+
DartCallbackRepresentation cb;
143+
cb.name = representation[kNameKey].GetString();
144+
cb.class_name = representation[kClassNameKey].GetString();
145+
cb.library_path = representation[kLibraryPathKey].GetString();
146+
cache_[hash] = cb;
147+
}
148+
}
149+
51150
Dart_Handle DartCallbackCache::LookupDartClosure(
52151
const std::string& name,
53152
const std::string& class_name,

lib/ui/plugins/callback_cache.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
#include "flutter/fml/synchronization/thread_annotations.h"
1515
#include "third_party/dart/runtime/include/dart_api.h"
1616

17-
#define DART_CALLBACK_INVALID_HANDLE -1
18-
1917
namespace blink {
2018

2119
typedef struct {
@@ -26,6 +24,9 @@ typedef struct {
2624

2725
class DartCallbackCache {
2826
public:
27+
static void SetCachePath(const std::string& path);
28+
static std::string GetCachePath() { return cache_path_; }
29+
2930
static int64_t GetCallbackHandle(const std::string& name,
3031
const std::string& class_name,
3132
const std::string& library_path)
@@ -36,12 +37,17 @@ class DartCallbackCache {
3637
static std::unique_ptr<DartCallbackRepresentation> GetCallbackInformation(
3738
int64_t handle) FML_LOCKS_EXCLUDED(mutex_);
3839

40+
static void LoadCacheFromDisk() FML_LOCKS_EXCLUDED(mutex_);
41+
3942
private:
4043
static Dart_Handle LookupDartClosure(const std::string& name,
4144
const std::string& class_name,
4245
const std::string& library_path);
4346

47+
static void SaveCacheToDisk() FML_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
48+
4449
static std::mutex mutex_;
50+
static std::string cache_path_;
4551

4652
static std::map<int64_t, DartCallbackRepresentation> cache_
4753
FML_GUARDED_BY(mutex_);

shell/platform/android/flutter_main.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "flutter/fml/message_loop.h"
1616
#include "flutter/fml/paths.h"
1717
#include "flutter/fml/platform/android/jni_util.h"
18+
#include "flutter/lib/ui/plugins/callback_cache.h"
1819
#include "flutter/runtime/dart_vm.h"
1920
#include "flutter/runtime/start_up.h"
2021
#include "flutter/shell/common/shell.h"
@@ -44,7 +45,8 @@ void FlutterMain::Init(JNIEnv* env,
4445
jclass clazz,
4546
jobject context,
4647
jobjectArray jargs,
47-
jstring bundlePath) {
48+
jstring bundlePath,
49+
jstring appStoragePath) {
4850
std::vector<std::string> args;
4951
args.push_back("flutter");
5052
for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
@@ -56,6 +58,11 @@ void FlutterMain::Init(JNIEnv* env,
5658

5759
settings.assets_path = fml::jni::JavaStringToString(env, bundlePath);
5860

61+
// Restore the callback cache.
62+
blink::DartCallbackCache::SetCachePath(
63+
fml::jni::JavaStringToString(env, appStoragePath));
64+
blink::DartCallbackCache::LoadCacheFromDisk();
65+
5966
if (!blink::DartVM::IsRunningPrecompiledCode()) {
6067
// Check to see if the appropriate kernel files are present and configure
6168
// settings accordingly.
@@ -97,7 +104,7 @@ bool FlutterMain::Register(JNIEnv* env) {
97104
{
98105
.name = "nativeInit",
99106
.signature = "(Landroid/content/Context;[Ljava/lang/String;Ljava/"
100-
"lang/String;)V",
107+
"lang/String;Ljava/lang/String;)V",
101108
.fnPtr = reinterpret_cast<void*>(&Init),
102109
},
103110
{

shell/platform/android/flutter_main.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ class FlutterMain {
3131
jclass clazz,
3232
jobject context,
3333
jobjectArray jargs,
34-
jstring bundlePath);
34+
jstring bundlePath,
35+
jstring appRootPath);
3536

3637
FML_DISALLOW_COPY_AND_ASSIGN(FlutterMain);
3738
};

shell/platform/android/io/flutter/util/PathUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
import android.content.Context;
88

99
public final class PathUtils {
10+
public static String getFilesDir(Context applicationContext) {
11+
return applicationContext.getFilesDir().getPath();
12+
}
13+
1014
public static String getDataDirectory(Context applicationContext) {
1115
return applicationContext.getDir("flutter", Context.MODE_PRIVATE).getPath();
1216
}

shell/platform/android/io/flutter/view/FlutterMain.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,9 @@ public static void ensureInitializationComplete(Context applicationContext, Stri
219219
}
220220

221221
String appBundlePath = findAppBundlePath(applicationContext);
222+
String appStoragePath = PathUtils.getFilesDir(applicationContext);
222223
nativeInit(applicationContext, shellArgs.toArray(new String[0]),
223-
appBundlePath);
224+
appBundlePath, appStoragePath);
224225

225226
sInitialized = true;
226227
} catch (Exception e) {
@@ -229,7 +230,7 @@ public static void ensureInitializationComplete(Context applicationContext, Stri
229230
}
230231
}
231232

232-
private static native void nativeInit(Context context, String[] args, String bundlePath);
233+
private static native void nativeInit(Context context, String[] args, String bundlePath, String appStoragePath);
233234
private static native void nativeRecordStartTimestamp(long initTimeMillis);
234235

235236
/**

shell/platform/darwin/ios/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ shared_library("create_flutter_framework_dylib") {
4646
"framework/Source/FlutterAppDelegate.mm",
4747
"framework/Source/FlutterAppDelegate_Internal.h",
4848
"framework/Source/FlutterCallbackCache.mm",
49+
"framework/Source/FlutterCallbackCache_Internal.h",
4950
"framework/Source/FlutterChannels.mm",
5051
"framework/Source/FlutterCodecs.mm",
5152
"framework/Source/FlutterDartProject.mm",

shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ NS_ASSUME_NONNULL_BEGIN
5959
*/
6060
- (BOOL)application:(UIApplication*)application
6161
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
62+
63+
/**
64+
Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
65+
66+
- Returns: `NO` if this plugin vetoes application launch.
67+
*/
68+
- (BOOL)application:(UIApplication*)application
69+
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
70+
6271
/**
6372
Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
6473
*/
@@ -293,8 +302,8 @@ NS_ASSUME_NONNULL_BEGIN
293302
@end
294303

295304
/**
296-
Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves to the application
297-
life cycle events.
305+
Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves
306+
to the application life cycle events.
298307
*/
299308
@protocol FlutterAppLifeCycleProvider
300309
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate;

shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN
1515
FLUTTER_EXPORT
1616
@interface FlutterPluginAppLifeCycleDelegate : NSObject
1717
/**
18-
Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as long as it is alive.
18+
Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as
19+
long as it is alive.
1920
2021
`delegate` will only referenced weakly.
2122
*/
@@ -29,6 +30,14 @@ FLUTTER_EXPORT
2930
- (BOOL)application:(UIApplication*)application
3031
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
3132

33+
/**
34+
Calls all plugins registered for `UIApplicationDelegate` callbacks.
35+
36+
- Returns: `NO` if any plugin vetoes application launch.
37+
*/
38+
- (BOOL)application:(UIApplication*)application
39+
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
40+
3241
/**
3342
Calls all plugins registered for `UIApplicationDelegate` callbacks.
3443
*/
@@ -74,8 +83,8 @@ FLUTTER_EXPORT
7483
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
7584

7685
/**
77-
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
78-
the request.
86+
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
87+
some plugin handles the request.
7988
8089
- Returns: `YES` if any plugin handles the request.
8190
*/
@@ -84,16 +93,16 @@ FLUTTER_EXPORT
8493
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options;
8594

8695
/**
87-
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
88-
the request.
96+
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
97+
some plugin handles the request.
8998
9099
- Returns: `YES` if any plugin handles the request.
91100
*/
92101
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url;
93102

94103
/**
95-
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
96-
the request.
104+
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
105+
some plugin handles the request.
97106
98107
- Returns: `YES` if any plugin handles the request.
99108
*/
@@ -111,8 +120,8 @@ FLUTTER_EXPORT
111120
API_AVAILABLE(ios(9.0));
112121

113122
/**
114-
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
115-
the request.
123+
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
124+
some plugin handles the request.
116125
117126
- Returns: `YES` if any plugin handles the request.
118127
*/
@@ -121,17 +130,17 @@ FLUTTER_EXPORT
121130
completionHandler:(nonnull void (^)(void))completionHandler;
122131

123132
/**
124-
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
125-
the request.
133+
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
134+
some plugin handles the request.
126135
127136
- Returns: `YES` if any plugin handles the request.
128137
*/
129138
- (BOOL)application:(UIApplication*)application
130139
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
131140

132141
/**
133-
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
134-
the request.
142+
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
143+
some plugin handles the request.
135144
- Returns: `YES` if any plugin handles the request.
136145
*/
137146
- (BOOL)application:(UIApplication*)application

shell/platform/darwin/ios/framework/Source/FlutterAppDelegate.mm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ - (void)dealloc {
2222
[super dealloc];
2323
}
2424

25+
- (BOOL)application:(UIApplication*)application
26+
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
27+
return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
28+
}
29+
2530
- (BOOL)application:(UIApplication*)application
2631
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
2732
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];

0 commit comments

Comments
 (0)