未验证 提交 3cbb5e20 编写于 作者: B Ben Konyi 提交者: GitHub

Persist DartCallbackCache contents across launches (#5947)

* Updated DartCallbackCache to write callback cache to disk which is
restored on engine startup
* Ensure cache isn't moved off disk in iOS
上级 953570a7
......@@ -502,6 +502,7 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/view/FlutterRunArgument
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/FlutterPluginAppLifeCycleDelegate.mm
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.h
FILE: ../../../flutter/shell/platform/darwin/ios/framework/Source/accessibility_text_entry.mm
......
......@@ -2,17 +2,39 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/lib/ui/plugins/callback_cache.h"
#include <fstream>
#include <iterator>
#include "flutter/fml/build_config.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/paths.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
#include "third_party/rapidjson/rapidjson/document.h"
#include "third_party/rapidjson/rapidjson/stringbuffer.h"
#include "third_party/rapidjson/rapidjson/writer.h"
#include "third_party/tonic/converter/dart_converter.h"
using rapidjson::Document;
using rapidjson::StringBuffer;
using rapidjson::Writer;
using tonic::ToDart;
namespace blink {
static const char* kHandleKey = "handle";
static const char* kRepresentationKey = "representation";
static const char* kNameKey = "name";
static const char* kClassNameKey = "class_name";
static const char* kLibraryPathKey = "library_path";
static const char* kCacheName = "flutter_callback_cache.json";
std::mutex DartCallbackCache::mutex_;
std::string DartCallbackCache::cache_path_;
std::map<int64_t, DartCallbackRepresentation> DartCallbackCache::cache_;
void DartCallbackCache::SetCachePath(const std::string& path) {
cache_path_ = fml::paths::JoinPaths({path, kCacheName});
}
Dart_Handle DartCallbackCache::GetCallback(int64_t handle) {
std::lock_guard<std::mutex> lock(mutex_);
auto iterator = cache_.find(handle);
......@@ -34,6 +56,7 @@ int64_t DartCallbackCache::GetCallbackHandle(const std::string& name,
if (cache_.find(hash) == cache_.end()) {
cache_[hash] = {name, class_name, library_path};
SaveCacheToDisk();
}
return hash;
}
......@@ -48,6 +71,82 @@ DartCallbackCache::GetCallbackInformation(int64_t handle) {
return nullptr;
}
void DartCallbackCache::SaveCacheToDisk() {
// Cache JSON format
// [
// {
// "hash": 42,
// "representation": {
// "name": "...",
// "class_name": "...",
// "library_path": "..."
// }
// },
// {
// ...
// }
// ]
StringBuffer s;
Writer<StringBuffer> writer(s);
writer.StartArray();
for (auto iterator = cache_.begin(); iterator != cache_.end(); ++iterator) {
int64_t hash = iterator->first;
DartCallbackRepresentation cb = iterator->second;
writer.StartObject();
writer.Key(kHandleKey);
writer.Int64(hash);
writer.Key(kRepresentationKey);
writer.StartObject();
writer.Key(kNameKey);
writer.String(cb.name.c_str());
writer.Key(kClassNameKey);
writer.String(cb.class_name.c_str());
writer.Key(kLibraryPathKey);
writer.String(cb.library_path.c_str());
writer.EndObject();
writer.EndObject();
}
writer.EndArray();
std::ofstream output(cache_path_);
output << s.GetString();
output.close();
}
void DartCallbackCache::LoadCacheFromDisk() {
std::lock_guard<std::mutex> lock(mutex_);
// Don't reload the cache if it's already populated.
if (!cache_.empty()) {
return;
}
std::ifstream input(cache_path_);
if (!input) {
return;
}
std::string cache_contents{std::istreambuf_iterator<char>(input),
std::istreambuf_iterator<char>()};
Document d;
d.Parse(cache_contents.c_str());
if (d.HasParseError() || !d.IsArray()) {
FML_LOG(WARNING) << "Could not parse callback cache, aborting restore";
// TODO(bkonyi): log and bail (delete cache?)
return;
}
const auto entries = d.GetArray();
for (auto it = entries.begin(); it != entries.end(); ++it) {
const auto root_obj = it->GetObject();
const auto representation = root_obj[kRepresentationKey].GetObject();
const int64_t hash = root_obj[kHandleKey].GetInt64();
DartCallbackRepresentation cb;
cb.name = representation[kNameKey].GetString();
cb.class_name = representation[kClassNameKey].GetString();
cb.library_path = representation[kLibraryPathKey].GetString();
cache_[hash] = cb;
}
}
Dart_Handle DartCallbackCache::LookupDartClosure(
const std::string& name,
const std::string& class_name,
......
......@@ -14,8 +14,6 @@
#include "flutter/fml/synchronization/thread_annotations.h"
#include "third_party/dart/runtime/include/dart_api.h"
#define DART_CALLBACK_INVALID_HANDLE -1
namespace blink {
typedef struct {
......@@ -26,6 +24,9 @@ typedef struct {
class DartCallbackCache {
public:
static void SetCachePath(const std::string& path);
static std::string GetCachePath() { return cache_path_; }
static int64_t GetCallbackHandle(const std::string& name,
const std::string& class_name,
const std::string& library_path)
......@@ -36,12 +37,17 @@ class DartCallbackCache {
static std::unique_ptr<DartCallbackRepresentation> GetCallbackInformation(
int64_t handle) FML_LOCKS_EXCLUDED(mutex_);
static void LoadCacheFromDisk() FML_LOCKS_EXCLUDED(mutex_);
private:
static Dart_Handle LookupDartClosure(const std::string& name,
const std::string& class_name,
const std::string& library_path);
static void SaveCacheToDisk() FML_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
static std::mutex mutex_;
static std::string cache_path_;
static std::map<int64_t, DartCallbackRepresentation> cache_
FML_GUARDED_BY(mutex_);
......
......@@ -15,6 +15,7 @@
#include "flutter/fml/message_loop.h"
#include "flutter/fml/paths.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
#include "flutter/runtime/dart_vm.h"
#include "flutter/runtime/start_up.h"
#include "flutter/shell/common/shell.h"
......@@ -44,7 +45,8 @@ void FlutterMain::Init(JNIEnv* env,
jclass clazz,
jobject context,
jobjectArray jargs,
jstring bundlePath) {
jstring bundlePath,
jstring appStoragePath) {
std::vector<std::string> args;
args.push_back("flutter");
for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
......@@ -56,6 +58,11 @@ void FlutterMain::Init(JNIEnv* env,
settings.assets_path = fml::jni::JavaStringToString(env, bundlePath);
// Restore the callback cache.
blink::DartCallbackCache::SetCachePath(
fml::jni::JavaStringToString(env, appStoragePath));
blink::DartCallbackCache::LoadCacheFromDisk();
if (!blink::DartVM::IsRunningPrecompiledCode()) {
// Check to see if the appropriate kernel files are present and configure
// settings accordingly.
......@@ -97,7 +104,7 @@ bool FlutterMain::Register(JNIEnv* env) {
{
.name = "nativeInit",
.signature = "(Landroid/content/Context;[Ljava/lang/String;Ljava/"
"lang/String;)V",
"lang/String;Ljava/lang/String;)V",
.fnPtr = reinterpret_cast<void*>(&Init),
},
{
......
......@@ -31,7 +31,8 @@ class FlutterMain {
jclass clazz,
jobject context,
jobjectArray jargs,
jstring bundlePath);
jstring bundlePath,
jstring appRootPath);
FML_DISALLOW_COPY_AND_ASSIGN(FlutterMain);
};
......
......@@ -7,6 +7,10 @@ package io.flutter.util;
import android.content.Context;
public final class PathUtils {
public static String getFilesDir(Context applicationContext) {
return applicationContext.getFilesDir().getPath();
}
public static String getDataDirectory(Context applicationContext) {
return applicationContext.getDir("flutter", Context.MODE_PRIVATE).getPath();
}
......
......@@ -219,8 +219,9 @@ public class FlutterMain {
}
String appBundlePath = findAppBundlePath(applicationContext);
String appStoragePath = PathUtils.getFilesDir(applicationContext);
nativeInit(applicationContext, shellArgs.toArray(new String[0]),
appBundlePath);
appBundlePath, appStoragePath);
sInitialized = true;
} catch (Exception e) {
......@@ -229,7 +230,7 @@ public class FlutterMain {
}
}
private static native void nativeInit(Context context, String[] args, String bundlePath);
private static native void nativeInit(Context context, String[] args, String bundlePath, String appStoragePath);
private static native void nativeRecordStartTimestamp(long initTimeMillis);
/**
......
......@@ -46,6 +46,7 @@ shared_library("create_flutter_framework_dylib") {
"framework/Source/FlutterAppDelegate.mm",
"framework/Source/FlutterAppDelegate_Internal.h",
"framework/Source/FlutterCallbackCache.mm",
"framework/Source/FlutterCallbackCache_Internal.h",
"framework/Source/FlutterChannels.mm",
"framework/Source/FlutterCodecs.mm",
"framework/Source/FlutterDartProject.mm",
......
......@@ -59,6 +59,15 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
- Returns: `NO` if this plugin vetoes application launch.
*/
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
*/
......@@ -293,8 +302,8 @@ NS_ASSUME_NONNULL_BEGIN
@end
/**
Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves to the application
life cycle events.
Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves
to the application life cycle events.
*/
@protocol FlutterAppLifeCycleProvider
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate;
......
......@@ -15,7 +15,8 @@ NS_ASSUME_NONNULL_BEGIN
FLUTTER_EXPORT
@interface FlutterPluginAppLifeCycleDelegate : NSObject
/**
Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as long as it is alive.
Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as
long as it is alive.
`delegate` will only referenced weakly.
*/
......@@ -29,6 +30,14 @@ FLUTTER_EXPORT
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
- Returns: `NO` if any plugin vetoes application launch.
*/
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks.
*/
......@@ -74,8 +83,8 @@ FLUTTER_EXPORT
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
some plugin handles the request.
- Returns: `YES` if any plugin handles the request.
*/
......@@ -84,16 +93,16 @@ FLUTTER_EXPORT
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
some plugin handles the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
some plugin handles the request.
- Returns: `YES` if any plugin handles the request.
*/
......@@ -111,8 +120,8 @@ FLUTTER_EXPORT
API_AVAILABLE(ios(9.0));
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
some plugin handles the request.
- Returns: `YES` if any plugin handles the request.
*/
......@@ -121,8 +130,8 @@ FLUTTER_EXPORT
completionHandler:(nonnull void (^)(void))completionHandler;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
some plugin handles the request.
- Returns: `YES` if any plugin handles the request.
*/
......@@ -130,8 +139,8 @@ FLUTTER_EXPORT
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
/**
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
the request.
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until
some plugin handles the request.
- Returns: `YES` if any plugin handles the request.
*/
- (BOOL)application:(UIApplication*)application
......
......@@ -22,6 +22,11 @@
[super dealloc];
}
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h"
#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
......@@ -23,4 +23,24 @@
return new_info;
}
@end
\ No newline at end of file
+ (void)setCachePath:(NSString*)path {
assert(path != nil);
blink::DartCallbackCache::SetCachePath([path UTF8String]);
NSString* cache_path =
[NSString stringWithUTF8String:blink::DartCallbackCache::GetCachePath().c_str()];
// Set the "Do Not Backup" flag to ensure that the cache isn't moved off disk in
// low-memory situations.
if (![[NSFileManager defaultManager] fileExistsAtPath:cache_path]) {
[[NSFileManager defaultManager] createFileAtPath:cache_path contents:nil attributes:nil];
NSError* error = nil;
NSURL* URL = [NSURL fileURLWithPath:cache_path];
BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
forKey:NSURLIsExcludedFromBackupKey
error:&error];
if (!success) {
NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
}
}
}
@end
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_FLUTTERCALLBACKCACHE_INTERNAL_H_
#define FLUTTER_FLUTTERCALLBACKCACHE_INTERNAL_H_
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCallbackCache.h"
@interface FlutterCallbackCache ()
+ (void)setCachePath:(NSString*)path;
@end
#endif // FLUTTER_FLUTTERCALLBACKCACHE_INTERNAL_H_
......@@ -4,7 +4,12 @@
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/paths.h"
#include "flutter/lib/ui/plugins/callback_cache.h"
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterCallbackCache_Internal.h"
static const char* kCallbackCacheSubDir = "Library/Caches/";
@implementation FlutterPluginAppLifeCycleDelegate {
UIBackgroundTaskIdentifier _debugBackgroundTask;
......@@ -15,6 +20,8 @@
- (instancetype)init {
if (self = [super init]) {
std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
[FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
_pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain];
}
return self;
......@@ -51,6 +58,22 @@ static BOOL isPowerOfTwo(NSUInteger x) {
return YES;
}
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
blink::DartCallbackCache::LoadCacheFromDisk();
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
if (!plugin) {
continue;
}
if ([plugin respondsToSelector:_cmd]) {
if (![plugin application:application willFinishLaunchingWithOptions:launchOptions]) {
return NO;
}
}
}
return YES;
}
// Returns the key window's rootViewController, if it's a FlutterViewController.
// Otherwise, returns nil.
- (FlutterViewController*)rootFlutterViewController {
......
......@@ -11171,37 +11171,6 @@ distribution.
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------------------
topaz
Copyright 2017, the Flutter project authors. Please see the AUTHORS file
for details. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册