Flutter Engine
The Flutter Engine
DeferredComponentManager.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.embedding.engine.deferredcomponents;
6
7import io.flutter.embedding.engine.FlutterJNI;
8import io.flutter.embedding.engine.systemchannels.DeferredComponentChannel;
9
10// TODO: add links to external documentation on how to use split aot features.
11/**
12 * Basic interface that handles downloading and loading of deferred components.
13 *
14 * <p>Flutter deferred component support is still in early developer preview and should not be used
15 * in production apps yet.
16 *
17 * <p>The Flutter default implementation is PlayStoreDeferredComponentManager.
18 *
19 * <p>DeferredComponentManager handles the embedder/Android level tasks of downloading, installing,
20 * and loading Dart deferred libraries. A typical code-flow begins with a Dart call to loadLibrary()
21 * on deferred imported library. See https://dart.dev/guides/language/language-tour#deferred-loading
22 * This call retrieves a unique identifier called the loading unit id, which is assigned by
23 * gen_snapshot during compilation. The loading unit id is passed down through the engine and
24 * invokes installDeferredComponent. Once the component is downloaded, loadAssets and
25 * loadDartLibrary should be invoked. loadDartLibrary should find shared library .so files for the
26 * engine to open and pass the .so path to FlutterJNI.loadDartDeferredLibrary. loadAssets should
27 * typically ensure the new assets are available to the engine's asset manager by passing an updated
28 * Android AssetManager to the engine via FlutterJNI.updateAssetManager.
29 *
30 * <p>The loadAssets and loadDartLibrary methods are separated out because they may also be called
31 * manually via platform channel messages. A full installDeferredComponent implementation should
32 * call these two methods as needed.
33 *
34 * <p>A deferred component is uniquely identified by a component name as defined in
35 * bundle_config.yaml. Each component may contain one or more loading units, uniquely identified by
36 * the loading unit ID and assets.
37 */
38public interface DeferredComponentManager {
39 /**
40 * Sets the FlutterJNI to be used to communication with the Flutter native engine.
41 *
42 * <p>A FlutterJNI is required in order to properly execute loadAssets and loadDartLibrary.
43 *
44 * <p>Since this class may be instantiated for injection before the FlutterEngine and FlutterJNI
45 * is fully initialized, this method should be called to provide the FlutterJNI instance to use
46 * for use in loadDartLibrary and loadAssets.
47 */
48 public abstract void setJNI(FlutterJNI flutterJNI);
49
50 /**
51 * Sets the DeferredComponentChannel system channel to handle the framework API to directly call
52 * methods in DeferredComponentManager.
53 *
54 * <p>A DeferredComponentChannel is required to handle assets-only deferred components and
55 * manually installed deferred components.
56 *
57 * <p>Since this class may be instantiated for injection before the FlutterEngine and System
58 * Channels are initialized, this method should be called to provide the DeferredComponentChannel.
59 * Similarly, the {@link DeferredComponentChannel.setDeferredComponentManager} method should also
60 * be called with this DeferredComponentManager instance to properly forward method invocations.
61 *
62 * <p>The {@link DeferredComponentChannel} passes manual invocations of {@link
63 * installDeferredComponent} and {@link getDeferredComponentInstallState} from the method channel
64 * to this DeferredComponentManager. Upon completion of the install process, successful
65 * installations should notify the DeferredComponentChannel by calling {@link
66 * DeferredComponentChannel.completeInstallSuccess} while errors and failures should call {@link
67 * DeferredComponentChannel.completeInstallError}.
68 */
70
71 /**
72 * Request that the deferred component be downloaded and installed.
73 *
74 * <p>This method begins the download and installation of the specified deferred component. For
75 * example, the Play Store dynamic delivery implementation uses SplitInstallManager to request the
76 * download of the component. Download is not complete when this method returns. The download
77 * process should be listened for and upon completion of download, listeners should invoke
78 * loadAssets first and then loadDartLibrary to complete the deferred component load process.
79 * Assets-only deferred components should also call {@link
80 * DeferredComponentChannel.completeInstallSuccess} or {@link
81 * DeferredComponentChannel.completeInstallError} to complete the method channel invocation's dart
82 * Future.
83 *
84 * <p>Both parameters are not always necessary to identify which component to install. Asset-only
85 * components do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be
86 * passed to download only with componentName. On the other hand, it can be possible to resolve
87 * the componentName based on the loadingUnitId. This resolution is done if componentName is null.
88 * At least one of loadingUnitId or componentName must be valid or non-null.
89 *
90 * <p>Flutter will typically call this method in two ways. When invoked as part of a dart
91 * `loadLibrary()` call, a valid loadingUnitId is passed in while the componentName is null. In
92 * this case, this method is responsible for figuring out what component the loadingUnitId
93 * corresponds to.
94 *
95 * <p>When invoked manually as part of loading an assets-only component, loadingUnitId is -1
96 * (invalid) and componentName is supplied. Without a loadingUnitId, this method just downloads
97 * the component by name and attempts to load assets via loadAssets while loadDartLibrary is
98 * skipped, even if the deferred component includes valid dart libs. To load dart libs, call
99 * `loadLibrary()` using the first way described in the previous paragraph as the method channel
100 * invocation will not load dart shared libraries.
101 *
102 * <p>While the Future retuned by either `loadLibary` or the method channel invocation will
103 * indicate when the code and assets are ready to be used, informational querying of the install
104 * process' state can be done with {@link getDeferredComponentInstallState}, though the results of
105 * this query should not be used to decide if the deferred component is ready to use. Only the
106 * Future completion should be used to do this.
107 *
108 * @param loadingUnitId The unique identifier associated with a Dart deferred library. This id is
109 * assigned by the compiler and can be seen for reference in bundle_config.yaml. This ID is
110 * primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded.
111 * Loading unit ids range from 0 to the number existing loading units. Passing a negative
112 * loading unit id indicates that no Dart deferred library should be loaded after download
113 * completes. This is the case when the deferred component is an assets-only component. If a
114 * negative loadingUnitId is passed, then componentName must not be null. Passing a
115 * loadingUnitId larger than the highest valid loading unit's id will cause the Dart
116 * loadLibrary() to complete with a failure.
117 * @param componentName The deferred component name as defined in bundle_config.yaml. This may be
118 * null if the deferred component to be loaded is associated with a loading unit/deferred dart
119 * library. In this case, it is this method's responsibility to map the loadingUnitId to its
120 * corresponding componentName. When loading asset-only or other deferred components without
121 * an associated Dart deferred library, loading unit id should a negative value and
122 * componentName must be non-null.
123 */
124 public abstract void installDeferredComponent(int loadingUnitId, String componentName);
125
126 /**
127 * Gets the current state of the installation session corresponding to the specified loadingUnitId
128 * and/or componentName.
129 *
130 * <p>Invocations of {@link installDeferredComponent} typically result in asynchronous downloading
131 * and other tasks. This method enables querying of the state of the installation. Querying the
132 * installation state is purely informational and does not impact the installation process. The
133 * results of this query should not be used to decide if the deferred component is ready to use.
134 * Upon completion of installation, the Future returned by the installation request will complete.
135 * Only after dart Future completion is it safe to use code and assets from the deferred
136 * component.
137 *
138 * <p>If no deferred component has been installed or requested to be installed by the provided
139 * loadingUnitId or componentName, then this method will return null.
140 *
141 * <p>Depending on the implementation, the returned String may vary. The Play store default
142 * implementation begins in the "requested" state before transitioning to the "downloading" and
143 * "installed" states.
144 *
145 * <p>Only successfully requested components have state. Modules that are invalid or have not been
146 * requested with {@link installDeferredComponent} will not have a state. Due to the asynchronous
147 * nature of the download process, components may not immediately have a valid state upon return
148 * of {@link installDeferredComponent}, though valid components will eventually obtain a state.
149 *
150 * <p>Both parameters are not always necessary to identify which component to install. Asset-only
151 * components do not have an associated loadingUnitId. Instead, an invalid ID like -1 may be
152 * passed to query only with componentName. On the other hand, it can be possible to resolve the
153 * componentName based on the loadingUnitId. This resolution is done if componentName is null. At
154 * least one of loadingUnitId or componentName must be valid or non-null.
155 *
156 * @param loadingUnitId The unique identifier associated with a Dart deferred library.
157 * @param componentName The deferred component name as defined in bundle_config.yaml.
158 */
159 public abstract String getDeferredComponentInstallState(int loadingUnitId, String componentName);
160
161 /**
162 * Extract and load any assets and resources from the deferred component for use by Flutter.
163 *
164 * <p>This method should provide a refreshed AssetManager to FlutterJNI.updateAssetManager that
165 * can access the new assets. If no assets are included as part of the deferred component, then
166 * nothing needs to be done.
167 *
168 * <p>If using the Play Store deferred component delivery, refresh the context via: {@code
169 * context.createPackageContext(context.getPackageName(), 0);} This returns a new context, from
170 * which an updated asset manager may be obtained and passed to updateAssetManager in FlutterJNI.
171 * This process does not require loadingUnitId or componentName, however, the two parameters are
172 * still present for custom implementations that store assets outside of Android's native system.
173 *
174 * <p>Assets shoud be loaded before the Dart deferred library is loaded, as successful loading of
175 * the Dart loading unit indicates the deferred component is fully loaded. Implementations of
176 * installDeferredComponent should invoke this after successful download.
177 *
178 * @param loadingUnitId The unique identifier associated with a Dart deferred library.
179 * @param componentName The deferred component name as defined in bundle_config.yaml.
180 */
181 public abstract void loadAssets(int loadingUnitId, String componentName);
182
183 /**
184 * Load the .so shared library file into the Dart VM.
185 *
186 * <p>When the download of a deferred component completes, this method should be called to find
187 * the path .so library file. The path(s) should then be passed to
188 * FlutterJNI.loadDartDeferredLibrary to be dlopen-ed and loaded into the Dart VM.
189 *
190 * <p>Specifically, APKs distributed by Android's app bundle format may vary by device and API
191 * number, so FlutterJNI's loadDartDeferredLibrary accepts a list of search paths with can include
192 * paths within APKs that have not been unpacked using the
193 * `path/to/apk.apk!path/inside/apk/lib.so` format. Each search path will be attempted in order
194 * until a shared library is found. This allows for the developer to avoid unpacking the apk zip.
195 *
196 * <p>Upon successful load of the Dart library, the Dart future from the originating loadLibary()
197 * call completes and developers are able to use symbols and assets from the deferred component.
198 *
199 * @param loadingUnitId The unique identifier associated with a Dart deferred library. This id is
200 * assigned by the compiler and can be seen for reference in bundle_config.yaml. This ID is
201 * primarily used in loadDartLibrary to indicate to Dart which Dart library is being loaded.
202 * Loading unit ids range from 0 to the number existing loading units. Negative loading unit
203 * ids are considered invalid and this method will result in a no-op.
204 * @param componentName The deferred component name as defined in bundle_config.yaml. If using
205 * Play Store deferred component delivery, this name corresponds to the root name on the
206 * installed APKs in which to search for the desired shared library .so file.
207 */
208 public abstract void loadDartLibrary(int loadingUnitId, String componentName);
209
210 /**
211 * Request that the specified component be uninstalled.
212 *
213 * <p>Since uninstallation requires significant disk i/o, this method only signals the intent to
214 * uninstall. Actual uninstallation (eg, removal of assets and files) may occur at a later time.
215 * However, once uninstallation is requested, the deferred component should not be used anymore
216 * until {@link installDeferredComponent} is called again.
217 *
218 * <p>Uninstallation, once complete, removes downloaded files and will require redownloading to
219 * install again.
220 *
221 * <p>Both parameters are not always necessary to identify which component to uninstall.
222 * Asset-only components do not have an associated loadingUnitId. Instead, an invalid ID like -1
223 * may be passed to download only with componentName. On the other hand, it can be possible to
224 * resolve the componentName based on the loadingUnitId. This resolution is done if componentName
225 * is null. At least one of loadingUnitId or componentName must be valid or non-null.
226 *
227 * @return false if no deferred component was found matching the input, true if an uninstall was
228 * successfully requested.
229 * @param loadingUnitId The unique identifier associated with a Dart deferred library.
230 * @param componentName The deferred component name as defined in bundle_config.yaml.
231 */
232 public abstract boolean uninstallDeferredComponent(int loadingUnitId, String componentName);
233
234 /**
235 * Cleans up and releases resources. This object is no longer usable after calling this method.
236 */
237 public abstract void destroy();
238}
abstract void installDeferredComponent(int loadingUnitId, String componentName)
abstract void setDeferredComponentChannel(DeferredComponentChannel channel)
abstract void loadDartLibrary(int loadingUnitId, String componentName)
abstract String getDeferredComponentInstallState(int loadingUnitId, String componentName)
abstract void loadAssets(int loadingUnitId, String componentName)
abstract boolean uninstallDeferredComponent(int loadingUnitId, String componentName)