Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_34031325
engine
提交
53fc019a
E
engine
项目概览
qq_34031325
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
53fc019a
编写于
11月 19, 2020
作者:
G
Gary Qian
提交者:
GitHub
11月 19, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Split AOT Android Embedder and shell (#22179)
上级
23a8e027
变更
29
隐藏空白更改
内联
并排
Showing
29 changed file
with
1242 addition
and
3 deletion
+1242
-3
DEPS
DEPS
+1
-1
ci/licenses_golden/licenses_flutter
ci/licenses_golden/licenses_flutter
+2
-0
shell/common/engine.cc
shell/common/engine.cc
+17
-0
shell/common/engine.h
shell/common/engine.h
+53
-0
shell/common/engine_unittests.cc
shell/common/engine_unittests.cc
+2
-0
shell/common/platform_view.cc
shell/common/platform_view.cc
+10
-0
shell/common/platform_view.h
shell/common/platform_view.h
+93
-0
shell/common/shell.cc
shell/common/shell.cc
+16
-0
shell/common/shell.h
shell/common/shell.h
+11
-0
shell/platform/android/BUILD.gn
shell/platform/android/BUILD.gn
+3
-0
shell/platform/android/embedding_bundle/build.gradle
shell/platform/android/embedding_bundle/build.gradle
+6
-0
shell/platform/android/io/flutter/FlutterInjector.java
shell/platform/android/io/flutter/FlutterInjector.java
+23
-2
shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
...rm/android/io/flutter/embedding/engine/FlutterEngine.java
+6
-0
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
...tform/android/io/flutter/embedding/engine/FlutterJNI.java
+114
-0
shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java
...bedding/engine/dynamicfeatures/DynamicFeatureManager.java
+154
-0
shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
...ngine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
+339
-0
shell/platform/android/jni/jni_mock.h
shell/platform/android/jni/jni_mock.h
+5
-0
shell/platform/android/jni/platform_view_android_jni.h
shell/platform/android/jni/platform_view_android_jni.h
+2
-0
shell/platform/android/platform_view_android.cc
shell/platform/android/platform_view_android.cc
+23
-0
shell/platform/android/platform_view_android.h
shell/platform/android/platform_view_android.h
+11
-0
shell/platform/android/platform_view_android_jni_impl.cc
shell/platform/android/platform_view_android_jni_impl.cc
+147
-0
shell/platform/android/platform_view_android_jni_impl.h
shell/platform/android/platform_view_android_jni_impl.h
+2
-0
shell/platform/android/test/io/flutter/FlutterInjectorTest.java
...platform/android/test/io/flutter/FlutterInjectorTest.java
+12
-0
shell/platform/android/test/io/flutter/FlutterTestSuite.java
shell/platform/android/test/io/flutter/FlutterTestSuite.java
+2
-0
shell/platform/android/test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java
...e/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java
+166
-0
shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm
...win/ios/framework/Source/FlutterEnginePlatformViewTest.mm
+5
-0
shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
...m/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
+5
-0
shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm
.../darwin/ios/framework/Source/accessibility_bridge_test.mm
+5
-0
shell/platform/fuchsia/flutter/platform_view_unittest.cc
shell/platform/fuchsia/flutter/platform_view_unittest.cc
+7
-0
未找到文件。
DEPS
浏览文件 @
53fc019a
...
...
@@ -482,7 +482,7 @@ deps = {
'packages': [
{
'package': 'flutter/android/embedding_bundle',
'version': 'last_updated:2020-0
5-20T01:36:16
-0700'
'version': 'last_updated:2020-0
9-11T17:57:41
-0700'
}
],
'condition': 'download_android_deps',
...
...
ci/licenses_golden/licenses_flutter
浏览文件 @
53fc019a
...
...
@@ -747,6 +747,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/Flutte
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/DartMessenger.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dart/PlatformMessageHandler.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/ApplicationInfoLoader.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterApplicationInfo.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
...
...
shell/common/engine.cc
浏览文件 @
53fc019a
...
...
@@ -507,4 +507,21 @@ const std::string& Engine::GetLastEntrypointLibrary() const {
return
last_entry_point_library_
;
}
// The Following commented out code connects into part 2 of the split AOT
// feature. Left commented out until it lands:
// // |RuntimeDelegate|
// void Engine::RequestDartDeferredLibrary(intptr_t loading_unit_id) {
// return delegate_.RequestDartDeferredLibrary(loading_unit_id);
// }
void
Engine
::
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
{
if
(
runtime_controller_
->
IsRootIsolateRunning
())
{
// runtime_controller_->LoadDartDeferredLibrary(loading_unit_id,
// snapshot_data, snapshot_instructions);
}
}
}
// namespace flutter
shell/common/engine.h
浏览文件 @
53fc019a
...
...
@@ -260,6 +260,21 @@ class Engine final : public RuntimeDelegate,
virtual
std
::
unique_ptr
<
std
::
vector
<
std
::
string
>>
ComputePlatformResolvedLocale
(
const
std
::
vector
<
std
::
string
>&
supported_locale_data
)
=
0
;
//--------------------------------------------------------------------------
/// @brief Invoked when the Dart VM requests that a deferred library
/// be loaded. Notifies the engine that the deferred library
/// identified by the specified loading unit id should be
/// downloaded and loaded into the Dart VM via
/// `LoadDartDeferredLibrary`
///
/// @param[in] loading_unit_id The unique id of the deferred library's
/// loading unit. This id is to be passed
/// back into LoadDartDeferredLibrary
/// in order to identify which deferred
/// library to load.
///
virtual
void
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
)
=
0
;
};
//----------------------------------------------------------------------------
...
...
@@ -767,6 +782,38 @@ class Engine final : public RuntimeDelegate,
///
const
std
::
string
&
InitialRoute
()
const
{
return
initial_route_
;
}
//--------------------------------------------------------------------------
/// @brief Loads the Dart shared library into the Dart VM. When the
/// Dart library is loaded successfully, the Dart future
/// returned by the originating loadLibrary() call completes.
///
/// The Dart compiler may generate separate shared libraries
/// files called 'loading units' when libraries are imported
/// as deferred. Each of these shared libraries are identified
/// by a unique loading unit id. Callers should dlopen the
/// shared library file and use dlsym to resolve the dart
/// symbols. These symbols can then be passed to this method to
/// be dynamically loaded into the VM.
///
/// This method is paired with a RequestDartDeferredLibrary
/// invocation that provides the embedder with the loading unit id
/// of the deferred library to load.
///
///
/// @param[in] loading_unit_id The unique id of the deferred library's
/// loading unit, as passed in by
/// RequestDartDeferredLibrary.
///
/// @param[in] snapshot_data Dart snapshot data of the loading unit's
/// shared library.
///
/// @param[in] snapshot_data Dart snapshot instructions of the loading
/// unit's shared library.
///
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
);
private:
Engine
::
Delegate
&
delegate_
;
const
Settings
settings_
;
...
...
@@ -815,6 +862,12 @@ class Engine final : public RuntimeDelegate,
std
::
unique_ptr
<
std
::
vector
<
std
::
string
>>
ComputePlatformResolvedLocale
(
const
std
::
vector
<
std
::
string
>&
supported_locale_data
)
override
;
// The Following commented out code connects into part 2 of the split AOT
// feature. Left commented out until it lands:
// // |RuntimeDelegate|
// void RequestDartDeferredLibrary(intptr_t loading_unit_id) override;
void
SetNeedsReportTimings
(
bool
value
)
override
;
void
StopAnimator
();
...
...
shell/common/engine_unittests.cc
浏览文件 @
53fc019a
...
...
@@ -32,6 +32,7 @@ class MockDelegate : public Engine::Delegate {
MOCK_METHOD1
(
ComputePlatformResolvedLocale
,
std
::
unique_ptr
<
std
::
vector
<
std
::
string
>>
(
const
std
::
vector
<
std
::
string
>&
));
MOCK_METHOD1
(
RequestDartDeferredLibrary
,
void
(
intptr_t
));
};
class
MockResponse
:
public
PlatformMessageResponse
{
...
...
@@ -55,6 +56,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
MOCK_METHOD1
(
ComputePlatformResolvedLocale
,
std
::
unique_ptr
<
std
::
vector
<
std
::
string
>>
(
const
std
::
vector
<
std
::
string
>&
));
MOCK_METHOD1
(
RequestDartDeferredLibrary
,
void
(
intptr_t
));
};
class
MockRuntimeController
:
public
RuntimeController
{
...
...
shell/common/platform_view.cc
浏览文件 @
53fc019a
...
...
@@ -159,4 +159,14 @@ PlatformView::ComputePlatformResolvedLocales(
return
out
;
}
void
PlatformView
::
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
)
{}
void
PlatformView
::
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
{}
void
PlatformView
::
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
{}
}
// namespace flutter
shell/common/platform_view.h
浏览文件 @
53fc019a
...
...
@@ -210,6 +210,43 @@ class PlatformView {
///
virtual
void
OnPlatformViewMarkTextureFrameAvailable
(
int64_t
texture_id
)
=
0
;
//--------------------------------------------------------------------------
/// @brief Loads the dart shared library into the dart VM. When the
/// dart library is loaded successfully, the dart future
/// returned by the originating loadLibrary() call completes.
///
/// The Dart compiler may generate separate shared library .so
/// files called 'loading units' when libraries are imported
/// as deferred. Each of these shared libraries are identified
/// by a unique loading unit id and can be dynamically loaded
/// into the VM by dlopen-ing and resolving the data and
/// instructions symbols.
///
///
/// @param[in] loading_unit_id The unique id of the deferred library's
/// loading unit.
///
/// @param[in] snapshot_data Dart snapshot data of the loading unit's
/// shared library.
///
/// @param[in] snapshot_data Dart snapshot instructions of the loading
/// unit's shared library.
///
virtual
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
=
0
;
// TODO(garyq): Implement a proper asset_resolver replacement instead of
// overwriting the entire asset manager.
//--------------------------------------------------------------------------
/// @brief Sets the asset manager of the engine to asset_manager
///
/// @param[in] asset_manager The asset manager to use.
///
virtual
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
=
0
;
};
//----------------------------------------------------------------------------
...
...
@@ -565,6 +602,62 @@ class PlatformView {
virtual
std
::
shared_ptr
<
ExternalViewEmbedder
>
CreateExternalViewEmbedder
();
//--------------------------------------------------------------------------
/// @brief Invoked when the dart VM requests that a deferred library
/// be loaded. Notifies the engine that the deferred library
/// identified by the specified loading unit id should be
/// downloaded and loaded into the Dart VM via
/// `LoadDartDeferredLibrary`
///
/// @param[in] loading_unit_id The unique id of the deferred library's
/// loading unit. This id is to be passed
/// back into LoadDartDeferredLibrary
/// in order to identify which deferred
/// library to load.
///
virtual
void
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
);
//--------------------------------------------------------------------------
/// @brief Loads the Dart shared library into the Dart VM. When the
/// Dart library is loaded successfully, the Dart future
/// returned by the originating loadLibrary() call completes.
///
/// The Dart compiler may generate separate shared libraries
/// files called 'loading units' when libraries are imported
/// as deferred. Each of these shared libraries are identified
/// by a unique loading unit id. Callers should dlopen the
/// shared library file and use dlsym to resolve the dart
/// symbols. These symbols can then be passed to this method to
/// be dynamically loaded into the VM.
///
/// This method is paired with a RequestDartDeferredLibrary
/// invocation that provides the embedder with the loading unit id
/// of the deferred library to load.
///
///
/// @param[in] loading_unit_id The unique id of the deferred library's
/// loading unit, as passed in by
/// RequestDartDeferredLibrary.
///
/// @param[in] snapshot_data Dart snapshot data of the loading unit's
/// shared library.
///
/// @param[in] snapshot_data Dart snapshot instructions of the loading
/// unit's shared library.
///
virtual
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
);
// TODO(garyq): Implement a proper asset_resolver replacement instead of
// overwriting the entire asset manager.
//--------------------------------------------------------------------------
/// @brief Sets the asset manager of the engine to asset_manager
///
/// @param[in] asset_manager The asset manager to use.
///
virtual
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
);
protected:
PlatformView
::
Delegate
&
delegate_
;
const
TaskRunners
task_runners_
;
...
...
shell/common/shell.cc
浏览文件 @
53fc019a
...
...
@@ -1185,6 +1185,22 @@ std::unique_ptr<std::vector<std::string>> Shell::ComputePlatformResolvedLocale(
return
platform_view_
->
ComputePlatformResolvedLocales
(
supported_locale_data
);
}
void
Shell
::
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
{
engine_
->
LoadDartDeferredLibrary
(
loading_unit_id
,
snapshot_data
,
snapshot_instructions
);
}
void
Shell
::
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
{
engine_
->
UpdateAssetManager
(
std
::
move
(
asset_manager
));
}
// |Engine::Delegate|
void
Shell
::
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
)
{
platform_view_
->
RequestDartDeferredLibrary
(
loading_unit_id
);
}
void
Shell
::
ReportTimings
()
{
FML_DCHECK
(
is_setup_
);
FML_DCHECK
(
task_runners_
.
GetRasterTaskRunner
()
->
RunsTasksOnCurrentThread
());
...
...
shell/common/shell.h
浏览文件 @
53fc019a
...
...
@@ -507,6 +507,14 @@ class Shell final : public PlatformView::Delegate,
// |PlatformView::Delegate|
void
OnPlatformViewSetNextFrameCallback
(
const
fml
::
closure
&
closure
)
override
;
// |PlatformView::Delegate|
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
override
;
// |PlatformView::Delegate|
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
override
;
// |Animator::Delegate|
void
OnAnimatorBeginFrame
(
fml
::
TimePoint
frame_target_time
)
override
;
...
...
@@ -548,6 +556,9 @@ class Shell final : public PlatformView::Delegate,
std
::
unique_ptr
<
std
::
vector
<
std
::
string
>>
ComputePlatformResolvedLocale
(
const
std
::
vector
<
std
::
string
>&
supported_locale_data
)
override
;
// |Engine::Delegate|
void
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
)
override
;
// |Rasterizer::Delegate|
void
OnFrameRasterized
(
const
FrameTiming
&
)
override
;
...
...
shell/platform/android/BUILD.gn
浏览文件 @
53fc019a
...
...
@@ -156,6 +156,8 @@ android_java_sources = [
"io/flutter/embedding/engine/dart/DartExecutor.java",
"io/flutter/embedding/engine/dart/DartMessenger.java",
"io/flutter/embedding/engine/dart/PlatformMessageHandler.java",
"io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java",
"io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java",
"io/flutter/embedding/engine/loader/ApplicationInfoLoader.java",
"io/flutter/embedding/engine/loader/FlutterApplicationInfo.java",
"io/flutter/embedding/engine/loader/FlutterLoader.java",
...
...
@@ -462,6 +464,7 @@ action("robolectric_tests") {
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/dart/DartExecutorTest.java",
"test/io/flutter/embedding/engine/dart/DartMessengerTest.java",
"test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java",
"test/io/flutter/embedding/engine/loader/ApplicationInfoLoaderTest.java",
"test/io/flutter/embedding/engine/loader/FlutterLoaderTest.java",
"test/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorViewTest.java",
...
...
shell/platform/android/embedding_bundle/build.gradle
浏览文件 @
53fc019a
...
...
@@ -48,6 +48,12 @@ android {
embedding
"androidx.lifecycle:lifecycle-common:$lifecycle_version"
embedding
"androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// This dependency is here to allow linking to Play core in tests, but
// is not used in a default Flutter app. This dependency should be manually
// added to the user's app gradle in order to opt into using split AOT
// dynamic features.
embedding
"com.google.android.play:core:1.8.0"
// Testing
// TODO(xster): remove these android-all compile time dependencies.
// Use https://github.com/robolectric/robolectric/blob/master/robolectric/src/main/java/org/robolectric/plugins/LegacyDependencyResolver.java#L24
...
...
shell/platform/android/io/flutter/FlutterInjector.java
浏览文件 @
53fc019a
...
...
@@ -5,7 +5,9 @@
package
io.flutter
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.VisibleForTesting
;
import
io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager
;
import
io.flutter.embedding.engine.loader.FlutterLoader
;
/**
...
...
@@ -62,11 +64,14 @@ public final class FlutterInjector {
instance
=
null
;
}
private
FlutterInjector
(
@NonNull
FlutterLoader
flutterLoader
)
{
private
FlutterInjector
(
@NonNull
FlutterLoader
flutterLoader
,
DynamicFeatureManager
dynamicFeatureManager
)
{
this
.
flutterLoader
=
flutterLoader
;
this
.
dynamicFeatureManager
=
dynamicFeatureManager
;
}
private
FlutterLoader
flutterLoader
;
private
DynamicFeatureManager
dynamicFeatureManager
;
/** Returns the {@link FlutterLoader} instance to use for the Flutter Android engine embedding. */
@NonNull
...
...
@@ -74,6 +79,15 @@ public final class FlutterInjector {
return
flutterLoader
;
}
/**
* Returns the {@link DynamicFeatureManager} instance to use for the Flutter Android engine
* embedding.
*/
@Nullable
public
DynamicFeatureManager
dynamicFeatureManager
()
{
return
dynamicFeatureManager
;
}
/**
* Builder used to supply a custom FlutterInjector instance to {@link
* FlutterInjector#setInstance(FlutterInjector)}.
...
...
@@ -82,6 +96,7 @@ public final class FlutterInjector {
*/
public
static
final
class
Builder
{
private
FlutterLoader
flutterLoader
;
private
DynamicFeatureManager
dynamicFeatureManager
;
/**
* Sets a {@link FlutterLoader} override.
*
...
...
@@ -92,10 +107,16 @@ public final class FlutterInjector {
return
this
;
}
public
Builder
setDynamicFeatureManager
(
@Nullable
DynamicFeatureManager
dynamicFeatureManager
)
{
this
.
dynamicFeatureManager
=
dynamicFeatureManager
;
return
this
;
}
private
void
fillDefaults
()
{
if
(
flutterLoader
==
null
)
{
flutterLoader
=
new
FlutterLoader
();
}
// DynamicFeatureManager's intended default is null.
}
/**
...
...
@@ -105,7 +126,7 @@ public final class FlutterInjector {
public
FlutterInjector
build
()
{
fillDefaults
();
return
new
FlutterInjector
(
flutterLoader
);
return
new
FlutterInjector
(
flutterLoader
,
dynamicFeatureManager
);
}
}
}
shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
浏览文件 @
53fc019a
...
...
@@ -299,6 +299,8 @@ public class FlutterEngine {
flutterJNI
.
addEngineLifecycleListener
(
engineLifecycleListener
);
flutterJNI
.
setPlatformViewsController
(
platformViewsController
);
flutterJNI
.
setLocalizationPlugin
(
localizationPlugin
);
flutterJNI
.
setDynamicFeatureManager
(
FlutterInjector
.
instance
().
dynamicFeatureManager
());
attachToJni
();
// TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if
...
...
@@ -374,7 +376,11 @@ public class FlutterEngine {
platformViewsController
.
onDetachedFromJNI
();
dartExecutor
.
onDetachedFromJNI
();
flutterJNI
.
removeEngineLifecycleListener
(
engineLifecycleListener
);
flutterJNI
.
setDynamicFeatureManager
(
null
);
flutterJNI
.
detachFromNativeAndReleaseResources
();
if
(
FlutterInjector
.
instance
().
dynamicFeatureManager
()
!=
null
)
{
FlutterInjector
.
instance
().
dynamicFeatureManager
().
destroy
();
}
}
/**
...
...
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
浏览文件 @
53fc019a
...
...
@@ -20,6 +20,7 @@ import androidx.annotation.VisibleForTesting;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener
;
import
io.flutter.embedding.engine.dart.PlatformMessageHandler
;
import
io.flutter.embedding.engine.dynamicfeatures.DynamicFeatureManager
;
import
io.flutter.embedding.engine.mutatorsstack.FlutterMutatorsStack
;
import
io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
;
import
io.flutter.embedding.engine.renderer.RenderSurface
;
...
...
@@ -225,6 +226,8 @@ public class FlutterJNI {
@Nullable
private
LocalizationPlugin
localizationPlugin
;
@Nullable
private
PlatformViewsController
platformViewsController
;
@Nullable
private
DynamicFeatureManager
dynamicFeatureManager
;
@NonNull
private
final
Set
<
EngineLifecycleListener
>
engineLifecycleListeners
=
new
CopyOnWriteArraySet
<>();
...
...
@@ -981,6 +984,117 @@ public class FlutterJNI {
// ----- End Localization Support ----
// ----- Start Dynamic Features Support ----
/** Sets the dynamic feature manager that is used to download and install split features. */
@UiThread
public
void
setDynamicFeatureManager
(
@Nullable
DynamicFeatureManager
dynamicFeatureManager
)
{
ensureRunningOnMainThread
();
this
.
dynamicFeatureManager
=
dynamicFeatureManager
;
if
(
dynamicFeatureManager
!=
null
)
{
dynamicFeatureManager
.
setJNI
(
this
);
}
}
/**
* Called by dart to request that a Dart deferred library corresponding to loadingUnitId be
* downloaded (if necessary) and loaded into the dart vm.
*
* <p>This method delegates the task to DynamicFeatureManager, which handles the download and
* loading of the dart library and any assets.
*
* @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is
* automatically retrieved when loadLibrary() is called on a dart deferred library.
*/
@SuppressWarnings
(
"unused"
)
@UiThread
public
void
requestDartDeferredLibrary
(
int
loadingUnitId
)
{
if
(
dynamicFeatureManager
!=
null
)
{
dynamicFeatureManager
.
downloadDynamicFeature
(
loadingUnitId
,
null
);
}
else
{
// TODO(garyq): Add link to setup/instructions guide wiki.
Log
.
e
(
TAG
,
"No DynamicFeatureManager found. Android setup must be completed before using split AOT dynamic features."
);
}
}
/**
* Searches each of the provided paths for a valid Dart shared library .so file and resolves
* symbols to load into the dart VM.
*
* <p>Successful loading of the dart library completes the future returned by loadLibrary() that
* triggered the install/load process.
*
* @param loadingUnitId The loadingUnitId is assigned during compile time by gen_snapshot and is
* automatically retrieved when loadLibrary() is called on a dart deferred library. This is
* used to identify which Dart deferred library the resolved correspond to.
* @param searchPaths An array of paths in which to look for valid dart shared libraries. This
* supports paths within zipped apks as long as the apks are not compressed using the
* `path/to/apk.apk!path/inside/apk/lib.so` format. Paths will be tried first to last and ends
* when a library is sucessfully found. When the found library is invalid, no additional paths
* will be attempted.
*/
@UiThread
public
void
loadDartDeferredLibrary
(
int
loadingUnitId
,
@NonNull
String
[]
searchPaths
)
{
ensureRunningOnMainThread
();
ensureAttachedToNative
();
nativeLoadDartDeferredLibrary
(
nativePlatformViewId
,
loadingUnitId
,
searchPaths
);
}
private
native
void
nativeLoadDartDeferredLibrary
(
long
nativePlatformViewId
,
int
loadingUnitId
,
@NonNull
String
[]
searchPaths
);
/**
* Adds the specified AssetManager as an APKAssetResolver in the Flutter Engine's AssetManager.
*
* <p>This may be used to update the engine AssetManager when a new dynamic feature is installed
* and a new Android AssetManager is created with access to new assets.
*
* @param assetManager An android AssetManager that is able to access the newly downloaded assets.
* @param assetBundlePath The subdirectory that the flutter assets are stored in. The typical
* value is `flutter_assets`.
*/
@UiThread
public
void
updateAssetManager
(
@NonNull
AssetManager
assetManager
,
@NonNull
String
assetBundlePath
)
{
ensureRunningOnMainThread
();
ensureAttachedToNative
();
nativeUpdateAssetManager
(
nativePlatformViewId
,
assetManager
,
assetBundlePath
);
}
private
native
void
nativeUpdateAssetManager
(
long
nativePlatformViewId
,
@NonNull
AssetManager
assetManager
,
@NonNull
String
assetBundlePath
);
/**
* Indicates that a failure was encountered during the Android portion of downloading a dynamic
* feature module and loading a dart deferred library, which is typically done by
* DynamicFeatureManager.
*
* <p>This will inform dart that the future returned by loadLibrary() should complete with an
* error.
*
* @param loadingUnitId The loadingUnitId that corresponds to the dart deferred library that
* failed to install.
* @param error The error message to display.
* @param isTransient When isTransient is false, new attempts to install will automatically result
* in same error in Dart before the request is passed to Android.
*/
@SuppressWarnings
(
"unused"
)
@UiThread
public
void
dynamicFeatureInstallFailure
(
int
loadingUnitId
,
@NonNull
String
error
,
boolean
isTransient
)
{
ensureRunningOnMainThread
();
nativeDynamicFeatureInstallFailure
(
loadingUnitId
,
error
,
isTransient
);
}
private
native
void
nativeDynamicFeatureInstallFailure
(
int
loadingUnitId
,
@NonNull
String
error
,
boolean
isTransient
);
// ----- End Dynamic Features Support ----
// @SuppressWarnings("unused")
@UiThread
public
void
onDisplayPlatformView
(
...
...
shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/DynamicFeatureManager.java
0 → 100644
浏览文件 @
53fc019a
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package
io.flutter.embedding.engine.dynamicfeatures
;
import
io.flutter.embedding.engine.FlutterJNI
;
// TODO: add links to external documentation on how to use split aot features.
/**
* Basic interface that handles downloading and loading of dynamic features.
*
* <p>Flutter dynamic feature support is still in early developer preview and should not be used in
* production apps yet.
*
* <p>The Flutter default implementation is PlayStoreDynamicFeatureManager.
*
* <p>DynamicFeatureManager handles the embedder/Android level tasks of downloading, installing, and
* loading Dart deferred libraries. A typical code-flow begins with a Dart call to loadLibrary() on
* deferred imported library. See https://dart.dev/guides/language/language-tour#deferred-loading
* This call retrieves a unique identifier called the loading unit id, which is assigned by
* gen_snapshot during compilation. The loading unit id is passed down through the engine and
* invokes downloadDynamicFeature. Once the feature module is downloaded, loadAssets and
* loadDartLibrary should be invoked. loadDartLibrary should find shared library .so files for the
* engine to open and pass the .so path to FlutterJNI.loadDartDeferredLibrary. loadAssets should
* typically ensure the new assets are available to the engine's asset manager by passing an updated
* Android AssetManager to the engine via FlutterJNI.updateAssetManager.
*
* <p>The loadAssets and loadDartLibrary methods are separated out because they may also be called
* manually via platform channel messages. A full downloadDynamicFeature implementation should call
* these two methods as needed.
*
* <p>A dynamic feature module is uniquely identified by a module name as defined in
* bundle_config.yaml. Each feature module may contain one or more loading units, uniquely
* identified by the loading unit ID and assets.
*/
public
interface
DynamicFeatureManager
{
/**
* Sets the FlutterJNI to be used to communication with the Flutter native engine.
*
* <p>A FlutterJNI is required in order to properly execute loadAssets and loadDartLibrary.
*
* <p>Since this class may be instantiated for injection before the FlutterEngine and FlutterJNI
* is fully initialized, this method should be called to provide the FlutterJNI instance to use
* for use in loadDartLibrary and loadAssets.
*/
public
abstract
void
setJNI
(
FlutterJNI
flutterJNI
);
/**
* Request that the feature module be downloaded and installed.
*
* <p>This method begins the download and installation of the specified feature module. For
* example, the Play Store dynamic delivery implementation uses SplitInstallManager to request the
* download of the module. Download is not complete when this method returns. The download process
* should be listened for and upon completion of download, listeners should invoke loadAssets
* first and then loadDartLibrary to complete the dynamic feature load process.
*
* <p>Both parameters are not always necessary to identify which module to install. Asset-only
* modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed
* to download only with moduleName. On the other hand, it can be possible to resolve the
* moduleName based on the loadingUnitId. This resolution is done if moduleName is null. At least
* one of loadingUnitId or moduleName must be valid or non-null.
*
* <p>Flutter will typically call this method in two ways. When invoked as part of a dart
* loadLibrary() call, a valid loadingUnitId is passed in while the moduleName is null. In this
* case, this method is responsible for figuring out what module the loadingUnitId corresponds to.
*
* <p>When invoked manually as part of loading an assets-only module, loadingUnitId is -1
* (invalid) and moduleName is supplied. Without a loadingUnitId, this method just downloads the
* module by name and attempts to load assets via loadAssets.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library. This id is
* assigned by the compiler and can be seen for reference in bundle_config.yaml. This ID is
* primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded.
* Loading unit ids range from 0 to the number existing loading units. Passing a negative
* loading unit id indicates that no Dart deferred library should be loaded after download
* completes. This is the case when the dynamic feature module is an assets-only module. If a
* negative loadingUnitId is passed, then moduleName must not be null. Passing a loadingUnitId
* larger than the highest valid loading unit's id will cause the Dart loadLibrary() to
* complete with a failure.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml. This may be
* null if the dynamic feature to be loaded is associated with a loading unit/deferred dart
* library. In this case, it is this method's responsibility to map the loadingUnitId to its
* corresponding moduleName. When loading asset-only or other dynamic features without an
* associated Dart deferred library, loading unit id should a negative value and moduleName
* must be non-null.
*/
public
abstract
void
downloadDynamicFeature
(
int
loadingUnitId
,
String
moduleName
);
/**
* Extract and load any assets and resources from the module for use by Flutter.
*
* <p>This method should provide a refreshed AssetManager to FlutterJNI.updateAssetManager that
* can access the new assets. If no assets are included as part of the dynamic feature, then
* nothing needs to be done.
*
* <p>If using the Play Store dynamic feature delivery, refresh the context via: {@code
* context.createPackageContext(context.getPackageName(), 0);} This returns a new context, from
* which an updated asset manager may be obtained and passed to updateAssetManager in FlutterJNI.
* This process does not require loadingUnitId or moduleName, however, the two parameters are
* still present for custom implementations that store assets outside of Android's native system.
*
* <p>Assets shoud be loaded before the Dart deferred library is loaded, as successful loading of
* the Dart loading unit indicates the dynamic feature is fully loaded. Implementations of
* downloadDynamicFeature should invoke this after successful download.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml.
*/
public
abstract
void
loadAssets
(
int
loadingUnitId
,
String
moduleName
);
/**
* Load the .so shared library file into the Dart VM.
*
* <p>When the download of a dynamic feature module completes, this method should be called to
* find the path .so library file. The path(s) should then be passed to
* FlutterJNI.loadDartDeferredLibrary to be dlopen-ed and loaded into the Dart VM.
*
* <p>Specifically, APKs distributed by Android's app bundle format may vary by device and API
* number, so FlutterJNI's loadDartDeferredLibrary accepts a list of search paths with can include
* paths within APKs that have not been unpacked using the
* `path/to/apk.apk!path/inside/apk/lib.so` format. Each search path will be attempted in order
* until a shared library is found. This allows for the developer to avoid unpacking the apk zip.
*
* <p>Upon successful load of the Dart library, the Dart future from the originating loadLibary()
* call completes and developers are able to use symbols and assets from the feature module.
*
* @param loadingUnitId The unique identifier associated with a Dart deferred library. This id is
* assigned by the compiler and can be seen for reference in bundle_config.yaml. This ID is
* primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded.
* Loading unit ids range from 0 to the number existing loading units. Negative loading unit
* ids are considered invalid and this method will result in a no-op.
* @param moduleName The dynamic feature module name as defined in bundle_config.yaml. If using
* Play Store dynamic feature delivery, this name corresponds to the root name on the
* installed APKs in which to search for the desired shared library .so file.
*/
public
abstract
void
loadDartLibrary
(
int
loadingUnitId
,
String
moduleName
);
/**
* Uninstall the specified feature module.
*
* <p>Both parameters are not always necessary to identify which module to uninstall. Asset-only
* modules do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be passed
* to download only with moduleName. On the other hand, it can be possible to resolve the
* moduleName based on the loadingUnitId. This resolution is done if moduleName is null. At least
* one of loadingUnitId or moduleName must be valid or non-null.
*/
public
abstract
void
uninstallFeature
(
int
loadingUnitId
,
String
moduleName
);
/**
* Cleans up and releases resources. This object is no longer usable after calling this method.
*/
public
abstract
void
destroy
();
}
shell/platform/android/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManager.java
0 → 100644
浏览文件 @
53fc019a
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package
io.flutter.embedding.engine.dynamicfeatures
;
import
android.content.Context
;
import
android.content.pm.PackageManager.NameNotFoundException
;
import
android.content.res.AssetManager
;
import
android.os.Build
;
import
androidx.annotation.NonNull
;
import
androidx.annotation.Nullable
;
import
com.google.android.play.core.splitinstall.SplitInstallException
;
import
com.google.android.play.core.splitinstall.SplitInstallManager
;
import
com.google.android.play.core.splitinstall.SplitInstallManagerFactory
;
import
com.google.android.play.core.splitinstall.SplitInstallRequest
;
import
com.google.android.play.core.splitinstall.SplitInstallSessionState
;
import
com.google.android.play.core.splitinstall.SplitInstallStateUpdatedListener
;
import
com.google.android.play.core.splitinstall.model.SplitInstallErrorCode
;
import
com.google.android.play.core.splitinstall.model.SplitInstallSessionStatus
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterJNI
;
import
java.io.File
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Queue
;
/**
* Flutter default implementation of DynamicFeatureManager that downloads dynamic feature modules
* from the Google Play store.
*/
public
class
PlayStoreDynamicFeatureManager
implements
DynamicFeatureManager
{
private
static
final
String
TAG
=
"PlayStoreDynamicFeatureManager"
;
private
@NonNull
SplitInstallManager
splitInstallManager
;
private
@Nullable
FlutterJNI
flutterJNI
;
private
@NonNull
Context
context
;
// Each request to install a feature module gets a session ID. These maps associate
// the session ID with the loading unit and module name that was requested.
private
@NonNull
Map
<
Integer
,
String
>
sessionIdToName
;
private
@NonNull
Map
<
Integer
,
Integer
>
sessionIdToLoadingUnitId
;
private
FeatureInstallStateUpdatedListener
listener
;
private
class
FeatureInstallStateUpdatedListener
implements
SplitInstallStateUpdatedListener
{
public
void
onStateUpdate
(
SplitInstallSessionState
state
)
{
if
(
sessionIdToName
.
containsKey
(
state
.
sessionId
()))
{
// TODO(garyq): Add system channel for split aot messages.
switch
(
state
.
status
())
{
case
SplitInstallSessionStatus
.
FAILED
:
{
Log
.
e
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) install failed with: %s"
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
(),
state
.
errorCode
()));
flutterJNI
.
dynamicFeatureInstallFailure
(
sessionIdToLoadingUnitId
.
get
(
state
.
sessionId
()),
"Module install failed with "
+
state
.
errorCode
(),
true
);
sessionIdToName
.
remove
(
state
.
sessionId
());
sessionIdToLoadingUnitId
.
remove
(
state
.
sessionId
());
break
;
}
case
SplitInstallSessionStatus
.
INSTALLED
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) install successfully."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
loadAssets
(
sessionIdToLoadingUnitId
.
get
(
state
.
sessionId
()),
sessionIdToName
.
get
(
state
.
sessionId
()));
// We only load Dart shared lib for the loading unit id requested. Other loading units
// (if present) in the dynamic feature module are not loaded, but can be loaded by
// calling again with their loading unit id.
loadDartLibrary
(
sessionIdToLoadingUnitId
.
get
(
state
.
sessionId
()),
sessionIdToName
.
get
(
state
.
sessionId
()));
sessionIdToName
.
remove
(
state
.
sessionId
());
sessionIdToLoadingUnitId
.
remove
(
state
.
sessionId
());
break
;
}
case
SplitInstallSessionStatus
.
CANCELED
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) install canceled."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
sessionIdToName
.
remove
(
state
.
sessionId
());
break
;
}
case
SplitInstallSessionStatus
.
CANCELING
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) install canceling."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
break
;
}
case
SplitInstallSessionStatus
.
PENDING
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) install pending."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
break
;
}
case
SplitInstallSessionStatus
.
REQUIRES_USER_CONFIRMATION
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) install requires user confirmation."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
break
;
}
case
SplitInstallSessionStatus
.
DOWNLOADING
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) downloading."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
break
;
}
case
SplitInstallSessionStatus
.
DOWNLOADED
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) downloaded."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
break
;
}
case
SplitInstallSessionStatus
.
INSTALLING
:
{
Log
.
d
(
TAG
,
String
.
format
(
"Module \"%s\" (sessionId %d) installing."
,
sessionIdToName
.
get
(
state
.
sessionId
()),
state
.
sessionId
()));
break
;
}
default
:
Log
.
d
(
TAG
,
"Status: "
+
state
.
status
());
}
}
}
}
public
PlayStoreDynamicFeatureManager
(
@NonNull
Context
context
,
@Nullable
FlutterJNI
flutterJNI
)
{
this
.
context
=
context
;
this
.
flutterJNI
=
flutterJNI
;
splitInstallManager
=
SplitInstallManagerFactory
.
create
(
context
);
listener
=
new
FeatureInstallStateUpdatedListener
();
splitInstallManager
.
registerListener
(
listener
);
sessionIdToName
=
new
HashMap
();
sessionIdToLoadingUnitId
=
new
HashMap
();
}
public
void
setJNI
(
@NonNull
FlutterJNI
flutterJNI
)
{
this
.
flutterJNI
=
flutterJNI
;
}
private
boolean
verifyJNI
()
{
if
(
flutterJNI
==
null
)
{
Log
.
e
(
TAG
,
"No FlutterJNI provided. `setJNI` must be called on the DynamicFeatureManager before attempting to load dart libraries or invoking with platform channels."
);
return
false
;
}
return
true
;
}
private
String
loadingUnitIdToModuleName
(
int
loadingUnitId
)
{
// Loading unit id to module name mapping stored in android Strings
// resources.
int
moduleNameIdentifier
=
context
.
getResources
()
.
getIdentifier
(
"loadingUnit"
+
loadingUnitId
,
"string"
,
context
.
getPackageName
());
return
context
.
getResources
().
getString
(
moduleNameIdentifier
);
}
public
void
downloadDynamicFeature
(
int
loadingUnitId
,
String
moduleName
)
{
String
resolvedModuleName
=
moduleName
==
null
?
moduleName
:
loadingUnitIdToModuleName
(
loadingUnitId
);
if
(
resolvedModuleName
==
null
)
{
Log
.
d
(
TAG
,
"Dynamic feature module name was null."
);
return
;
}
SplitInstallRequest
request
=
SplitInstallRequest
.
newBuilder
().
addModule
(
moduleName
).
build
();
splitInstallManager
// Submits the request to install the module through the
// asynchronous startInstall() task. Your app needs to be
// in the foreground to submit the request.
.
startInstall
(
request
)
// Called when the install request is sent successfully. This is different than a successful
// install which is handled in FeatureInstallStateUpdatedListener.
.
addOnSuccessListener
(
sessionId
->
{
this
.
sessionIdToName
.
put
(
sessionId
,
moduleName
);
this
.
sessionIdToLoadingUnitId
.
put
(
sessionId
,
loadingUnitId
);
})
.
addOnFailureListener
(
exception
->
{
switch
(((
SplitInstallException
)
exception
).
getErrorCode
())
{
case
SplitInstallErrorCode
.
NETWORK_ERROR
:
flutterJNI
.
dynamicFeatureInstallFailure
(
loadingUnitId
,
String
.
format
(
"Install of dynamic feature module \"%s\" failed with a network error"
,
moduleName
),
true
);
break
;
case
SplitInstallErrorCode
.
MODULE_UNAVAILABLE
:
flutterJNI
.
dynamicFeatureInstallFailure
(
loadingUnitId
,
String
.
format
(
"Install of dynamic feature module \"%s\" failed as it is unavailable"
,
moduleName
),
false
);
break
;
default
:
flutterJNI
.
dynamicFeatureInstallFailure
(
loadingUnitId
,
String
.
format
(
"Install of dynamic feature module \"%s\" failed with error %d: %s"
,
moduleName
,
((
SplitInstallException
)
exception
).
getErrorCode
(),
((
SplitInstallException
)
exception
).
getMessage
()),
false
);
break
;
}
});
}
public
void
loadAssets
(
int
loadingUnitId
,
String
moduleName
)
{
if
(!
verifyJNI
())
{
return
;
}
// Since android dynamic feature asset manager is handled through
// context, neither parameter is used here. Assets are stored in
// the apk's `assets` directory allowing them to be accessed by
// Android's AssetManager directly.
try
{
context
=
context
.
createPackageContext
(
context
.
getPackageName
(),
0
);
AssetManager
assetManager
=
context
.
getAssets
();
flutterJNI
.
updateAssetManager
(
assetManager
,
// TODO(garyq): Made the "flutter_assets" directory dynamic based off of DartEntryPoint.
"flutter_assets"
);
}
catch
(
NameNotFoundException
e
)
{
throw
new
RuntimeException
(
e
);
}
}
public
void
loadDartLibrary
(
int
loadingUnitId
,
String
moduleName
)
{
if
(!
verifyJNI
())
{
return
;
}
// Loading unit must be specified and valid to load a dart library.
if
(
loadingUnitId
<
0
)
{
return
;
}
// This matches/depends on dart's loading unit naming convention, which we use unchanged.
String
aotSharedLibraryName
=
"app.so-"
+
loadingUnitId
+
".part.so"
;
// Possible values: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64
String
abi
;
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
LOLLIPOP
)
{
abi
=
Build
.
SUPPORTED_ABIS
[
0
];
}
else
{
abi
=
Build
.
CPU_ABI
;
}
String
pathAbi
=
abi
.
replace
(
"-"
,
"_"
);
// abis are represented with underscores in paths.
// TODO(garyq): Optimize this apk/file discovery process to use less i/o and be more
// performant and robust.
// Search directly in APKs first
List
<
String
>
apkPaths
=
new
ArrayList
();
// If not found in APKs, we check in extracted native libs for the lib directly.
List
<
String
>
soPaths
=
new
ArrayList
();
Queue
<
File
>
searchFiles
=
new
LinkedList
();
searchFiles
.
add
(
context
.
getFilesDir
());
while
(!
searchFiles
.
isEmpty
())
{
File
file
=
searchFiles
.
remove
();
if
(
file
!=
null
&&
file
.
isDirectory
())
{
for
(
File
f
:
file
.
listFiles
())
{
searchFiles
.
add
(
f
);
}
continue
;
}
String
name
=
file
.
getName
();
if
(
name
.
endsWith
(
".apk"
)
&&
name
.
startsWith
(
moduleName
)
&&
name
.
contains
(
pathAbi
))
{
apkPaths
.
add
(
file
.
getAbsolutePath
());
continue
;
}
if
(
name
.
equals
(
aotSharedLibraryName
))
{
soPaths
.
add
(
file
.
getAbsolutePath
());
}
}
List
<
String
>
searchPaths
=
new
ArrayList
();
for
(
String
path
:
apkPaths
)
{
searchPaths
.
add
(
path
+
"!lib/"
+
abi
+
"/"
+
aotSharedLibraryName
);
}
for
(
String
path
:
soPaths
)
{
searchPaths
.
add
(
path
);
}
flutterJNI
.
loadDartDeferredLibrary
(
loadingUnitId
,
searchPaths
.
toArray
(
new
String
[
apkPaths
.
size
()]));
}
public
void
uninstallFeature
(
int
loadingUnitId
,
String
moduleName
)
{
// TODO(garyq): support uninstalling.
}
public
void
destroy
()
{
splitInstallManager
.
unregisterListener
(
listener
);
flutterJNI
=
null
;
}
}
shell/platform/android/jni/jni_mock.h
浏览文件 @
53fc019a
...
...
@@ -95,6 +95,11 @@ class JNIMock final : public PlatformViewAndroidJNI {
(
override
));
MOCK_METHOD
(
double
,
GetDisplayRefreshRate
,
(),
(
override
));
MOCK_METHOD
(
bool
,
RequestDartDeferredLibrary
,
(
int
loading_unit_id
),
(
override
));
};
}
// namespace flutter
...
...
shell/platform/android/jni/platform_view_android_jni.h
浏览文件 @
53fc019a
...
...
@@ -195,6 +195,8 @@ class PlatformViewAndroidJNI {
std
::
vector
<
std
::
string
>
supported_locales_data
)
=
0
;
virtual
double
GetDisplayRefreshRate
()
=
0
;
virtual
bool
RequestDartDeferredLibrary
(
int
loading_unit_id
)
=
0
;
};
}
// namespace flutter
...
...
shell/platform/android/platform_view_android.cc
浏览文件 @
53fc019a
...
...
@@ -336,6 +336,29 @@ PlatformViewAndroid::ComputePlatformResolvedLocales(
supported_locale_data
);
}
// |PlatformView|
void
PlatformViewAndroid
::
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
)
{
if
(
jni_facade_
->
RequestDartDeferredLibrary
(
loading_unit_id
))
{
return
;
}
return
;
// TODO(garyq): Call LoadDartDeferredLibraryFailure()
}
// |PlatformView|
void
PlatformViewAndroid
::
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
{
delegate_
.
LoadDartDeferredLibrary
(
loading_unit_id
,
snapshot_data
,
snapshot_instructions
);
}
// |PlatformView|
void
PlatformViewAndroid
::
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
{
delegate_
.
UpdateAssetManager
(
std
::
move
(
asset_manager
));
}
void
PlatformViewAndroid
::
InstallFirstFrameCallback
()
{
// On Platform Task Runner.
SetNextFrameCallback
(
...
...
shell/platform/android/platform_view_android.h
浏览文件 @
53fc019a
...
...
@@ -93,6 +93,14 @@ class PlatformViewAndroid final : public PlatformView {
int64_t
texture_id
,
const
fml
::
jni
::
JavaObjectWeakGlobalRef
&
surface_texture
);
// |PlatformView|
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
override
;
// |PlatformView|
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
override
;
private:
const
std
::
shared_ptr
<
PlatformViewAndroidJNI
>
jni_facade_
;
std
::
unique_ptr
<
AndroidContext
>
android_context_
;
...
...
@@ -137,6 +145,9 @@ class PlatformViewAndroid final : public PlatformView {
std
::
unique_ptr
<
std
::
vector
<
std
::
string
>>
ComputePlatformResolvedLocales
(
const
std
::
vector
<
std
::
string
>&
supported_locale_data
)
override
;
// |PlatformView|
void
RequestDartDeferredLibrary
(
intptr_t
loading_unit_id
)
override
;
void
InstallFirstFrameCallback
();
void
FireFirstFrameCallback
();
...
...
shell/platform/android/platform_view_android_jni_impl.cc
浏览文件 @
53fc019a
...
...
@@ -5,7 +5,9 @@
#include "flutter/shell/platform/android/platform_view_android_jni_impl.h"
#include <android/native_window_jni.h>
#include <dlfcn.h>
#include <jni.h>
#include <sstream>
#include <utility>
#include "unicode/uchar.h"
...
...
@@ -100,6 +102,8 @@ static jmethodID g_detach_from_gl_context_method = nullptr;
static
jmethodID
g_compute_platform_resolved_locale_method
=
nullptr
;
static
jmethodID
g_request_dart_deferred_library_method
=
nullptr
;
// Called By Java
static
jmethodID
g_on_display_platform_view_method
=
nullptr
;
...
...
@@ -508,6 +512,108 @@ static jboolean FlutterTextUtilsIsRegionalIndicator(JNIEnv* env,
jint
codePoint
)
{
return
u_hasBinaryProperty
(
codePoint
,
UProperty
::
UCHAR_REGIONAL_INDICATOR
);
}
static
void
LoadLoadingUnitFailure
(
intptr_t
loading_unit_id
,
std
::
string
message
,
bool
transient
)
{
// TODO(garyq): Implement
}
static
void
DynamicFeatureInstallFailure
(
JNIEnv
*
env
,
jobject
obj
,
jint
jLoadingUnitId
,
jstring
jError
,
jboolean
jTransient
)
{
LoadLoadingUnitFailure
(
static_cast
<
intptr_t
>
(
jLoadingUnitId
),
fml
::
jni
::
JavaStringToString
(
env
,
jError
),
static_cast
<
bool
>
(
jTransient
));
}
static
void
LoadDartDeferredLibrary
(
JNIEnv
*
env
,
jobject
obj
,
jlong
shell_holder
,
jint
jLoadingUnitId
,
jobjectArray
jSearchPaths
)
{
// Convert java->c++
intptr_t
loading_unit_id
=
static_cast
<
intptr_t
>
(
jLoadingUnitId
);
std
::
vector
<
std
::
string
>
search_paths
=
fml
::
jni
::
StringArrayToVector
(
env
,
jSearchPaths
);
// TODO: Switch to using the NativeLibrary class, eg:
//
// fml::RefPtr<fml::NativeLibrary> native_lib =
// fml::NativeLibrary::Create(lib_name.c_str());
//
// Find and open the shared library.
void
*
handle
=
nullptr
;
while
(
handle
==
nullptr
&&
!
search_paths
.
empty
())
{
std
::
string
path
=
search_paths
.
back
();
handle
=
::
dlopen
(
path
.
c_str
(),
RTLD_NOW
);
search_paths
.
pop_back
();
}
if
(
handle
==
nullptr
)
{
LoadLoadingUnitFailure
(
loading_unit_id
,
"No lib .so found for provided search paths."
,
true
);
return
;
}
// Resolve symbols.
uint8_t
*
isolate_data
=
static_cast
<
uint8_t
*>
(
::
dlsym
(
handle
,
DartSnapshot
::
kIsolateDataSymbol
));
if
(
isolate_data
==
nullptr
)
{
// Mac sometimes requires an underscore prefix.
std
::
stringstream
underscore_symbol_name
;
underscore_symbol_name
<<
"_"
<<
DartSnapshot
::
kIsolateDataSymbol
;
isolate_data
=
static_cast
<
uint8_t
*>
(
::
dlsym
(
handle
,
underscore_symbol_name
.
str
().
c_str
()));
if
(
isolate_data
==
nullptr
)
{
LoadLoadingUnitFailure
(
loading_unit_id
,
"Could not resolve data symbol in library"
,
true
);
return
;
}
}
uint8_t
*
isolate_instructions
=
static_cast
<
uint8_t
*>
(
::
dlsym
(
handle
,
DartSnapshot
::
kIsolateInstructionsSymbol
));
if
(
isolate_instructions
==
nullptr
)
{
// Mac sometimes requires an underscore prefix.
std
::
stringstream
underscore_symbol_name
;
underscore_symbol_name
<<
"_"
<<
DartSnapshot
::
kIsolateInstructionsSymbol
;
isolate_instructions
=
static_cast
<
uint8_t
*>
(
::
dlsym
(
handle
,
underscore_symbol_name
.
str
().
c_str
()));
if
(
isolate_data
==
nullptr
)
{
LoadLoadingUnitFailure
(
loading_unit_id
,
"Could not resolve instructions symbol in library"
,
true
);
return
;
}
}
ANDROID_SHELL_HOLDER
->
GetPlatformView
()
->
LoadDartDeferredLibrary
(
loading_unit_id
,
isolate_data
,
isolate_instructions
);
// TODO(garyq): fallback on soPath.
}
// TODO(garyq): persist additional asset resolvers by updating instead of
// replacing with newly created asset_manager
static
void
UpdateAssetManager
(
JNIEnv
*
env
,
jobject
obj
,
jlong
shell_holder
,
jobject
jAssetManager
,
jstring
jAssetBundlePath
)
{
auto
asset_manager
=
std
::
make_shared
<
flutter
::
AssetManager
>
();
asset_manager
->
PushBack
(
std
::
make_unique
<
flutter
::
APKAssetProvider
>
(
env
,
// jni environment
jAssetManager
,
// asset manager
fml
::
jni
::
JavaStringToString
(
env
,
jAssetBundlePath
))
// apk asset dir
);
// Create config to set persistent cache asset manager
RunConfiguration
config
(
nullptr
,
std
::
move
(
asset_manager
));
ANDROID_SHELL_HOLDER
->
GetPlatformView
()
->
UpdateAssetManager
(
config
.
GetAssetManager
());
}
bool
RegisterApi
(
JNIEnv
*
env
)
{
static
const
JNINativeMethod
flutter_jni_methods
[]
=
{
// Start of methods from FlutterJNI
...
...
@@ -664,6 +770,22 @@ bool RegisterApi(JNIEnv* env) {
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
FlutterTextUtilsIsRegionalIndicator
),
},
{
.
name
=
"nativeLoadDartDeferredLibrary"
,
.
signature
=
"(JI[Ljava/lang/String;)V"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
LoadDartDeferredLibrary
),
},
{
.
name
=
"nativeUpdateAssetManager"
,
.
signature
=
"(JLandroid/content/res/AssetManager;Ljava/lang/String;)V"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
UpdateAssetManager
),
},
{
.
name
=
"nativeDynamicFeatureInstallFailure"
,
.
signature
=
"(ILjava/lang/String;Z)V"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
DynamicFeatureInstallFailure
),
},
};
if
(
env
->
RegisterNatives
(
g_flutter_jni_class
->
obj
(),
flutter_jni_methods
,
...
...
@@ -907,6 +1029,14 @@ bool PlatformViewAndroid::Register(JNIEnv* env) {
return
false
;
}
g_request_dart_deferred_library_method
=
env
->
GetMethodID
(
g_flutter_jni_class
->
obj
(),
"requestDartDeferredLibrary"
,
"(I)V"
);
if
(
g_request_dart_deferred_library_method
==
nullptr
)
{
FML_LOG
(
ERROR
)
<<
"Could not locate requestDartDeferredLibrary method"
;
return
false
;
}
return
RegisterApi
(
env
);
}
...
...
@@ -1334,4 +1464,21 @@ double PlatformViewAndroidJNIImpl::GetDisplayRefreshRate() {
return
static_cast
<
double
>
(
env
->
GetStaticFloatField
(
clazz
,
fid
));
}
bool
PlatformViewAndroidJNIImpl
::
RequestDartDeferredLibrary
(
int
loading_unit_id
)
{
JNIEnv
*
env
=
fml
::
jni
::
AttachCurrentThread
();
auto
java_object
=
java_object_
.
get
(
env
);
if
(
java_object
.
is_null
())
{
return
true
;
}
env
->
CallObjectMethod
(
java_object
.
obj
(),
g_request_dart_deferred_library_method
,
loading_unit_id
);
FML_CHECK
(
CheckException
(
env
));
return
true
;
}
}
// namespace flutter
shell/platform/android/platform_view_android_jni_impl.h
浏览文件 @
53fc019a
...
...
@@ -80,6 +80,8 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI {
double
GetDisplayRefreshRate
()
override
;
bool
RequestDartDeferredLibrary
(
int
loading_unit_id
)
override
;
private:
// Reference to FlutterJNI object.
const
fml
::
jni
::
JavaObjectWeakGlobalRef
java_object_
;
...
...
shell/platform/android/test/io/flutter/FlutterInjectorTest.java
浏览文件 @
53fc019a
...
...
@@ -6,8 +6,10 @@ package io.flutter;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
junit
.
Assert
.
assertNull
;
import
static
org
.
junit
.
Assert
.
assertThrows
;
import
io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManager
;
import
io.flutter.embedding.engine.loader.FlutterLoader
;
import
org.junit.Before
;
import
org.junit.Test
;
...
...
@@ -21,6 +23,7 @@ import org.robolectric.annotation.Config;
@RunWith
(
RobolectricTestRunner
.
class
)
public
class
FlutterInjectorTest
{
@Mock
FlutterLoader
mockFlutterLoader
;
@Mock
PlayStoreDynamicFeatureManager
mockDynamicFeatureManager
;
@Before
public
void
setUp
()
{
...
...
@@ -34,6 +37,7 @@ public class FlutterInjectorTest {
// Implicitly builds when first accessed.
FlutterInjector
injector
=
FlutterInjector
.
instance
();
assertNotNull
(
injector
.
flutterLoader
());
assertNull
(
injector
.
dynamicFeatureManager
());
}
@Test
...
...
@@ -44,6 +48,14 @@ public class FlutterInjectorTest {
assertEquals
(
injector
.
flutterLoader
(),
mockFlutterLoader
);
}
@Test
public
void
canInjectDynamicFeatureManager
()
{
FlutterInjector
.
setInstance
(
new
FlutterInjector
.
Builder
().
setDynamicFeatureManager
(
mockDynamicFeatureManager
).
build
());
FlutterInjector
injector
=
FlutterInjector
.
instance
();
assertEquals
(
injector
.
dynamicFeatureManager
(),
mockDynamicFeatureManager
);
}
@Test
()
public
void
cannotBeChangedOnceRead
()
{
FlutterInjector
.
instance
();
...
...
shell/platform/android/test/io/flutter/FlutterTestSuite.java
浏览文件 @
53fc019a
...
...
@@ -18,6 +18,7 @@ import io.flutter.embedding.engine.LocalizationPluginTest;
import
io.flutter.embedding.engine.RenderingComponentTest
;
import
io.flutter.embedding.engine.dart.DartExecutorTest
;
import
io.flutter.embedding.engine.dart.DartMessengerTest
;
import
io.flutter.embedding.engine.dynamicfeatures.PlayStoreDynamicFeatureManagerTest
;
import
io.flutter.embedding.engine.loader.ApplicationInfoLoaderTest
;
import
io.flutter.embedding.engine.loader.FlutterLoaderTest
;
import
io.flutter.embedding.engine.mutatorsstack.FlutterMutatorViewTest
;
...
...
@@ -77,6 +78,7 @@ import test.io.flutter.embedding.engine.PluginComponentTest;
PlatformChannelTest
.
class
,
PlatformPluginTest
.
class
,
PlatformViewsControllerTest
.
class
,
PlayStoreDynamicFeatureManagerTest
.
class
,
PluginComponentTest
.
class
,
PreconditionsTest
.
class
,
RenderingComponentTest
.
class
,
...
...
shell/platform/android/test/io/flutter/embedding/engine/dynamicfeatures/PlayStoreDynamicFeatureManagerTest.java
0 → 100644
浏览文件 @
53fc019a
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package
io.flutter.embedding.engine.dynamicfeatures
;
import
static
junit
.
framework
.
TestCase
.
assertEquals
;
import
static
junit
.
framework
.
TestCase
.
assertTrue
;
import
static
org
.
mockito
.
Mockito
.
any
;
import
static
org
.
mockito
.
Mockito
.
anyInt
;
import
static
org
.
mockito
.
Mockito
.
doReturn
;
import
static
org
.
mockito
.
Mockito
.
spy
;
import
android.content.Context
;
import
android.content.pm.PackageManager.NameNotFoundException
;
import
android.content.res.AssetManager
;
import
androidx.annotation.NonNull
;
import
io.flutter.embedding.engine.FlutterJNI
;
import
java.io.File
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.RuntimeEnvironment
;
import
org.robolectric.annotation.Config
;
@Config
(
manifest
=
Config
.
NONE
)
@RunWith
(
RobolectricTestRunner
.
class
)
public
class
PlayStoreDynamicFeatureManagerTest
{
private
class
TestFlutterJNI
extends
FlutterJNI
{
public
int
loadDartDeferredLibraryCalled
=
0
;
public
int
updateAssetManagerCalled
=
0
;
public
int
dynamicFeatureInstallFailureCalled
=
0
;
public
String
[]
searchPaths
;
public
int
loadingUnitId
;
public
AssetManager
assetManager
;
public
TestFlutterJNI
()
{}
@Override
public
void
loadDartDeferredLibrary
(
int
loadingUnitId
,
@NonNull
String
[]
searchPaths
)
{
loadDartDeferredLibraryCalled
++;
this
.
searchPaths
=
searchPaths
;
this
.
loadingUnitId
=
loadingUnitId
;
}
@Override
public
void
updateAssetManager
(
@NonNull
AssetManager
assetManager
,
@NonNull
String
assetBundlePath
)
{
updateAssetManagerCalled
++;
this
.
loadingUnitId
=
loadingUnitId
;
this
.
assetManager
=
assetManager
;
}
@Override
public
void
dynamicFeatureInstallFailure
(
int
loadingUnitId
,
@NonNull
String
error
,
boolean
isTransient
)
{
dynamicFeatureInstallFailureCalled
++;
}
}
// Skips the download process to directly call the loadAssets and loadDartLibrary methods.
private
class
TestPlayStoreDynamicFeatureManager
extends
PlayStoreDynamicFeatureManager
{
public
TestPlayStoreDynamicFeatureManager
(
Context
context
,
FlutterJNI
jni
)
{
super
(
context
,
jni
);
}
@Override
public
void
downloadDynamicFeature
(
int
loadingUnitId
,
String
moduleName
)
{
// Override this to skip the online SplitInstallManager portion.
loadAssets
(
loadingUnitId
,
moduleName
);
loadDartLibrary
(
loadingUnitId
,
moduleName
);
}
}
@Test
public
void
downloadCallsJNIFunctions
()
throws
NameNotFoundException
{
TestFlutterJNI
jni
=
new
TestFlutterJNI
();
Context
spyContext
=
spy
(
RuntimeEnvironment
.
systemContext
);
doReturn
(
spyContext
).
when
(
spyContext
).
createPackageContext
(
any
(),
anyInt
());
doReturn
(
null
).
when
(
spyContext
).
getAssets
();
String
soTestPath
=
"test/path/app.so-123.part.so"
;
doReturn
(
new
File
(
soTestPath
)).
when
(
spyContext
).
getFilesDir
();
TestPlayStoreDynamicFeatureManager
playStoreManager
=
new
TestPlayStoreDynamicFeatureManager
(
spyContext
,
jni
);
jni
.
setDynamicFeatureManager
(
playStoreManager
);
assertEquals
(
jni
.
loadingUnitId
,
0
);
playStoreManager
.
downloadDynamicFeature
(
123
,
"TestModuleName"
);
assertEquals
(
jni
.
loadDartDeferredLibraryCalled
,
1
);
assertEquals
(
jni
.
updateAssetManagerCalled
,
1
);
assertEquals
(
jni
.
dynamicFeatureInstallFailureCalled
,
0
);
assertTrue
(
jni
.
searchPaths
[
0
].
endsWith
(
soTestPath
));
assertEquals
(
jni
.
searchPaths
.
length
,
1
);
assertEquals
(
jni
.
loadingUnitId
,
123
);
}
@Test
public
void
searchPathsAddsApks
()
throws
NameNotFoundException
{
TestFlutterJNI
jni
=
new
TestFlutterJNI
();
Context
spyContext
=
spy
(
RuntimeEnvironment
.
systemContext
);
doReturn
(
spyContext
).
when
(
spyContext
).
createPackageContext
(
any
(),
anyInt
());
doReturn
(
null
).
when
(
spyContext
).
getAssets
();
String
apkTestPath
=
"test/path/TestModuleName_armeabi_v7a.apk"
;
doReturn
(
new
File
(
apkTestPath
)).
when
(
spyContext
).
getFilesDir
();
TestPlayStoreDynamicFeatureManager
playStoreManager
=
new
TestPlayStoreDynamicFeatureManager
(
spyContext
,
jni
);
jni
.
setDynamicFeatureManager
(
playStoreManager
);
assertEquals
(
jni
.
loadingUnitId
,
0
);
playStoreManager
.
downloadDynamicFeature
(
123
,
"TestModuleName"
);
assertEquals
(
jni
.
loadDartDeferredLibraryCalled
,
1
);
assertEquals
(
jni
.
updateAssetManagerCalled
,
1
);
assertEquals
(
jni
.
dynamicFeatureInstallFailureCalled
,
0
);
assertTrue
(
jni
.
searchPaths
[
0
].
endsWith
(
apkTestPath
+
"!lib/armeabi-v7a/app.so-123.part.so"
));
assertEquals
(
jni
.
searchPaths
.
length
,
1
);
assertEquals
(
jni
.
loadingUnitId
,
123
);
}
@Test
public
void
invalidSearchPathsAreIgnored
()
throws
NameNotFoundException
{
TestFlutterJNI
jni
=
new
TestFlutterJNI
();
Context
spyContext
=
spy
(
RuntimeEnvironment
.
systemContext
);
doReturn
(
spyContext
).
when
(
spyContext
).
createPackageContext
(
any
(),
anyInt
());
doReturn
(
null
).
when
(
spyContext
).
getAssets
();
String
apkTestPath
=
"test/path/invalidpath.apk"
;
doReturn
(
new
File
(
apkTestPath
)).
when
(
spyContext
).
getFilesDir
();
TestPlayStoreDynamicFeatureManager
playStoreManager
=
new
TestPlayStoreDynamicFeatureManager
(
spyContext
,
jni
);
jni
.
setDynamicFeatureManager
(
playStoreManager
);
assertEquals
(
jni
.
loadingUnitId
,
0
);
playStoreManager
.
downloadDynamicFeature
(
123
,
"TestModuleName"
);
assertEquals
(
jni
.
loadDartDeferredLibraryCalled
,
1
);
assertEquals
(
jni
.
updateAssetManagerCalled
,
1
);
assertEquals
(
jni
.
dynamicFeatureInstallFailureCalled
,
0
);
assertEquals
(
jni
.
searchPaths
.
length
,
0
);
assertEquals
(
jni
.
loadingUnitId
,
123
);
}
@Test
public
void
assetManagerUpdateInvoked
()
throws
NameNotFoundException
{
TestFlutterJNI
jni
=
new
TestFlutterJNI
();
Context
spyContext
=
spy
(
RuntimeEnvironment
.
systemContext
);
doReturn
(
spyContext
).
when
(
spyContext
).
createPackageContext
(
any
(),
anyInt
());
AssetManager
assetManager
=
spyContext
.
getAssets
();
String
apkTestPath
=
"blah doesn't matter here"
;
doReturn
(
new
File
(
apkTestPath
)).
when
(
spyContext
).
getFilesDir
();
TestPlayStoreDynamicFeatureManager
playStoreManager
=
new
TestPlayStoreDynamicFeatureManager
(
spyContext
,
jni
);
jni
.
setDynamicFeatureManager
(
playStoreManager
);
assertEquals
(
jni
.
loadingUnitId
,
0
);
playStoreManager
.
downloadDynamicFeature
(
123
,
"TestModuleName"
);
assertEquals
(
jni
.
loadDartDeferredLibraryCalled
,
1
);
assertEquals
(
jni
.
updateAssetManagerCalled
,
1
);
assertEquals
(
jni
.
dynamicFeatureInstallFailureCalled
,
0
);
assertEquals
(
jni
.
assetManager
,
assetManager
);
}
}
shell/platform/darwin/ios/framework/Source/FlutterEnginePlatformViewTest.mm
浏览文件 @
53fc019a
...
...
@@ -33,6 +33,11 @@ class MockDelegate : public PlatformView::Delegate {
void
OnPlatformViewRegisterTexture
(
std
::
shared_ptr
<
Texture
>
texture
)
override
{}
void
OnPlatformViewUnregisterTexture
(
int64_t
texture_id
)
override
{}
void
OnPlatformViewMarkTextureFrameAvailable
(
int64_t
texture_id
)
override
{}
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
override
{}
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
override
{}
};
}
// namespace
...
...
shell/platform/darwin/ios/framework/Source/FlutterPlatformViewsTest.mm
浏览文件 @
53fc019a
...
...
@@ -103,6 +103,11 @@ class FlutterPlatformViewsTestMockPlatformViewDelegate : public PlatformView::De
void
OnPlatformViewRegisterTexture
(
std
::
shared_ptr
<
Texture
>
texture
)
override
{}
void
OnPlatformViewUnregisterTexture
(
int64_t
texture_id
)
override
{}
void
OnPlatformViewMarkTextureFrameAvailable
(
int64_t
texture_id
)
override
{}
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
override
{}
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
override
{}
};
}
// namespace
...
...
shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm
浏览文件 @
53fc019a
...
...
@@ -86,6 +86,11 @@ class MockDelegate : public PlatformView::Delegate {
void
OnPlatformViewRegisterTexture
(
std
::
shared_ptr
<
Texture
>
texture
)
override
{}
void
OnPlatformViewUnregisterTexture
(
int64_t
texture_id
)
override
{}
void
OnPlatformViewMarkTextureFrameAvailable
(
int64_t
texture_id
)
override
{}
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
override
{}
void
UpdateAssetManager
(
std
::
shared_ptr
<
AssetManager
>
asset_manager
)
override
{}
};
class
MockIosDelegate
:
public
AccessibilityBridge
::
IosDelegate
{
...
...
shell/platform/fuchsia/flutter/platform_view_unittest.cc
浏览文件 @
53fc019a
...
...
@@ -104,6 +104,13 @@ class MockPlatformViewDelegate : public flutter::PlatformView::Delegate {
const
std
::
vector
<
std
::
string
>&
supported_locale_data
)
{
return
nullptr
;
}
// |flutter::PlatformView::Delegate|
void
LoadDartDeferredLibrary
(
intptr_t
loading_unit_id
,
const
uint8_t
*
snapshot_data
,
const
uint8_t
*
snapshot_instructions
)
{}
// |flutter::PlatformView::Delegate|
void
UpdateAssetManager
(
std
::
shared_ptr
<
flutter
::
AssetManager
>
asset_manager
)
{}
flutter
::
Surface
*
surface
()
const
{
return
surface_
.
get
();
}
flutter
::
PlatformMessage
*
message
()
const
{
return
message_
.
get
();
}
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录