Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_34031325
engine
提交
fde7c8c3
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,发现更多精彩内容 >>
未验证
提交
fde7c8c3
编写于
9月 03, 2019
作者:
M
Matt Carroll
提交者:
GitHub
9月 03, 2019
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Rename first frame method and notify FlutterActivity when full drawn (#38714 #36796). (#11357)
上级
ff4cf8ee
变更
22
隐藏空白更改
内联
并排
Showing
22 changed file
with
638 addition
and
389 deletion
+638
-389
ci/licenses_golden/licenses_flutter
ci/licenses_golden/licenses_flutter
+2
-1
shell/platform/android/BUILD.gn
shell/platform/android/BUILD.gn
+5
-1
shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
...android/io/flutter/embedding/android/FlutterActivity.java
+12
-1
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
...embedding/android/FlutterActivityAndFragmentDelegate.java
+20
-11
shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
...android/io/flutter/embedding/android/FlutterFragment.java
+27
-23
shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
...droid/io/flutter/embedding/android/FlutterSplashView.java
+10
-5
shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
...roid/io/flutter/embedding/android/FlutterSurfaceView.java
+37
-42
shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java
...roid/io/flutter/embedding/android/FlutterTextureView.java
+11
-38
shell/platform/android/io/flutter/embedding/android/FlutterView.java
...orm/android/io/flutter/embedding/android/FlutterView.java
+35
-24
shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
...rm/android/io/flutter/embedding/engine/FlutterEngine.java
+6
-5
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
...tform/android/io/flutter/embedding/engine/FlutterJNI.java
+32
-45
shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
...io/flutter/embedding/engine/renderer/FlutterRenderer.java
+91
-118
shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java
...r/embedding/engine/renderer/FlutterUiDisplayListener.java
+24
-0
shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
...d/io/flutter/embedding/engine/renderer/RenderSurface.java
+55
-0
shell/platform/android/io/flutter/view/FlutterNativeView.java
...l/platform/android/io/flutter/view/FlutterNativeView.java
+18
-51
shell/platform/android/io/flutter/view/FlutterView.java
shell/platform/android/io/flutter/view/FlutterView.java
+0
-23
shell/platform/android/io/flutter/view/TextureRegistry.java
shell/platform/android/io/flutter/view/TextureRegistry.java
+1
-0
shell/platform/android/platform_view_android_jni.cc
shell/platform/android/platform_view_android_jni.cc
+1
-1
shell/platform/android/test/io/flutter/FlutterTestSuite.java
shell/platform/android/test/io/flutter/FlutterTestSuite.java
+6
-0
shell/platform/android/test/io/flutter/embedding/engine/FlutterJNITest.java
...roid/test/io/flutter/embedding/engine/FlutterJNITest.java
+48
-0
shell/platform/android/test/io/flutter/embedding/engine/RenderingComponentTest.java
...t/io/flutter/embedding/engine/RenderingComponentTest.java
+93
-0
shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java
...lutter/embedding/engine/renderer/FlutterRendererTest.java
+104
-0
未找到文件。
ci/licenses_golden/licenses_flutter
浏览文件 @
fde7c8c3
...
...
@@ -603,7 +603,8 @@ FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugin
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/KeyEventChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/LifecycleChannel.java
...
...
shell/platform/android/BUILD.gn
浏览文件 @
fde7c8c3
...
...
@@ -170,7 +170,8 @@ action("flutter_shell_java") {
"io/flutter/embedding/engine/plugins/shim/ShimPluginRegistry.java",
"io/flutter/embedding/engine/plugins/shim/ShimRegistrar.java",
"io/flutter/embedding/engine/renderer/FlutterRenderer.java",
"io/flutter/embedding/engine/renderer/OnFirstFrameRenderedListener.java",
"io/flutter/embedding/engine/renderer/FlutterUiDisplayListener.java",
"io/flutter/embedding/engine/renderer/RenderSurface.java",
"io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java",
"io/flutter/embedding/engine/systemchannels/KeyEventChannel.java",
"io/flutter/embedding/engine/systemchannels/LifecycleChannel.java",
...
...
@@ -412,6 +413,9 @@ action("robolectric_tests") {
"test/io/flutter/embedding/android/FlutterActivityTest.java",
"test/io/flutter/embedding/android/FlutterFragmentTest.java",
"test/io/flutter/embedding/engine/FlutterEngineCacheTest.java",
"test/io/flutter/embedding/engine/FlutterJNITest.java",
"test/io/flutter/embedding/engine/RenderingComponentTest.java",
"test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java",
"test/io/flutter/embedding/engine/systemchannels/PlatformChannelTest.java",
"test/io/flutter/util/PreconditionsTest.java",
]
...
...
shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
浏览文件 @
fde7c8c3
...
...
@@ -886,7 +886,18 @@ public class FlutterActivity extends Activity
}
@Override
public
void
onFirstFrameRendered
()
{}
public
void
onFlutterUiDisplayed
()
{
// Notifies Android that we're fully drawn so that performance metrics can be collected by
// Flutter performance tests.
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
KITKAT
)
{
reportFullyDrawn
();
}
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
// no-op
}
/**
* The mode of the background of a {@code FlutterActivity}, either opaque or transparent.
...
...
shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
浏览文件 @
fde7c8c3
...
...
@@ -25,7 +25,7 @@ import io.flutter.embedding.engine.FlutterEngine;
import
io.flutter.embedding.engine.FlutterEngineCache
;
import
io.flutter.embedding.engine.FlutterShellArgs
;
import
io.flutter.embedding.engine.dart.DartExecutor
;
import
io.flutter.embedding.engine.renderer.
OnFirstFrameRendered
Listener
;
import
io.flutter.embedding.engine.renderer.
FlutterUiDisplay
Listener
;
import
io.flutter.plugin.platform.PlatformPlugin
;
import
io.flutter.view.FlutterMain
;
...
...
@@ -83,10 +83,15 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
private
boolean
isFlutterEngineFromHost
;
@NonNull
private
final
OnFirstFrameRenderedListener
onFirstFrameRenderedListener
=
new
OnFirstFrameRendered
Listener
()
{
private
final
FlutterUiDisplayListener
flutterUiDisplayListener
=
new
FlutterUiDisplay
Listener
()
{
@Override
public
void
onFirstFrameRendered
()
{
host
.
onFirstFrameRendered
();
public
void
onFlutterUiDisplayed
()
{
host
.
onFlutterUiDisplayed
();
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
host
.
onFlutterUiNoLongerDisplayed
();
}
};
...
...
@@ -228,7 +233,7 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
* <p>
* {@code inflater} and {@code container} may be null when invoked from an {@code Activity}.
* <p>
* This method creates a new {@link FlutterView}, adds a {@link
OnFirstFrameRendered
Listener} to
* This method creates a new {@link FlutterView}, adds a {@link
FlutterUiDisplay
Listener} to
* it, and then returns it.
*/
@NonNull
...
...
@@ -236,7 +241,7 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
Log
.
v
(
TAG
,
"Creating FlutterView."
);
ensureAlive
();
flutterView
=
new
FlutterView
(
host
.
getActivity
(),
host
.
getRenderMode
(),
host
.
getTransparencyMode
());
flutterView
.
addOnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
flutterView
.
addOnFirstFrameRenderedListener
(
flutterUiDisplay
Listener
);
flutterSplashView
=
new
FlutterSplashView
(
host
.
getContext
());
if
(
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
JELLY_BEAN_MR1
)
{
...
...
@@ -391,12 +396,12 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
/**
* Invoke this from {@code Activity#onDestroy()} or {@code Fragment#onDestroyView()}.
* <p>
* This method removes this delegate's {@link FlutterView}'s {@link
OnFirstFrameRendered
Listener}.
* This method removes this delegate's {@link FlutterView}'s {@link
FlutterUiDisplay
Listener}.
*/
void
onDestroyView
()
{
Log
.
v
(
TAG
,
"onDestroyView()"
);
ensureAlive
();
flutterView
.
removeOnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
flutterView
.
removeOnFirstFrameRenderedListener
(
flutterUiDisplay
Listener
);
}
/**
...
...
@@ -695,9 +700,13 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
boolean
shouldAttachEngineToActivity
();
/**
* Invoked by this delegate when its {@link FlutterView} has rendered its first Flutter
* frame.
* Invoked by this delegate when its {@link FlutterView} starts painting pixels.
*/
void
onFlutterUiDisplayed
();
/**
* Invoked by this delegate when its {@link FlutterView} stops painting pixels.
*/
void
onF
irstFrameRender
ed
();
void
onF
lutterUiNoLongerDisplay
ed
();
}
}
shell/platform/android/io/flutter/embedding/android/FlutterFragment.java
浏览文件 @
fde7c8c3
...
...
@@ -21,7 +21,7 @@ import android.view.ViewGroup;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.FlutterShellArgs
;
import
io.flutter.embedding.engine.renderer.
OnFirstFrameRendered
Listener
;
import
io.flutter.embedding.engine.renderer.
FlutterUiDisplay
Listener
;
import
io.flutter.plugin.platform.PlatformPlugin
;
import
io.flutter.view.FlutterMain
;
...
...
@@ -567,21 +567,6 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm
// implementation for details about why it exists.
private
FlutterActivityAndFragmentDelegate
delegate
;
private
final
OnFirstFrameRenderedListener
onFirstFrameRenderedListener
=
new
OnFirstFrameRenderedListener
()
{
@Override
public
void
onFirstFrameRendered
()
{
// Notify our subclasses that the first frame has been rendered.
FlutterFragment
.
this
.
onFirstFrameRendered
();
// Notify our owning Activity that the first frame has been rendered.
FragmentActivity
fragmentActivity
=
getActivity
();
if
(
fragmentActivity
instanceof
OnFirstFrameRenderedListener
)
{
OnFirstFrameRenderedListener
activityAsListener
=
(
OnFirstFrameRenderedListener
)
fragmentActivity
;
activityAsListener
.
onFirstFrameRendered
();
}
}
};
public
FlutterFragment
()
{
// Ensure that we at least have an empty Bundle of arguments so that we don't
// need to continually check for null arguments before grabbing one.
...
...
@@ -951,21 +936,40 @@ public class FlutterFragment extends Fragment implements FlutterActivityAndFragm
}
/**
* Invoked after the {@link FlutterView} within this {@code FlutterFragment} renders its first
* frame.
* Invoked after the {@link FlutterView} within this {@code FlutterFragment} starts rendering
* pixels to the screen.
* <p>
* This method forwards {@code onFlutterUiDisplayed()} to its attached {@code Activity}, if
* the attached {@code Activity} implements {@link FlutterUiDisplayListener}.
* <p>
* Subclasses that override this method must call through to the {@code super} method.
* <p>
* Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
*/
@Override
public
void
onFlutterUiDisplayed
()
{
FragmentActivity
attachedActivity
=
getActivity
();
if
(
attachedActivity
instanceof
FlutterUiDisplayListener
)
{
((
FlutterUiDisplayListener
)
attachedActivity
).
onFlutterUiDisplayed
();
}
}
/**
* Invoked after the {@link FlutterView} within this {@code FlutterFragment} stops rendering
* pixels to the screen.
* <p>
* This method forwards {@code onF
irstFrameRendered()} to its attached {@code Activity}, if
*
the attached {@code Activity} implements {@link OnFirstFrameRendered
Listener}.
* This method forwards {@code onF
lutterUiNoLongerDisplayed()} to its attached {@code Activity},
*
if the attached {@code Activity} implements {@link FlutterUiDisplay
Listener}.
* <p>
* Subclasses that override this method must call through to the {@code super} method.
* <p>
* Used by this {@code FlutterFragment}'s {@link FlutterActivityAndFragmentDelegate.Host}
*/
@Override
public
void
onF
irstFrameRender
ed
()
{
public
void
onF
lutterUiNoLongerDisplay
ed
()
{
FragmentActivity
attachedActivity
=
getActivity
();
if
(
attachedActivity
instanceof
OnFirstFrameRendered
Listener
)
{
((
OnFirstFrameRenderedListener
)
attachedActivity
).
onFirstFrameRender
ed
();
if
(
attachedActivity
instanceof
FlutterUiDisplay
Listener
)
{
((
FlutterUiDisplayListener
)
attachedActivity
).
onFlutterUiNoLongerDisplay
ed
();
}
}
...
...
shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
浏览文件 @
fde7c8c3
...
...
@@ -16,7 +16,7 @@ import android.widget.FrameLayout;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.renderer.
OnFirstFrameRendered
Listener
;
import
io.flutter.embedding.engine.renderer.
FlutterUiDisplay
Listener
;
/**
* {@code View} that displays a {@link SplashScreen} until a given {@link FlutterView}
...
...
@@ -51,13 +51,18 @@ import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
};
@NonNull
private
final
OnFirstFrameRenderedListener
onFirstFrameRenderedListener
=
new
OnFirstFrameRendered
Listener
()
{
private
final
FlutterUiDisplayListener
flutterUiDisplayListener
=
new
FlutterUiDisplay
Listener
()
{
@Override
public
void
onF
irstFrameRender
ed
()
{
public
void
onF
lutterUiDisplay
ed
()
{
if
(
splashScreen
!=
null
)
{
transitionToFlutter
();
}
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
// no-op
}
};
@NonNull
...
...
@@ -114,7 +119,7 @@ import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
)
{
// If we were displaying a previous FlutterView, remove it.
if
(
this
.
flutterView
!=
null
)
{
this
.
flutterView
.
removeOnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
this
.
flutterView
.
removeOnFirstFrameRenderedListener
(
flutterUiDisplay
Listener
);
removeView
(
this
.
flutterView
);
}
// If we were displaying a previous splash screen View, remove it.
...
...
@@ -136,7 +141,7 @@ import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
// waiting for the first frame to render. Show a splash UI until that happens.
splashScreenView
=
splashScreen
.
createSplashView
(
getContext
(),
splashScreenState
);
addView
(
this
.
splashScreenView
);
flutterView
.
addOnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
flutterView
.
addOnFirstFrameRenderedListener
(
flutterUiDisplay
Listener
);
}
else
if
(
isSplashScreenTransitionNeededNow
())
{
Log
.
v
(
TAG
,
"Showing an immediate splash transition to Flutter due to previously interrupted transition."
);
splashScreenView
=
splashScreen
.
createSplashView
(
getContext
(),
splashScreenState
);
...
...
shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
浏览文件 @
fde7c8c3
...
...
@@ -12,12 +12,10 @@ import android.util.AttributeSet;
import
android.view.SurfaceHolder
;
import
android.view.SurfaceView
;
import
java.util.HashSet
;
import
java.util.Set
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener
;
import
io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
;
import
io.flutter.embedding.engine.renderer.RenderSurface
;
/**
* Paints a Flutter UI on a {@link android.view.Surface}.
...
...
@@ -34,7 +32,7 @@ import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
* desired, consider using a {@link FlutterView} which provides all of these behaviors and
* utilizes a {@code FlutterSurfaceView} internally.
*/
public
class
FlutterSurfaceView
extends
SurfaceView
implements
FlutterRenderer
.
RenderSurface
{
public
class
FlutterSurfaceView
extends
SurfaceView
implements
RenderSurface
{
private
static
final
String
TAG
=
"FlutterSurfaceView"
;
private
final
boolean
renderTransparently
;
...
...
@@ -42,8 +40,6 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
private
boolean
isAttachedToFlutterRenderer
=
false
;
@Nullable
private
FlutterRenderer
flutterRenderer
;
@NonNull
private
Set
<
OnFirstFrameRenderedListener
>
onFirstFrameRenderedListeners
=
new
HashSet
<>();
// Connects the {@code Surface} beneath this {@code SurfaceView} with Flutter's native code.
// Callbacks are received by this Object and then those messages are forwarded to our
...
...
@@ -51,7 +47,7 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
private
final
SurfaceHolder
.
Callback
surfaceCallback
=
new
SurfaceHolder
.
Callback
()
{
@Override
public
void
surfaceCreated
(
@NonNull
SurfaceHolder
holder
)
{
Log
.
v
(
TAG
,
"SurfaceHolder.Callback.s
urfaceCreated
()"
);
Log
.
v
(
TAG
,
"SurfaceHolder.Callback.s
tartRenderingToSurface
()"
);
isSurfaceAvailableForRendering
=
true
;
if
(
isAttachedToFlutterRenderer
)
{
...
...
@@ -69,7 +65,7 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
@Override
public
void
surfaceDestroyed
(
@NonNull
SurfaceHolder
holder
)
{
Log
.
v
(
TAG
,
"SurfaceHolder.Callback.s
urfaceDestroyed
()"
);
Log
.
v
(
TAG
,
"SurfaceHolder.Callback.s
topRenderingToSurface
()"
);
isSurfaceAvailableForRendering
=
false
;
if
(
isAttachedToFlutterRenderer
)
{
...
...
@@ -78,6 +74,24 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
}
};
private
final
FlutterUiDisplayListener
flutterUiDisplayListener
=
new
FlutterUiDisplayListener
()
{
@Override
public
void
onFlutterUiDisplayed
()
{
Log
.
v
(
TAG
,
"onFlutterUiDisplayed()"
);
// Now that a frame is ready to display, take this SurfaceView from transparent to opaque.
setAlpha
(
1.0f
);
if
(
flutterRenderer
!=
null
)
{
flutterRenderer
.
removeIsDisplayingFlutterUiListener
(
this
);
}
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
// no-op
}
};
/**
* Constructs a {@code FlutterSurfaceView} programmatically, without any XML attributes.
*/
...
...
@@ -123,6 +137,12 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
setAlpha
(
0.0f
);
}
@Nullable
@Override
public
FlutterRenderer
getAttachedRenderer
()
{
return
flutterRenderer
;
}
/**
* Invoked by the owner of this {@code FlutterSurfaceView} when it wants to begin rendering
* a Flutter UI to this {@code FlutterSurfaceView}.
...
...
@@ -140,12 +160,15 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
Log
.
v
(
TAG
,
"Attaching to FlutterRenderer."
);
if
(
this
.
flutterRenderer
!=
null
)
{
Log
.
v
(
TAG
,
"Already connected to a FlutterRenderer. Detaching from old one and attaching to new one."
);
this
.
flutterRenderer
.
detachFromRenderSurface
();
this
.
flutterRenderer
.
stopRenderingToSurface
();
this
.
flutterRenderer
.
removeIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
}
this
.
flutterRenderer
=
flutterRenderer
;
isAttachedToFlutterRenderer
=
true
;
this
.
flutterRenderer
.
addIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
// If we're already attached to an Android window then we're now attached to both a renderer
// and the Android window. We can begin rendering now.
if
(
isSurfaceAvailableForRendering
)
{
...
...
@@ -173,6 +196,8 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
// Make the SurfaceView invisible to avoid showing a black rectangle.
setAlpha
(
0.0f
);
this
.
flutterRenderer
.
removeIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
flutterRenderer
=
null
;
isAttachedToFlutterRenderer
=
false
;
}
else
{
...
...
@@ -186,7 +211,7 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
throw
new
IllegalStateException
(
"connectSurfaceToRenderer() should only be called when flutterRenderer and getHolder() are non-null."
);
}
flutterRenderer
.
s
urfaceCreated
(
getHolder
().
getSurface
());
flutterRenderer
.
s
tartRenderingToSurface
(
getHolder
().
getSurface
());
}
// FlutterRenderer must be non-null.
...
...
@@ -205,36 +230,6 @@ public class FlutterSurfaceView extends SurfaceView implements FlutterRenderer.R
throw
new
IllegalStateException
(
"disconnectSurfaceFromRenderer() should only be called when flutterRenderer is non-null."
);
}
flutterRenderer
.
surfaceDestroyed
();
}
/**
* Adds the given {@code listener} to this {@code FlutterSurfaceView}, to be notified upon Flutter's
* first rendered frame.
*/
@Override
public
void
addOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{
onFirstFrameRenderedListeners
.
add
(
listener
);
}
/**
* Removes the given {@code listener}, which was previously added with
* {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}.
*/
@Override
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{
onFirstFrameRenderedListeners
.
remove
(
listener
);
}
@Override
public
void
onFirstFrameRendered
()
{
// TODO(mattcarroll): decide where this method should live and what it needs to do.
Log
.
v
(
TAG
,
"onFirstFrameRendered()"
);
// Now that a frame is ready to display, take this SurfaceView from transparent to opaque.
setAlpha
(
1.0f
);
for
(
OnFirstFrameRenderedListener
listener
:
onFirstFrameRenderedListeners
)
{
listener
.
onFirstFrameRendered
();
}
flutterRenderer
.
stopRenderingToSurface
();
}
}
shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java
浏览文件 @
fde7c8c3
...
...
@@ -12,12 +12,9 @@ import android.util.AttributeSet;
import
android.view.Surface
;
import
android.view.TextureView
;
import
java.util.HashSet
;
import
java.util.Set
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.
OnFirstFrameRenderedListener
;
import
io.flutter.embedding.engine.renderer.
RenderSurface
;
/**
* Paints a Flutter UI on a {@link SurfaceTexture}.
...
...
@@ -34,15 +31,13 @@ import io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener;
* desired, consider using a {@link FlutterView} which provides all of these behaviors and
* utilizes a {@code FlutterTextureView} internally.
*/
public
class
FlutterTextureView
extends
TextureView
implements
FlutterRenderer
.
RenderSurface
{
public
class
FlutterTextureView
extends
TextureView
implements
RenderSurface
{
private
static
final
String
TAG
=
"FlutterTextureView"
;
private
boolean
isSurfaceAvailableForRendering
=
false
;
private
boolean
isAttachedToFlutterRenderer
=
false
;
@Nullable
private
FlutterRenderer
flutterRenderer
;
@NonNull
private
Set
<
OnFirstFrameRenderedListener
>
onFirstFrameRenderedListeners
=
new
HashSet
<>();
// Connects the {@code SurfaceTexture} beneath this {@code TextureView} with Flutter's native code.
// Callbacks are received by this Object and then those messages are forwarded to our
...
...
@@ -111,6 +106,12 @@ public class FlutterTextureView extends TextureView implements FlutterRenderer.R
setSurfaceTextureListener
(
surfaceTextureListener
);
}
@Nullable
@Override
public
FlutterRenderer
getAttachedRenderer
()
{
return
flutterRenderer
;
}
/**
* Invoked by the owner of this {@code FlutterTextureView} when it wants to begin rendering
* a Flutter UI to this {@code FlutterTextureView}.
...
...
@@ -128,7 +129,7 @@ public class FlutterTextureView extends TextureView implements FlutterRenderer.R
Log
.
v
(
TAG
,
"Attaching to FlutterRenderer."
);
if
(
this
.
flutterRenderer
!=
null
)
{
Log
.
v
(
TAG
,
"Already connected to a FlutterRenderer. Detaching from old one and attaching to new one."
);
this
.
flutterRenderer
.
detachFromRender
Surface
();
this
.
flutterRenderer
.
stopRenderingTo
Surface
();
}
this
.
flutterRenderer
=
flutterRenderer
;
...
...
@@ -171,7 +172,7 @@ public class FlutterTextureView extends TextureView implements FlutterRenderer.R
throw
new
IllegalStateException
(
"connectSurfaceToRenderer() should only be called when flutterRenderer and getSurfaceTexture() are non-null."
);
}
flutterRenderer
.
s
urfaceCreated
(
new
Surface
(
getSurfaceTexture
()));
flutterRenderer
.
s
tartRenderingToSurface
(
new
Surface
(
getSurfaceTexture
()));
}
// FlutterRenderer must be non-null.
...
...
@@ -190,34 +191,6 @@ public class FlutterTextureView extends TextureView implements FlutterRenderer.R
throw
new
IllegalStateException
(
"disconnectSurfaceFromRenderer() should only be called when flutterRenderer is non-null."
);
}
flutterRenderer
.
surfaceDestroyed
();
}
/**
* Adds the given {@code listener} to this {@code FlutterTextureView}, to be notified upon Flutter's
* first rendered frame.
*/
@Override
public
void
addOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{
onFirstFrameRenderedListeners
.
add
(
listener
);
}
/**
* Removes the given {@code listener}, which was previously added with
* {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}.
*/
@Override
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{
onFirstFrameRenderedListeners
.
remove
(
listener
);
}
@Override
public
void
onFirstFrameRendered
()
{
// TODO(mattcarroll): decide where this method should live and what it needs to do.
Log
.
v
(
TAG
,
"onFirstFrameRendered()"
);
for
(
OnFirstFrameRenderedListener
listener
:
onFirstFrameRenderedListeners
)
{
listener
.
onFirstFrameRendered
();
}
flutterRenderer
.
stopRenderingToSurface
();
}
}
shell/platform/android/io/flutter/embedding/android/FlutterView.java
浏览文件 @
fde7c8c3
...
...
@@ -37,7 +37,8 @@ import java.util.Set;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterEngine
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener
;
import
io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
;
import
io.flutter.embedding.engine.renderer.RenderSurface
;
import
io.flutter.plugin.editing.TextInputPlugin
;
import
io.flutter.plugin.platform.PlatformViewsController
;
import
io.flutter.view.AccessibilityBridge
;
...
...
@@ -74,9 +75,9 @@ public class FlutterView extends FrameLayout {
// Internal view hierarchy references.
@Nullable
private
FlutterRenderer
.
RenderSurface
renderSurface
;
private
final
Set
<
OnFirstFrameRenderedListener
>
onFirstFrameRendered
Listeners
=
new
HashSet
<>();
private
boolean
didRenderFirstFrame
;
private
RenderSurface
renderSurface
;
private
final
Set
<
FlutterUiDisplayListener
>
flutterUiDisplay
Listeners
=
new
HashSet
<>();
private
boolean
isFlutterUiDisplayed
;
// Connections to a Flutter execution context.
@Nullable
...
...
@@ -108,13 +109,22 @@ public class FlutterView extends FrameLayout {
}
};
private
final
OnFirstFrameRenderedListener
onFirstFrameRenderedListener
=
new
OnFirstFrameRendered
Listener
()
{
private
final
FlutterUiDisplayListener
flutterUiDisplayListener
=
new
FlutterUiDisplay
Listener
()
{
@Override
public
void
onF
irstFrameRender
ed
()
{
didRenderFirstFrame
=
true
;
public
void
onF
lutterUiDisplay
ed
()
{
isFlutterUiDisplayed
=
true
;
for
(
OnFirstFrameRenderedListener
listener
:
onFirstFrameRenderedListeners
)
{
listener
.
onFirstFrameRendered
();
for
(
FlutterUiDisplayListener
listener
:
flutterUiDisplayListeners
)
{
listener
.
onFlutterUiDisplayed
();
}
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
isFlutterUiDisplayed
=
false
;
for
(
FlutterUiDisplayListener
listener
:
flutterUiDisplayListeners
)
{
listener
.
onFlutterUiNoLongerDisplayed
();
}
}
};
...
...
@@ -228,23 +238,23 @@ public class FlutterView extends FrameLayout {
* </ol>
*/
public
boolean
hasRenderedFirstFrame
()
{
return
didRenderFirstFrame
;
return
isFlutterUiDisplayed
;
}
/**
* Adds the given {@code listener} to this {@code FlutterView}, to be notified upon Flutter's
* first rendered frame.
*/
public
void
addOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRendered
Listener
listener
)
{
onFirstFrameRendered
Listeners
.
add
(
listener
);
public
void
addOnFirstFrameRenderedListener
(
@NonNull
FlutterUiDisplay
Listener
listener
)
{
flutterUiDisplay
Listeners
.
add
(
listener
);
}
/**
* Removes the given {@code listener}, which was previously added with
* {@link #addOnFirstFrameRenderedListener(
OnFirstFrameRendered
Listener)}.
* {@link #addOnFirstFrameRenderedListener(
FlutterUiDisplay
Listener)}.
*/
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRendered
Listener
listener
)
{
onFirstFrameRendered
Listeners
.
remove
(
listener
);
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
FlutterUiDisplay
Listener
listener
)
{
flutterUiDisplay
Listeners
.
remove
(
listener
);
}
//------- Start: Process View configuration that Flutter cares about. ------
...
...
@@ -580,9 +590,9 @@ public class FlutterView extends FrameLayout {
// Instruct our FlutterRenderer that we are now its designated RenderSurface.
FlutterRenderer
flutterRenderer
=
this
.
flutterEngine
.
getRenderer
();
didRenderFirstFrame
=
flutterRenderer
.
hasRenderedFirstFrame
();
flutterRenderer
.
attachToRenderSurface
(
renderSurface
);
flutterRenderer
.
add
OnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
isFlutterUiDisplayed
=
flutterRenderer
.
isDisplayingFlutterUi
();
renderSurface
.
attachToRenderer
(
flutterRenderer
);
flutterRenderer
.
add
IsDisplayingFlutterUiListener
(
flutterUiDisplay
Listener
);
// Initialize various components that know how to process Android View I/O
// in a way that Flutter understands.
...
...
@@ -631,8 +641,8 @@ public class FlutterView extends FrameLayout {
// If the first frame has already been rendered, notify all first frame listeners.
// Do this after all other initialization so that listeners don't inadvertently interact
// with a FlutterView that is only partially attached to a FlutterEngine.
if
(
didRenderFirstFrame
)
{
onFirstFrameRenderedListener
.
onFirstFrameRender
ed
();
if
(
isFlutterUiDisplayed
)
{
flutterUiDisplayListener
.
onFlutterUiDisplay
ed
();
}
}
...
...
@@ -674,9 +684,9 @@ public class FlutterView extends FrameLayout {
// Instruct our FlutterRenderer that we are no longer interested in being its RenderSurface.
FlutterRenderer
flutterRenderer
=
flutterEngine
.
getRenderer
();
didRenderFirstFrame
=
false
;
flutterRenderer
.
remove
OnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
flutterRenderer
.
detachFromRender
Surface
();
isFlutterUiDisplayed
=
false
;
flutterRenderer
.
remove
IsDisplayingFlutterUiListener
(
flutterUiDisplay
Listener
);
flutterRenderer
.
stopRenderingTo
Surface
();
flutterEngine
=
null
;
}
...
...
@@ -685,7 +695,8 @@ public class FlutterView extends FrameLayout {
*/
@VisibleForTesting
public
boolean
isAttachedToFlutterEngine
()
{
return
flutterEngine
!=
null
&&
flutterEngine
.
getRenderer
().
isAttachedTo
(
renderSurface
);
return
flutterEngine
!=
null
&&
flutterEngine
.
getRenderer
()
==
renderSurface
.
getAttachedRenderer
();
}
/**
...
...
shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
浏览文件 @
fde7c8c3
...
...
@@ -20,6 +20,7 @@ import io.flutter.embedding.engine.plugins.broadcastreceiver.BroadcastReceiverCo
import
io.flutter.embedding.engine.plugins.contentprovider.ContentProviderControlSurface
;
import
io.flutter.embedding.engine.plugins.service.ServiceControlSurface
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.RenderSurface
;
import
io.flutter.embedding.engine.systemchannels.AccessibilityChannel
;
import
io.flutter.embedding.engine.systemchannels.KeyEventChannel
;
import
io.flutter.embedding.engine.systemchannels.LifecycleChannel
;
...
...
@@ -51,8 +52,8 @@ import io.flutter.plugin.platform.PlatformViewsController;
* invoked twice on the same {@code FlutterEngine}.
*
* To start rendering Flutter content to the screen, use {@link #getRenderer()} to obtain a
* {@link FlutterRenderer} and then attach a {@link
FlutterRenderer.
RenderSurface}. Consider using
* a {@link io.flutter.embedding.android.FlutterView} as a {@link
FlutterRenderer.
RenderSurface}.
* {@link FlutterRenderer} and then attach a {@link RenderSurface}. Consider using
* a {@link io.flutter.embedding.android.FlutterView} as a {@link RenderSurface}.
*/
// TODO(mattcarroll): re-evaluate system channel APIs - some are not well named or differentiated
public
class
FlutterEngine
implements
LifecycleOwner
{
...
...
@@ -118,8 +119,8 @@ public class FlutterEngine implements LifecycleOwner {
* to begin executing Dart code within this {@code FlutterEngine}.
*
* A new {@code FlutterEngine} will not display any UI until a
* {@link
io.flutter.embedding.engine.renderer.FlutterRenderer.
RenderSurface} is registered. See
* {@link #getRenderer()} and {@link FlutterRenderer#
attachToRenderSurface(FlutterRenderer.
RenderSurface)}.
* {@link RenderSurface} is registered. See
* {@link #getRenderer()} and {@link FlutterRenderer#
startRenderingToSurface(
RenderSurface)}.
*
* A new {@code FlutterEngine} does not come with any Flutter plugins attached. To attach plugins,
* see {@link #getPlugins()}.
...
...
@@ -221,7 +222,7 @@ public class FlutterEngine implements LifecycleOwner {
* The rendering system associated with this {@code FlutterEngine}.
*
* To render a Flutter UI that is produced by this {@code FlutterEngine}'s Dart code, attach
* a {@link
io.flutter.embedding.engine.renderer.FlutterRenderer.
RenderSurface} to this
* a {@link RenderSurface} to this
* {@link FlutterRenderer}.
*/
@NonNull
...
...
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
浏览文件 @
fde7c8c3
...
...
@@ -12,18 +12,19 @@ import android.os.Looper;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.UiThread
;
import
android.support.annotation.VisibleForTesting
;
import
android.view.Surface
;
import
android.view.SurfaceHolder
;
import
java.nio.ByteBuffer
;
import
java.util.HashSet
;
import
java.util.Set
;
import
java.util.concurrent.CopyOnWriteArraySet
;
import
io.flutter.Log
;
import
io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener
;
import
io.flutter.embedding.engine.dart.PlatformMessageHandler
;
import
io.flutter.embedding.engine.renderer.Flutter
Render
er
;
import
io.flutter.embedding.engine.renderer.
OnFirstFrameRenderedListener
;
import
io.flutter.embedding.engine.renderer.Flutter
UiDisplayListen
er
;
import
io.flutter.embedding.engine.renderer.
RenderSurface
;
import
io.flutter.plugin.common.StandardMessageCodec
;
import
io.flutter.view.AccessibilityBridge
;
import
io.flutter.view.FlutterCallbackInformation
;
...
...
@@ -72,13 +73,13 @@ import io.flutter.view.FlutterCallbackInformation;
* }
*
* To provide a visual, interactive surface for Flutter rendering and touch events, register a
* {@link
FlutterRenderer.RenderSurface} with {@link #setRenderSurface(FlutterRenderer.
RenderSurface)}
* {@link
RenderSurface} with {@link #setRenderSurface(
RenderSurface)}
*
* To receive callbacks for certain events that occur on the native side, register listeners:
*
* <ol>
* <li>{@link #addEngineLifecycleListener(EngineLifecycleListener)}</li>
* <li>{@link #add
OnFirstFrameRenderedListener(OnFirstFrameRendered
Listener)}</li>
* <li>{@link #add
IsDisplayingFlutterUiListener(FlutterUiDisplay
Listener)}</li>
* </ol>
*
* To facilitate platform messages between Java and Dart running in Flutter, register a handler:
...
...
@@ -155,15 +156,13 @@ public class FlutterJNI {
@Nullable
private
Long
nativePlatformViewId
;
@Nullable
private
FlutterRenderer
.
RenderSurface
renderSurface
;
@Nullable
private
AccessibilityDelegate
accessibilityDelegate
;
@Nullable
private
PlatformMessageHandler
platformMessageHandler
;
@NonNull
private
final
Set
<
EngineLifecycleListener
>
engineLifecycleListeners
=
new
Hash
Set
<>();
private
final
Set
<
EngineLifecycleListener
>
engineLifecycleListeners
=
new
CopyOnWriteArray
Set
<>();
@NonNull
private
final
Set
<
OnFirstFrameRenderedListener
>
firstFrameListeners
=
new
Hash
Set
<>();
private
final
Set
<
FlutterUiDisplayListener
>
flutterUiDisplayListeners
=
new
CopyOnWriteArray
Set
<>();
@NonNull
private
final
Looper
mainLooper
;
// cached to avoid synchronization on repeat access.
...
...
@@ -233,58 +232,46 @@ public class FlutterJNI {
//----- Start Render Surface Support -----
/**
* Sets the {@link FlutterRenderer.RenderSurface} delegate for the attached Flutter context.
* <p>
* Flutter expects a user interface to exist on the platform side (Android), and that interface
* is expected to offer some capabilities that Flutter depends upon. The {@link FlutterRenderer.RenderSurface}
* interface represents those expectations.
* <p>
* If an app includes a user interface that renders a Flutter UI then a {@link FlutterRenderer.RenderSurface}
* should be set (this is the typical Flutter scenario). If no UI is being rendered, such as a
* Flutter app that is running Dart code in the background, then no registration may be necessary.
* <p>
* If no {@link FlutterRenderer.RenderSurface} is registered, then related messages coming from
* Flutter will be dropped (ignored).
* Adds a {@link FlutterUiDisplayListener}, which receives a callback when Flutter's
* engine notifies {@code FlutterJNI} that Flutter is painting pixels to the {@link Surface} that
* was provided to Flutter.
*/
@UiThread
public
void
setRenderSurface
(
@Nullable
FlutterRenderer
.
RenderSurface
renderSurface
)
{
public
void
addIsDisplayingFlutterUiListener
(
@NonNull
FlutterUiDisplayListener
listener
)
{
ensureRunningOnMainThread
();
this
.
renderSurface
=
renderSurface
;
flutterUiDisplayListeners
.
add
(
listener
)
;
}
/**
* Adds a {@link OnFirstFrameRenderedListener}, which receives a callback when Flutter's
* engine notifies {@code FlutterJNI} that the first frame of a Flutter UI has been rendered
* to the {@link Surface} that was provided to Flutter.
* Removes a {@link FlutterUiDisplayListener} that was added with
* {@link #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}.
*/
@UiThread
public
void
addOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRendered
Listener
listener
)
{
public
void
removeIsDisplayingFlutterUiListener
(
@NonNull
FlutterUiDisplay
Listener
listener
)
{
ensureRunningOnMainThread
();
f
irstFrameListeners
.
add
(
listener
);
f
lutterUiDisplayListeners
.
remove
(
listener
);
}
/**
* Removes a {@link OnFirstFrameRenderedListener} that was added with
* {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}.
*/
// Called by native to notify first Flutter frame rendered.
@SuppressWarnings
(
"unused"
)
@VisibleForTesting
@UiThread
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{
void
onFirstFrame
(
)
{
ensureRunningOnMainThread
();
firstFrameListeners
.
remove
(
listener
);
for
(
FlutterUiDisplayListener
listener
:
flutterUiDisplayListeners
)
{
listener
.
onFlutterUiDisplayed
();
}
}
//
Called by native to notify first Flutter frame rendered
.
@
SuppressWarnings
(
"unused"
)
//
TODO(mattcarroll): get native to call this when rendering stops
.
@
VisibleForTesting
@UiThread
private
void
onFirstFrame
()
{
void
onRenderingStopped
()
{
ensureRunningOnMainThread
();
if
(
renderSurface
!=
null
)
{
renderSurface
.
onFirstFrameRendered
();
}
// TODO(mattcarroll): log dropped messages when in debug mode (https://github.com/flutter/flutter/issues/25391)
for
(
OnFirstFrameRenderedListener
listener
:
firstFrame
Listeners
)
{
listener
.
onF
irstFrameRender
ed
();
for
(
FlutterUiDisplayListener
listener
:
flutterUiDisplay
Listeners
)
{
listener
.
onF
lutterUiNoLongerDisplay
ed
();
}
}
...
...
@@ -331,6 +318,7 @@ public class FlutterJNI {
public
void
onSurfaceDestroyed
()
{
ensureRunningOnMainThread
();
ensureAttachedToNative
();
onRenderingStopped
();
nativeSurfaceDestroyed
(
nativePlatformViewId
);
}
...
...
@@ -431,7 +419,6 @@ public class FlutterJNI {
* See {@link AccessibilityBridge} for an example of an {@link AccessibilityDelegate} and the
* surrounding responsibilities.
*/
// TODO(mattcarroll): move AccessibilityDelegate definition into FlutterJNI. FlutterJNI should be the basis of dependencies, not the other way round.
@UiThread
public
void
setAccessibilityDelegate
(
@Nullable
AccessibilityDelegate
accessibilityDelegate
)
{
ensureRunningOnMainThread
();
...
...
@@ -772,7 +759,7 @@ public class FlutterJNI {
/**
* Removes the given {@code engineLifecycleListener}, which was previously added using
* {@link #add
OnFirstFrameRenderedListener(OnFirstFrameRendered
Listener)}.
* {@link #add
IsDisplayingFlutterUiListener(FlutterUiDisplay
Listener)}.
*/
@UiThread
public
void
removeEngineLifecycleListener
(
@NonNull
EngineLifecycleListener
engineLifecycleListener
)
{
...
...
shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
浏览文件 @
fde7c8c3
...
...
@@ -17,95 +17,85 @@ import java.nio.ByteBuffer;
import
java.util.concurrent.atomic.AtomicLong
;
import
io.flutter.Log
;
import
io.flutter.embedding.android.FlutterView
;
import
io.flutter.embedding.engine.FlutterJNI
;
import
io.flutter.view.TextureRegistry
;
/**
* WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE.
* IF YOU USE IT, WE WILL BREAK YOU.
*
* {@code FlutterRenderer} works in tandem with a provided {@link RenderSurface} to create an
* interactive Flutter UI.
*
* {@code FlutterRenderer} manages textures for rendering, and forwards some Java calls to native Flutter
* code via JNI. The corresponding {@link RenderSurface} is used as a delegate to carry out
* certain actions on behalf of this {@code FlutterRenderer} within an Android view hierarchy.
*
* {@link FlutterView} is an implementation of a {@link RenderSurface}.
* Represents the rendering responsibilities of a {@code FlutterEngine}.
* <p>
* {@code FlutterRenderer} works in tandem with a provided {@link RenderSurface} to paint Flutter
* pixels to an Android {@code View} hierarchy.
* <p>
* {@code FlutterRenderer} manages textures for rendering, and forwards some Java calls to native
* Flutter code via JNI. The corresponding {@link RenderSurface} provides the Android
* {@link Surface} that this renderer paints.
* <p>
* {@link io.flutter.embedding.android.FlutterSurfaceView} and
* {@link io.flutter.embedding.android.FlutterTextureView} are implementations of
* {@link RenderSurface}.
*/
@TargetApi
(
Build
.
VERSION_CODES
.
JELLY_BEAN
)
public
class
FlutterRenderer
implements
TextureRegistry
{
private
static
final
String
TAG
=
"FlutterRenderer"
;
@NonNull
private
final
FlutterJNI
flutterJNI
;
@NonNull
private
final
AtomicLong
nextTextureId
=
new
AtomicLong
(
0L
);
private
RenderSurface
renderSurface
;
private
boolean
hasRenderedFirstFrame
=
false
;
@Nullable
private
Surface
surface
;
private
boolean
isDisplayingFlutterUi
=
false
;
private
final
OnFirstFrameRenderedListener
onFirstFrameRenderedListener
=
new
OnFirstFrameRenderedListener
()
{
@NonNull
private
final
FlutterUiDisplayListener
flutterUiDisplayListener
=
new
FlutterUiDisplayListener
()
{
@Override
public
void
onFirstFrameRendered
()
{
hasRenderedFirstFrame
=
true
;
public
void
onFlutterUiDisplayed
()
{
isDisplayingFlutterUi
=
true
;
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
isDisplayingFlutterUi
=
false
;
}
};
public
FlutterRenderer
(
@NonNull
FlutterJNI
flutterJNI
)
{
this
.
flutterJNI
=
flutterJNI
;
this
.
flutterJNI
.
add
OnFirstFrameRenderedListener
(
onFirstFrameRendered
Listener
);
this
.
flutterJNI
.
add
IsDisplayingFlutterUiListener
(
flutterUiDisplay
Listener
);
}
/**
* Returns true if this {@code FlutterRenderer} is
attached to the given {@link RenderSurface},
* false otherwise.
* Returns true if this {@code FlutterRenderer} is
painting pixels to an Android {@code View}
*
hierarchy,
false otherwise.
*/
public
boolean
isAttachedTo
(
@NonNull
RenderSurface
renderSurface
)
{
return
this
.
renderSurface
==
renderSurface
;
}
public
void
attachToRenderSurface
(
@NonNull
RenderSurface
renderSurface
)
{
Log
.
v
(
TAG
,
"Attaching to RenderSurface."
);
// TODO(mattcarroll): determine desired behavior when attaching to an already attached renderer
if
(
this
.
renderSurface
!=
null
)
{
Log
.
v
(
TAG
,
"Already attached to a RenderSurface. Detaching from old one and attaching to new one."
);
detachFromRenderSurface
();
}
this
.
renderSurface
=
renderSurface
;
this
.
renderSurface
.
attachToRenderer
(
this
);
this
.
flutterJNI
.
setRenderSurface
(
renderSurface
);
}
public
void
detachFromRenderSurface
()
{
Log
.
v
(
TAG
,
"Detaching from RenderSurface."
);
// TODO(mattcarroll): determine desired behavior if we're asked to detach without first being attached
if
(
this
.
renderSurface
!=
null
)
{
this
.
renderSurface
.
detachFromRenderer
();
this
.
renderSurface
=
null
;
surfaceDestroyed
();
this
.
flutterJNI
.
setRenderSurface
(
null
);
}
public
boolean
isDisplayingFlutterUi
()
{
return
isDisplayingFlutterUi
;
}
public
boolean
hasRenderedFirstFrame
()
{
return
hasRenderedFirstFrame
;
}
public
void
add
OnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRendered
Listener
listener
)
{
flutterJNI
.
add
OnFirstFrameRendered
Listener
(
listener
);
/**
* Adds a listener that is invoked whenever this {@code FlutterRenderer} starts and stops painting
* pixels to an Android {@code View} hierarchy.
*/
public
void
add
IsDisplayingFlutterUiListener
(
@NonNull
FlutterUiDisplay
Listener
listener
)
{
flutterJNI
.
add
IsDisplayingFlutterUi
Listener
(
listener
);
if
(
hasRenderedFirstFrame
)
{
listener
.
onF
irstFrameRender
ed
();
if
(
isDisplayingFlutterUi
)
{
listener
.
onF
lutterUiDisplay
ed
();
}
}
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{
flutterJNI
.
removeOnFirstFrameRenderedListener
(
listener
);
/**
* Removes a listener added via
* {@link #addIsDisplayingFlutterUiListener(FlutterUiDisplayListener)}.
*/
public
void
removeIsDisplayingFlutterUiListener
(
@NonNull
FlutterUiDisplayListener
listener
)
{
flutterJNI
.
removeIsDisplayingFlutterUiListener
(
listener
);
}
//------ START TextureRegistry IMPLEMENTATION -----
// TODO(mattcarroll): detachFromGLContext requires API 16. Create solution for earlier APIs.
@TargetApi
(
Build
.
VERSION_CODES
.
JELLY_BEAN
)
/**
* Creates and returns a new {@link SurfaceTexture} that is also made available to Flutter code.
*/
@Override
public
SurfaceTextureEntry
createSurfaceTexture
()
{
Log
.
v
(
TAG
,
"Creating a SurfaceTexture."
);
...
...
@@ -180,19 +170,57 @@ public class FlutterRenderer implements TextureRegistry {
}
//------ END TextureRegistry IMPLEMENTATION ----
// TODO(mattcarroll): describe the native behavior that this invokes
public
void
surfaceCreated
(
@NonNull
Surface
surface
)
{
/**
* Notifies Flutter that the given {@code surface} was created and is available for Flutter
* rendering.
* <p>
* See {@link android.view.SurfaceHolder.Callback} and
* {@link android.view.TextureView.SurfaceTextureListener}
*/
public
void
startRenderingToSurface
(
@NonNull
Surface
surface
)
{
if
(
this
.
surface
!=
null
)
{
stopRenderingToSurface
();
}
this
.
surface
=
surface
;
flutterJNI
.
onSurfaceCreated
(
surface
);
}
// TODO(mattcarroll): describe the native behavior that this invokes
/**
* Notifies Flutter that a {@code surface} previously registered with
* {@link #startRenderingToSurface(Surface)} has changed size to the given {@code width} and
* {@code height}.
* <p>
* See {@link android.view.SurfaceHolder.Callback} and
* {@link android.view.TextureView.SurfaceTextureListener}
*/
public
void
surfaceChanged
(
int
width
,
int
height
)
{
flutterJNI
.
onSurfaceChanged
(
width
,
height
);
}
// TODO(mattcarroll): describe the native behavior that this invokes
public
void
surfaceDestroyed
()
{
/**
* Notifies Flutter that a {@code surface} previously registered with
* {@link #startRenderingToSurface(Surface)} has been destroyed and needs to be released and
* cleaned up on the Flutter side.
* <p>
* See {@link android.view.SurfaceHolder.Callback} and
* {@link android.view.TextureView.SurfaceTextureListener}
*/
public
void
stopRenderingToSurface
()
{
flutterJNI
.
onSurfaceDestroyed
();
surface
=
null
;
// TODO(mattcarroll): the source of truth for this call should be FlutterJNI, which is where
// the call to onFlutterUiDisplayed() comes from. However, no such native callback exists yet,
// so until the engine and FlutterJNI are configured to call us back when rendering stops,
// we will manually monitor that change here.
if
(
isDisplayingFlutterUi
)
{
flutterUiDisplayListener
.
onFlutterUiNoLongerDisplayed
();
}
isDisplayingFlutterUi
=
false
;
}
// TODO(mattcarroll): describe the native behavior that this invokes
...
...
@@ -279,61 +307,6 @@ public class FlutterRenderer implements TextureRegistry {
);
}
/**
* Delegate used in conjunction with a {@link FlutterRenderer} to create an interactive Flutter
* UI.
*
* A {@code RenderSurface} is responsible for carrying out behaviors that are needed by a
* corresponding {@link FlutterRenderer}.
*
* A {@code RenderSurface} also receives callbacks for important events, e.g.,
* {@link #onFirstFrameRendered()}.
*/
public
interface
RenderSurface
{
/**
* Invoked by the owner of this {@code RenderSurface} when it wants to begin rendering
* a Flutter UI to this {@code RenderSurface}.
*
* The details of how rendering is handled is an implementation detail.
*/
void
attachToRenderer
(
@NonNull
FlutterRenderer
renderer
);
/**
* Invoked by the owner of this {@code RenderSurface} when it no longer wants to render
* a Flutter UI to this {@code RenderSurface}.
*
* This method will cease any on-going rendering from Flutter to this {@code RenderSurface}.
*/
void
detachFromRenderer
();
// TODO(mattcarroll): convert old FlutterView to use FlutterEngine instead of individual
// components, then use FlutterEngine's FlutterRenderer to watch for the first frame and
// remove the following methods from this interface.
/**
* The {@link FlutterRenderer} corresponding to this {@code RenderSurface} has painted its
* first frame since being initialized.
*
* "Initialized" refers to Flutter engine initialization, not the first frame after attaching
* to the {@link FlutterRenderer}. Therefore, the first frame may have already rendered by
* the time a {@code RenderSurface} has called {@link #attachToRenderSurface(RenderSurface)}
* on a {@link FlutterRenderer}. In such a situation, {@code #onFirstFrameRendered()} will
* never be called.
*/
void
onFirstFrameRendered
();
/**
* Adds the given {@code listener} to this {@code FlutterRenderer}, to be notified upon Flutter's
* first rendered frame.
*/
void
addOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
);
/**
* Removes the given {@code listener}, which was previously added with
* {@link #addOnFirstFrameRenderedListener(OnFirstFrameRenderedListener)}.
*/
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
);
}
/**
* Mutable data structure that holds all viewport metrics properties that Flutter cares about.
*
...
...
shell/platform/android/io/flutter/embedding/engine/renderer/
OnFirstFrameRendered
Listener.java
→
shell/platform/android/io/flutter/embedding/engine/renderer/
FlutterUiDisplay
Listener.java
浏览文件 @
fde7c8c3
...
...
@@ -5,16 +5,20 @@
package
io.flutter.embedding.engine.renderer
;
/**
* Listener invoked after Flutter paints its first frame since being initialized.
*
* WARNING: THIS CLASS IS EXPERIMENTAL. DO NOT SHIP A DEPENDENCY ON THIS CODE.
* IF YOU USE IT, WE WILL BREAK YOU.
* Listener invoked when Flutter starts and stops rendering pixels to an Android {@code View}
* hierarchy.
*/
public
interface
OnFirstFrameRendered
Listener
{
public
interface
FlutterUiDisplay
Listener
{
/**
* A {@link FlutterRenderer} has painted its first frame since being initialized.
*
* This method will not be invoked if this listener is added after the first frame is rendered.
* Flutter started painting pixels to an Android {@code View} hierarchy.
* <p>
* This method will not be invoked if this listener is added after the {@link FlutterRenderer}
* has started painting pixels.
*/
void
onFirstFrameRendered
();
void
onFlutterUiDisplayed
();
/**
* Flutter stopped painting pixels to an Android {@code View} hierarchy.
*/
void
onFlutterUiNoLongerDisplayed
();
}
shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
0 → 100644
浏览文件 @
fde7c8c3
// 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.renderer
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.view.Surface
;
/**
* Owns a {@code Surface} that {@code FlutterRenderer} would like to paint.
* <p>
* {@code RenderSurface} is responsible for providing a {@code Surface} to a given
* {@code FlutterRenderer} when requested, and then notify that {@code FlutterRenderer} when
* the {@code Surface} changes, or is destroyed.
* <p>
* The behavior of providing a {@code Surface} is delegated to this interface because the timing
* of a {@code Surface}'s availability is determined by Android. Therefore, an accessor method
* would not fulfill the requirements. Therefore, a {@code RenderSurface} is given a
* {@code FlutterRenderer}, which the {@code RenderSurface} is expected to notify as a
* {@code Surface} becomes available, changes, or is destroyed.
*/
public
interface
RenderSurface
{
/**
* Returns the {@code FlutterRenderer} that is attached to this {@code RenderSurface}, or
* null if no {@code FlutterRenderer} is currently attached.
*/
@Nullable
FlutterRenderer
getAttachedRenderer
();
/**
* Instructs this {@code RenderSurface} to give its {@code Surface} to the given
* {@code FlutterRenderer} so that Flutter can paint pixels on it.
* <p>
* After this call, {@code RenderSurface} is expected to invoke the following methods on
* {@link FlutterRenderer} at the appropriate times:
* <ol>
* <li>{@link FlutterRenderer#startRenderingToSurface(Surface)}</li>
* <li>{@link FlutterRenderer#surfaceChanged(int, int)}}</li>
* <li>{@link FlutterRenderer#stopRenderingToSurface()}</li>
* </ol>
*/
void
attachToRenderer
(
@NonNull
FlutterRenderer
renderer
);
/**
* Instructs this {@code RenderSurface} to stop forwarding {@code Surface} notifications to the
* {@code FlutterRenderer} that was previously connected with
* {@link #attachToRenderer(FlutterRenderer)}.
* <p>
* This {@code RenderSurface} should also clean up any references related to the previously
* connected {@code FlutterRenderer}.
*/
void
detachFromRenderer
();
}
shell/platform/android/io/flutter/view/FlutterNativeView.java
浏览文件 @
fde7c8c3
...
...
@@ -13,16 +13,9 @@ import io.flutter.app.FlutterPluginRegistry;
import
io.flutter.embedding.engine.FlutterJNI
;
import
io.flutter.embedding.engine.FlutterEngine.EngineLifecycleListener
;
import
io.flutter.embedding.engine.dart.DartExecutor
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer.RenderSurface
;
import
io.flutter.embedding.engine.renderer.OnFirstFrameRenderedListener
;
import
io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
;
import
io.flutter.plugin.common.*
;
import
java.nio.ByteBuffer
;
import
java.util.concurrent.atomic.AtomicBoolean
;
import
java.util.HashMap
;
import
java.util.Map
;
import
io.flutter.embedding.engine.dart.PlatformMessageHandler
;
public
class
FlutterNativeView
implements
BinaryMessenger
{
private
static
final
String
TAG
=
"FlutterNativeView"
;
...
...
@@ -34,6 +27,21 @@ public class FlutterNativeView implements BinaryMessenger {
private
final
Context
mContext
;
private
boolean
applicationIsRunning
;
private
final
FlutterUiDisplayListener
flutterUiDisplayListener
=
new
FlutterUiDisplayListener
()
{
@Override
public
void
onFlutterUiDisplayed
()
{
if
(
mFlutterView
==
null
)
{
return
;
}
mFlutterView
.
onFirstFrame
();
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{
// no-op
}
};
public
FlutterNativeView
(
@NonNull
Context
context
)
{
this
(
context
,
false
);
}
...
...
@@ -42,7 +50,7 @@ public class FlutterNativeView implements BinaryMessenger {
mContext
=
context
;
mPluginRegistry
=
new
FlutterPluginRegistry
(
this
,
context
);
mFlutterJNI
=
new
FlutterJNI
();
mFlutterJNI
.
setRenderSurface
(
new
RenderSurfaceImpl
()
);
mFlutterJNI
.
addIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
this
.
dartExecutor
=
new
DartExecutor
(
mFlutterJNI
,
context
.
getAssets
());
mFlutterJNI
.
addEngineLifecycleListener
(
new
EngineLifecycleListenerImpl
());
attach
(
this
,
isBackgroundView
);
...
...
@@ -58,6 +66,7 @@ public class FlutterNativeView implements BinaryMessenger {
mPluginRegistry
.
destroy
();
dartExecutor
.
onDetachedFromJNI
();
mFlutterView
=
null
;
mFlutterJNI
.
removeIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
mFlutterJNI
.
detachFromNativeAndReleaseResources
();
applicationIsRunning
=
false
;
}
...
...
@@ -143,48 +152,6 @@ public class FlutterNativeView implements BinaryMessenger {
dartExecutor
.
onAttachedToJNI
();
}
private
final
class
RenderSurfaceImpl
implements
RenderSurface
{
@Override
public
void
attachToRenderer
(
@NonNull
FlutterRenderer
renderer
)
{
// Not relevant for v1 embedding.
}
@Override
public
void
detachFromRenderer
()
{
// Not relevant for v1 embedding.
}
// Called by native to update the semantics/accessibility tree.
public
void
updateSemantics
(
ByteBuffer
buffer
,
String
[]
strings
)
{
if
(
mFlutterView
==
null
)
{
return
;
}
mFlutterView
.
updateSemantics
(
buffer
,
strings
);
}
// Called by native to update the custom accessibility actions.
public
void
updateCustomAccessibilityActions
(
ByteBuffer
buffer
,
String
[]
strings
)
{
if
(
mFlutterView
==
null
)
{
return
;
}
mFlutterView
.
updateCustomAccessibilityActions
(
buffer
,
strings
);
}
// Called by native to notify first Flutter frame rendered.
public
void
onFirstFrameRendered
()
{
if
(
mFlutterView
==
null
)
{
return
;
}
mFlutterView
.
onFirstFrame
();
}
@Override
public
void
addOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{}
@Override
public
void
removeOnFirstFrameRenderedListener
(
@NonNull
OnFirstFrameRenderedListener
listener
)
{}
}
private
final
class
EngineLifecycleListenerImpl
implements
EngineLifecycleListener
{
// Called by native to notify when the engine is restarted (cold reload).
@SuppressWarnings
(
"unused"
)
...
...
shell/platform/android/io/flutter/view/FlutterView.java
浏览文件 @
fde7c8c3
...
...
@@ -679,29 +679,6 @@ public class FlutterView extends SurfaceView implements BinaryMessenger, Texture
);
}
// Called by native to update the semantics/accessibility tree.
public
void
updateSemantics
(
ByteBuffer
buffer
,
String
[]
strings
)
{
try
{
if
(
mAccessibilityNodeProvider
!=
null
)
{
buffer
.
order
(
ByteOrder
.
LITTLE_ENDIAN
);
mAccessibilityNodeProvider
.
updateSemantics
(
buffer
,
strings
);
}
}
catch
(
Exception
ex
)
{
Log
.
e
(
TAG
,
"Uncaught exception while updating semantics"
,
ex
);
}
}
public
void
updateCustomAccessibilityActions
(
ByteBuffer
buffer
,
String
[]
strings
)
{
try
{
if
(
mAccessibilityNodeProvider
!=
null
)
{
buffer
.
order
(
ByteOrder
.
LITTLE_ENDIAN
);
mAccessibilityNodeProvider
.
updateCustomAccessibilityActions
(
buffer
,
strings
);
}
}
catch
(
Exception
ex
)
{
Log
.
e
(
TAG
,
"Uncaught exception while updating local context actions"
,
ex
);
}
}
// Called by FlutterNativeView to notify first Flutter frame rendered.
public
void
onFirstFrame
()
{
didRenderFirstFrame
=
true
;
...
...
shell/platform/android/io/flutter/view/TextureRegistry.java
浏览文件 @
fde7c8c3
...
...
@@ -6,6 +6,7 @@ package io.flutter.view;
import
android.graphics.SurfaceTexture
;
// TODO(mattcarroll): re-evalute docs in this class and add nullability annotations.
/**
* Registry of backend textures used with a single {@link FlutterView} instance.
* Entries may be embedded into the Flutter view using the
...
...
shell/platform/android/platform_view_android_jni.cc
浏览文件 @
fde7c8c3
...
...
@@ -486,7 +486,7 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env,
bool
RegisterApi
(
JNIEnv
*
env
)
{
static
const
JNINativeMethod
flutter_jni_methods
[]
=
{
// Start of methods from Flutter
NativeView
// Start of methods from Flutter
JNI
{
.
name
=
"nativeAttach"
,
.
signature
=
"(Lio/flutter/embedding/engine/FlutterJNI;Z)J"
,
...
...
shell/platform/android/test/io/flutter/FlutterTestSuite.java
浏览文件 @
fde7c8c3
...
...
@@ -13,7 +13,10 @@ import io.flutter.embedding.android.FlutterActivityTest;
import
io.flutter.embedding.android.FlutterFragmentTest
;
import
io.flutter.embedding.engine.FlutterEngineCacheTest
;
import
io.flutter.embedding.engine.systemchannels.PlatformChannelTest
;
import
io.flutter.embedding.engine.RenderingComponentTest
;
import
io.flutter.embedding.engine.renderer.FlutterRendererTest
;
import
io.flutter.util.PreconditionsTest
;
import
io.flutter.embedding.engine.FlutterJNITest
;
@RunWith
(
Suite
.
class
)
@SuiteClasses
({
...
...
@@ -23,6 +26,9 @@ import io.flutter.util.PreconditionsTest;
FlutterFragmentTest
.
class
,
// FlutterActivityAndFragmentDelegateTest.class, TODO(mklim): Fix and re-enable this
FlutterEngineCacheTest
.
class
,
FlutterJNITest
.
class
,
RenderingComponentTest
.
class
,
FlutterRendererTest
.
class
,
PlatformChannelTest
.
class
})
/** Runs all of the unit tests listed in the {@code @SuiteClasses} annotation. */
...
...
shell/platform/android/test/io/flutter/embedding/engine/FlutterJNITest.java
0 → 100644
浏览文件 @
fde7c8c3
package
io.flutter.embedding.engine
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.annotation.Config
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
@Config
(
manifest
=
Config
.
NONE
)
@RunWith
(
RobolectricTestRunner
.
class
)
public
class
FlutterJNITest
{
@Test
public
void
itAllowsFirstFrameListenersToRemoveThemselvesInline
()
{
// --- Test Setup ---
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
AtomicInteger
callbackInvocationCount
=
new
AtomicInteger
(
0
);
FlutterUiDisplayListener
callback
=
new
FlutterUiDisplayListener
()
{
@Override
public
void
onFlutterUiDisplayed
()
{
callbackInvocationCount
.
incrementAndGet
();
flutterJNI
.
removeIsDisplayingFlutterUiListener
(
this
);
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{}
};
flutterJNI
.
addIsDisplayingFlutterUiListener
(
callback
);
// --- Execute Test ---
flutterJNI
.
onFirstFrame
();
// --- Verify Results ---
assertEquals
(
1
,
callbackInvocationCount
.
get
());
// --- Execute Test ---
// The callback removed itself from the listener list. A second call doesn't call the callback.
flutterJNI
.
onFirstFrame
();
// --- Verify Results ---
assertEquals
(
1
,
callbackInvocationCount
.
get
());
}
}
shell/platform/android/test/io/flutter/embedding/engine/RenderingComponentTest.java
0 → 100644
浏览文件 @
fde7c8c3
package
io.flutter.embedding.engine
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.annotation.Config
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
io.flutter.embedding.engine.renderer.FlutterRenderer
;
import
io.flutter.embedding.engine.renderer.FlutterUiDisplayListener
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
never
;
import
static
org
.
mockito
.
Mockito
.
times
;
import
static
org
.
mockito
.
Mockito
.
verify
;
@Config
(
manifest
=
Config
.
NONE
)
@RunWith
(
RobolectricTestRunner
.
class
)
public
class
RenderingComponentTest
{
@Test
public
void
flutterUiDisplayListenersCanRemoveThemselvesWhenInvoked
()
{
// Setup test.
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
flutterJNI
);
AtomicInteger
listenerInvocationCount
=
new
AtomicInteger
(
0
);
FlutterUiDisplayListener
listener
=
new
FlutterUiDisplayListener
()
{
@Override
public
void
onFlutterUiDisplayed
()
{
// This is the behavior we're testing, but we also verify that this method
// was invoked to ensure that this test behavior executed.
flutterRenderer
.
removeIsDisplayingFlutterUiListener
(
this
);
// Track the invocation to ensure this method is called once, and only once.
listenerInvocationCount
.
incrementAndGet
();
}
@Override
public
void
onFlutterUiNoLongerDisplayed
()
{}
};
flutterRenderer
.
addIsDisplayingFlutterUiListener
(
listener
);
// Execute behavior under test.
// Pretend we are the native side and tell FlutterJNI that Flutter has rendered a frame.
flutterJNI
.
onFirstFrame
();
// Verify results.
// If we got to this point without an exception, and if our listener was called one time,
// then the behavior under test is correct.
assertEquals
(
1
,
listenerInvocationCount
.
get
());
}
@Test
public
void
flutterUiDisplayListenersAddedAfterFirstFrameAreAutomaticallyInvoked
()
{
// Setup test.
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
flutterJNI
);
FlutterUiDisplayListener
listener
=
mock
(
FlutterUiDisplayListener
.
class
);
// Pretend we are the native side and tell FlutterJNI that Flutter has rendered a frame.
flutterJNI
.
onFirstFrame
();
// Execute behavior under test.
flutterRenderer
.
addIsDisplayingFlutterUiListener
(
listener
);
// Verify results.
verify
(
listener
,
times
(
1
)).
onFlutterUiDisplayed
();
}
@Test
public
void
flutterUiDisplayListenersAddedAfterFlutterUiDisappearsAreNotInvoked
()
{
// Setup test.
FlutterJNI
flutterJNI
=
new
FlutterJNI
();
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
flutterJNI
);
FlutterUiDisplayListener
listener
=
mock
(
FlutterUiDisplayListener
.
class
);
// Pretend we are the native side and tell FlutterJNI that Flutter has rendered a frame.
flutterJNI
.
onFirstFrame
();
// Pretend that rendering has stopped.
flutterJNI
.
onRenderingStopped
();
// Execute behavior under test.
flutterRenderer
.
addIsDisplayingFlutterUiListener
(
listener
);
// Verify results.
verify
(
listener
,
never
()).
onFlutterUiDisplayed
();
}
}
shell/platform/android/test/io/flutter/embedding/engine/renderer/FlutterRendererTest.java
0 → 100644
浏览文件 @
fde7c8c3
package
io.flutter.embedding.engine.renderer
;
import
android.view.Surface
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.annotation.Config
;
import
io.flutter.embedding.engine.FlutterJNI
;
import
static
junit
.
framework
.
TestCase
.
assertTrue
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
mockito
.
Matchers
.
eq
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
times
;
import
static
org
.
mockito
.
Mockito
.
verify
;
@Config
(
manifest
=
Config
.
NONE
)
@RunWith
(
RobolectricTestRunner
.
class
)
public
class
FlutterRendererTest
{
private
FlutterJNI
fakeFlutterJNI
;
private
Surface
fakeSurface
;
@Before
public
void
setup
()
{
fakeFlutterJNI
=
mock
(
FlutterJNI
.
class
);
fakeSurface
=
mock
(
Surface
.
class
);
}
@Test
public
void
itForwardsSurfaceCreationNotificationToFlutterJNI
()
{
// Setup the test.
Surface
fakeSurface
=
mock
(
Surface
.
class
);
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
fakeFlutterJNI
);
// Execute the behavior under test.
flutterRenderer
.
startRenderingToSurface
(
fakeSurface
);
// Verify the behavior under test.
verify
(
fakeFlutterJNI
,
times
(
1
)).
onSurfaceCreated
(
eq
(
fakeSurface
));
}
@Test
public
void
itForwardsSurfaceChangeNotificationToFlutterJNI
()
{
// Setup the test.
Surface
fakeSurface
=
mock
(
Surface
.
class
);
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
fakeFlutterJNI
);
flutterRenderer
.
startRenderingToSurface
(
fakeSurface
);
// Execute the behavior under test.
flutterRenderer
.
surfaceChanged
(
100
,
50
);
// Verify the behavior under test.
verify
(
fakeFlutterJNI
,
times
(
1
)).
onSurfaceChanged
(
eq
(
100
),
eq
(
50
));
}
@Test
public
void
itForwardsSurfaceDestructionNotificationToFlutterJNI
()
{
// Setup the test.
Surface
fakeSurface
=
mock
(
Surface
.
class
);
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
fakeFlutterJNI
);
flutterRenderer
.
startRenderingToSurface
(
fakeSurface
);
// Execute the behavior under test.
flutterRenderer
.
stopRenderingToSurface
();
// Verify the behavior under test.
verify
(
fakeFlutterJNI
,
times
(
1
)).
onSurfaceDestroyed
();
}
@Test
public
void
itStopsRenderingToOneSurfaceBeforeRenderingToANewSurface
()
{
// Setup the test.
Surface
fakeSurface2
=
mock
(
Surface
.
class
);
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
fakeFlutterJNI
);
flutterRenderer
.
startRenderingToSurface
(
fakeSurface
);
// Execute behavior under test.
flutterRenderer
.
startRenderingToSurface
(
fakeSurface2
);
// Verify behavior under test.
verify
(
fakeFlutterJNI
,
times
(
1
)).
onSurfaceDestroyed
();
// notification of 1st surface's removal.
}
@Test
public
void
itStopsRenderingToSurfaceWhenRequested
()
{
// Setup the test.
FlutterRenderer
flutterRenderer
=
new
FlutterRenderer
(
fakeFlutterJNI
);
flutterRenderer
.
startRenderingToSurface
(
fakeSurface
);
// Execute the behavior under test.
flutterRenderer
.
stopRenderingToSurface
();
// Verify behavior under test.
verify
(
fakeFlutterJNI
,
times
(
1
)).
onSurfaceDestroyed
();
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录