Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
sxychenjing
engine
提交
c332675a
E
engine
项目概览
sxychenjing
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
3
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,发现更多精彩内容 >>
未验证
提交
c332675a
编写于
6月 26, 2020
作者:
E
Emmanuel Garcia
提交者:
GitHub
6月 26, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Fix hybrid composition bugs (#19325)
上级
fc0e2721
变更
13
隐藏空白更改
内联
并排
Showing
13 changed file
with
216 addition
and
33 deletion
+216
-33
shell/platform/android/external_view_embedder/external_view_embedder.cc
.../android/external_view_embedder/external_view_embedder.cc
+5
-6
shell/platform/android/io/flutter/embedding/android/FlutterImageView.java
...ndroid/io/flutter/embedding/android/FlutterImageView.java
+58
-10
shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
...roid/io/flutter/embedding/android/FlutterSurfaceView.java
+16
-1
shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java
...roid/io/flutter/embedding/android/FlutterTextureView.java
+13
-0
shell/platform/android/io/flutter/embedding/android/FlutterView.java
...orm/android/io/flutter/embedding/android/FlutterView.java
+3
-4
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
...tform/android/io/flutter/embedding/engine/FlutterJNI.java
+17
-0
shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
...io/flutter/embedding/engine/renderer/FlutterRenderer.java
+12
-0
shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
...d/io/flutter/embedding/engine/renderer/RenderSurface.java
+7
-0
shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java
...d/io/flutter/plugin/platform/PlatformViewsController.java
+15
-11
shell/platform/android/platform_view_android.cc
shell/platform/android/platform_view_android.cc
+16
-0
shell/platform/android/platform_view_android.h
shell/platform/android/platform_view_android.h
+3
-0
shell/platform/android/platform_view_android_jni_impl.cc
shell/platform/android/platform_view_android_jni_impl.cc
+19
-0
shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java
...id/test/io/flutter/embedding/android/FlutterViewTest.java
+32
-1
未找到文件。
shell/platform/android/external_view_embedder/external_view_embedder.cc
浏览文件 @
c332675a
...
...
@@ -26,12 +26,12 @@ void AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView(
"AndroidExternalViewEmbedder::PrerollCompositeEmbeddedView"
);
auto
rtree_factory
=
RTreeFactory
();
view_rtrees_
.
insert
({
view_id
,
rtree_factory
.
getInstance
()}
);
view_rtrees_
.
insert
_or_assign
(
view_id
,
rtree_factory
.
getInstance
()
);
auto
picture_recorder
=
std
::
make_unique
<
SkPictureRecorder
>
();
picture_recorder
->
beginRecording
(
SkRect
::
Make
(
frame_size_
),
&
rtree_factory
);
picture_recorders_
.
insert
({
view_id
,
std
::
move
(
picture_recorder
)}
);
picture_recorders_
.
insert
_or_assign
(
view_id
,
std
::
move
(
picture_recorder
)
);
composition_order_
.
push_back
(
view_id
);
// Update params only if they changed.
if
(
view_params_
.
count
(
view_id
)
==
1
&&
...
...
@@ -96,6 +96,8 @@ bool AndroidExternalViewEmbedder::SubmitFrame(
picture_recorders_
.
at
(
view_id
)
->
finishRecordingAsPicture
();
FML_CHECK
(
picture
);
pictures
.
insert
({
view_id
,
picture
});
overlay_layers
.
insert
({
view_id
,
{}});
sk_sp
<
RTree
>
rtree
=
view_rtrees_
.
at
(
view_id
);
// Determinate if Flutter UI intersects with any of the previous
// platform views stacked by z position.
...
...
@@ -124,20 +126,17 @@ bool AndroidExternalViewEmbedder::SubmitFrame(
intersection_rects
.
push_back
(
joined_rect
);
}
for
(
SkRect
&
intersection_rect
:
intersection_rects
)
{
// Get the intersection rect between the current rect
// and the platform view rect.
// joined_rect.intersect(platform_view_rect);
// Subpixels in the platform may not align with the canvas subpixels.
//
// To workaround it, round the floating point bounds and make the rect
// slighly larger. For example, {0.3, 0.5, 3.1, 4.7} becomes {0, 0, 4,
// 5}.
intersection_rect
.
set
(
intersection_rect
.
roundOut
());
overlay_layers
.
at
(
view_id
).
push_back
(
intersection_rect
);
// Clip the background canvas, so it doesn't contain any of the pixels
// drawn on the overlay layer.
background_canvas
->
clipRect
(
intersection_rect
,
SkClipOp
::
kDifference
);
}
overlay_layers
.
insert
({
current_view_id
,
intersection_rects
});
}
background_canvas
->
drawPicture
(
pictures
.
at
(
view_id
));
}
...
...
shell/platform/android/io/flutter/embedding/android/FlutterImageView.java
浏览文件 @
c332675a
...
...
@@ -41,13 +41,33 @@ public class FlutterImageView extends View implements RenderSurface {
@Nullable
private
Bitmap
currentBitmap
;
@Nullable
private
FlutterRenderer
flutterRenderer
;
public
enum
SurfaceKind
{
/** Displays the background canvas. */
background
,
/** Displays the overlay surface canvas. */
overlay
,
}
/** The kind of surface. */
private
SurfaceKind
kind
;
/**
* The number of images acquired from the current {@link android.media.ImageReader} that are
* waiting to be painted. This counter is decreased after calling {@link
* android.media.Image#close()}.
*/
private
int
pendingImages
=
0
;
/**
* Constructs a {@code FlutterImageView} with an {@link android.media.ImageReader} that provides
* the Flutter UI.
*/
public
FlutterImageView
(
@NonNull
Context
context
,
@NonNull
ImageReader
imageReader
)
{
public
FlutterImageView
(
@NonNull
Context
context
,
@NonNull
ImageReader
imageReader
,
SurfaceKind
kind
)
{
super
(
context
,
null
);
this
.
imageReader
=
imageReader
;
this
.
kind
=
kind
;
}
@Nullable
...
...
@@ -62,12 +82,16 @@ public class FlutterImageView extends View implements RenderSurface {
*/
@Override
public
void
attachToRenderer
(
@NonNull
FlutterRenderer
flutterRenderer
)
{
if
(
this
.
flutterRenderer
!=
null
)
{
this
.
flutterRenderer
.
stopRenderingToSurface
();
}
this
.
flutterRenderer
=
flutterRenderer
;
flutterRenderer
.
startRenderingToSurface
(
imageReader
.
getSurface
());
switch
(
kind
)
{
case
background:
flutterRenderer
.
swapSurface
(
imageReader
.
getSurface
());
break
;
case
overlay:
// Don't do anything as this is done by the handler of
// `FlutterJNI#createOverlaySurface()` in the native side.
break
;
}
}
/**
...
...
@@ -75,16 +99,39 @@ public class FlutterImageView extends View implements RenderSurface {
* Flutter UI to this {@code FlutterImageView}.
*/
public
void
detachFromRenderer
()
{
if
(
flutterRenderer
!=
null
)
{
flutterRenderer
.
stopRenderingToSurface
();
flutterRenderer
=
null
;
switch
(
kind
)
{
case
background:
// TODO: Swap the surface back to the original one.
// https://github.com/flutter/flutter/issues/58291
break
;
case
overlay:
// TODO: Handle this in the native side.
// https://github.com/flutter/flutter/issues/59904
break
;
}
}
public
void
pause
()
{
// Not supported.
}
/** Acquires the next image to be drawn to the {@link android.graphics.Canvas}. */
@TargetApi
(
19
)
public
void
acquireLatestImage
()
{
nextImage
=
imageReader
.
acquireLatestImage
();
// There's no guarantee that the image will be closed before the next call to
// `acquireLatestImage()`. For example, the device may not produce new frames if
// it's in sleep mode, so the calls to `invalidate()` will be queued up
// until the device produces a new frame.
//
// While the engine will also stop producing frames, there is a race condition.
//
// To avoid exceptions, check if a new image can be acquired.
if
(
pendingImages
<
imageReader
.
getMaxImages
())
{
nextImage
=
imageReader
.
acquireLatestImage
();
if
(
nextImage
!=
null
)
{
pendingImages
++;
}
}
invalidate
();
}
...
...
@@ -94,6 +141,7 @@ public class FlutterImageView extends View implements RenderSurface {
if
(
nextImage
!=
null
)
{
if
(
currentImage
!=
null
)
{
currentImage
.
close
();
pendingImages
--;
}
currentImage
=
nextImage
;
nextImage
=
null
;
...
...
shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
浏览文件 @
c332675a
...
...
@@ -200,7 +200,7 @@ public class FlutterSurfaceView extends SurfaceView implements RenderSurface {
// Make the SurfaceView invisible to avoid showing a black rectangle.
setAlpha
(
0.0f
);
this
.
flutterRenderer
.
removeIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
flutterRenderer
.
removeIsDisplayingFlutterUiListener
(
flutterUiDisplayListener
);
flutterRenderer
=
null
;
isAttachedToFlutterRenderer
=
false
;
...
...
@@ -209,6 +209,21 @@ public class FlutterSurfaceView extends SurfaceView implements RenderSurface {
}
}
/**
* Invoked by the owner of this {@code FlutterSurfaceView} when it should pause rendering Flutter
* UI to this {@code FlutterSurfaceView}.
*/
public
void
pause
()
{
if
(
flutterRenderer
!=
null
)
{
// Don't remove the `flutterUiDisplayListener` as `onFlutterUiDisplayed()` will make
// the `FlutterSurfaceView` visible.
flutterRenderer
=
null
;
isAttachedToFlutterRenderer
=
false
;
}
else
{
Log
.
w
(
TAG
,
"pause() invoked when no FlutterRenderer was attached."
);
}
}
// FlutterRenderer and getSurfaceTexture() must both be non-null.
private
void
connectSurfaceToRenderer
()
{
if
(
flutterRenderer
==
null
||
getHolder
()
==
null
)
{
...
...
shell/platform/android/io/flutter/embedding/android/FlutterTextureView.java
浏览文件 @
c332675a
...
...
@@ -173,6 +173,19 @@ public class FlutterTextureView extends TextureView implements RenderSurface {
}
}
/**
* Invoked by the owner of this {@code FlutterTextureView} when it should pause rendering Flutter
* UI to this {@code FlutterTextureView}.
*/
public
void
pause
()
{
if
(
flutterRenderer
!=
null
)
{
flutterRenderer
=
null
;
isAttachedToFlutterRenderer
=
false
;
}
else
{
Log
.
w
(
TAG
,
"pause() invoked when no FlutterRenderer was attached."
);
}
}
// FlutterRenderer and getSurfaceTexture() must both be non-null.
private
void
connectSurfaceToRenderer
()
{
if
(
flutterRenderer
==
null
||
getSurfaceTexture
()
==
null
)
{
...
...
shell/platform/android/io/flutter/embedding/android/FlutterView.java
浏览文件 @
c332675a
...
...
@@ -943,16 +943,15 @@ public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseC
}
public
void
convertToImageView
()
{
renderSurface
.
detachFromRenderer
();
renderSurface
.
pause
();
ImageReader
imageReader
=
PlatformViewsController
.
createImageReader
(
getWidth
(),
getHeight
());
flutterImageView
=
new
FlutterImageView
(
getContext
(),
imageReader
);
flutterImageView
=
new
FlutterImageView
(
getContext
(),
imageReader
,
FlutterImageView
.
SurfaceKind
.
background
);
renderSurface
=
flutterImageView
;
if
(
flutterEngine
!=
null
)
{
renderSurface
.
attachToRenderer
(
flutterEngine
.
getRenderer
());
}
removeAllViews
();
addView
(
flutterImageView
);
}
...
...
shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java
浏览文件 @
c332675a
...
...
@@ -313,6 +313,23 @@ public class FlutterJNI {
private
native
void
nativeSurfaceCreated
(
long
nativePlatformViewId
,
@NonNull
Surface
surface
);
/**
* In hybrid composition, call this method when the {@link Surface} has changed.
*
* <p>In hybrid composition, the root surfaces changes from {@link
* android.view.SurfaceHolder#getSurface()} to {@link android.media.ImageReader#getSurface()} when
* a platform view is in the current frame.
*/
@UiThread
public
void
onSurfaceWindowChanged
(
@NonNull
Surface
surface
)
{
ensureRunningOnMainThread
();
ensureAttachedToNative
();
nativeSurfaceWindowChanged
(
nativePlatformViewId
,
surface
);
}
private
native
void
nativeSurfaceWindowChanged
(
long
nativePlatformViewId
,
@NonNull
Surface
surface
);
/**
* Call this method when the {@link Surface} changes that was previously registered with {@link
* #onSurfaceCreated(Surface)}.
...
...
shell/platform/android/io/flutter/embedding/engine/renderer/FlutterRenderer.java
浏览文件 @
c332675a
...
...
@@ -183,6 +183,18 @@ public class FlutterRenderer implements TextureRegistry {
flutterJNI
.
onSurfaceCreated
(
surface
);
}
/**
* Swaps the {@link Surface} used to render the current frame.
*
* <p>In hybrid composition, the root surfaces changes from {@link
* android.view.SurfaceHolder#getSurface()} to {@link android.media.ImageReader#getSurface()} when
* a platform view is in the current frame.
*/
public
void
swapSurface
(
@NonNull
Surface
surface
)
{
this
.
surface
=
surface
;
flutterJNI
.
onSurfaceWindowChanged
(
surface
);
}
/**
* Notifies Flutter that a {@code surface} previously registered with {@link
* #startRenderingToSurface(Surface)} has changed size to the given {@code width} and {@code
...
...
shell/platform/android/io/flutter/embedding/engine/renderer/RenderSurface.java
浏览文件 @
c332675a
...
...
@@ -53,4 +53,11 @@ public interface RenderSurface {
* connected {@code FlutterRenderer}.
*/
void
detachFromRenderer
();
/**
* Instructs this {@code RenderSurface} to stop forwarding {@code Surface} notifications to the
* {@code FlutterRenderer} that was previously connected with {@link
* #attachToRenderer(FlutterRenderer)}.
*/
void
pause
();
}
shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java
浏览文件 @
c332675a
...
...
@@ -54,6 +54,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
private
Context
context
;
// The View currently rendering the Flutter UI associated with these platform views.
// TODO(egarciad): Investigate if this can be downcasted to `FlutterView`.
private
View
flutterView
;
// The texture registry maintaining the textures into which the embedded views will be rendered.
...
...
@@ -557,20 +558,23 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
vdControllers
.
clear
();
}
private
void
initializeRootImageViewIfNeeded
()
{
if
(!
flutterViewConvertedToImageView
)
{
((
FlutterView
)
flutterView
).
convertToImageView
();
flutterViewConvertedToImageView
=
true
;
}
}
public
void
onDisplayPlatformView
(
int
viewId
,
int
x
,
int
y
,
int
width
,
int
height
)
{
initializeRootImageViewIfNeeded
();
// TODO: Implement this method. https://github.com/flutter/flutter/issues/58288
}
public
void
onDisplayOverlaySurface
(
int
id
,
int
x
,
int
y
,
int
width
,
int
height
)
{
FlutterView
flutterView
=
(
FlutterView
)
this
.
flutterView
;
if
(!
flutterViewConvertedToImageView
)
{
flutterView
.
convertToImageView
();
flutterViewConvertedToImageView
=
true
;
}
initializeRootImageViewIfNeeded
();
FlutterImageView
overlayView
=
overlayLayerViews
.
get
(
id
);
if
(
overlayView
.
getParent
()
==
null
)
{
flutterView
.
addView
(
overlayView
);
((
FlutterView
)
flutterView
)
.
addView
(
overlayView
);
}
FrameLayout
.
LayoutParams
layoutParams
=
new
FrameLayout
.
LayoutParams
((
int
)
width
,
(
int
)
height
);
...
...
@@ -579,7 +583,6 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
overlayView
.
setLayoutParams
(
layoutParams
);
overlayView
.
setVisibility
(
View
.
VISIBLE
);
overlayView
.
bringToFront
();
currentFrameUsedOverlayLayerIds
.
add
(
id
);
}
...
...
@@ -599,8 +602,7 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
}
if
(
flutterViewConvertedToImageView
)
{
FlutterView
flutterView
=
(
FlutterView
)
this
.
flutterView
;
flutterView
.
acquireLatestImageViewFrame
();
((
FlutterView
)
flutterView
).
acquireLatestImageViewFrame
();
}
}
...
...
@@ -621,7 +623,9 @@ public class PlatformViewsController implements PlatformViewsAccessibilityDelega
@TargetApi
(
19
)
public
FlutterOverlaySurface
createOverlaySurface
()
{
ImageReader
imageReader
=
createImageReader
(
flutterView
.
getWidth
(),
flutterView
.
getHeight
());
FlutterImageView
imageView
=
new
FlutterImageView
(
flutterView
.
getContext
(),
imageReader
);
FlutterImageView
imageView
=
new
FlutterImageView
(
flutterView
.
getContext
(),
imageReader
,
FlutterImageView
.
SurfaceKind
.
overlay
);
int
id
=
nextOverlayLayerId
++;
overlayLayerViews
.
put
(
id
,
imageView
);
...
...
shell/platform/android/platform_view_android.cc
浏览文件 @
c332675a
...
...
@@ -104,6 +104,22 @@ void PlatformViewAndroid::NotifyCreated(
PlatformView
::
NotifyCreated
();
}
void
PlatformViewAndroid
::
NotifySurfaceWindowChanged
(
fml
::
RefPtr
<
AndroidNativeWindow
>
native_window
)
{
if
(
android_surface_
)
{
fml
::
AutoResetWaitableEvent
latch
;
fml
::
TaskRunner
::
RunNowOrPostTask
(
task_runners_
.
GetRasterTaskRunner
(),
[
&
latch
,
surface
=
android_surface_
.
get
(),
native_window
=
std
::
move
(
native_window
)]()
{
surface
->
TeardownOnScreenContext
();
surface
->
SetNativeWindow
(
native_window
);
latch
.
Signal
();
});
latch
.
Wait
();
}
}
void
PlatformViewAndroid
::
NotifyDestroyed
()
{
PlatformView
::
NotifyDestroyed
();
...
...
shell/platform/android/platform_view_android.h
浏览文件 @
c332675a
...
...
@@ -41,6 +41,9 @@ class PlatformViewAndroid final : public PlatformView {
void
NotifyCreated
(
fml
::
RefPtr
<
AndroidNativeWindow
>
native_window
);
void
NotifySurfaceWindowChanged
(
fml
::
RefPtr
<
AndroidNativeWindow
>
native_window
);
void
NotifyChanged
(
const
SkISize
&
size
);
// |PlatformView|
...
...
shell/platform/android/platform_view_android_jni_impl.cc
浏览文件 @
c332675a
...
...
@@ -140,6 +140,20 @@ static void SurfaceCreated(JNIEnv* env,
ANDROID_SHELL_HOLDER
->
GetPlatformView
()
->
NotifyCreated
(
std
::
move
(
window
));
}
static
void
SurfaceWindowChanged
(
JNIEnv
*
env
,
jobject
jcaller
,
jlong
shell_holder
,
jobject
jsurface
)
{
// Note: This frame ensures that any local references used by
// ANativeWindow_fromSurface are released immediately. This is needed as a
// workaround for https://code.google.com/p/android/issues/detail?id=68174
fml
::
jni
::
ScopedJavaLocalFrame
scoped_local_reference_frame
(
env
);
auto
window
=
fml
::
MakeRefCounted
<
AndroidNativeWindow
>
(
ANativeWindow_fromSurface
(
env
,
jsurface
));
ANDROID_SHELL_HOLDER
->
GetPlatformView
()
->
NotifySurfaceWindowChanged
(
std
::
move
(
window
));
}
static
void
SurfaceChanged
(
JNIEnv
*
env
,
jobject
jcaller
,
jlong
shell_holder
,
...
...
@@ -541,6 +555,11 @@ bool RegisterApi(JNIEnv* env) {
.
signature
=
"(JLandroid/view/Surface;)V"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
SurfaceCreated
),
},
{
.
name
=
"nativeSurfaceWindowChanged"
,
.
signature
=
"(JLandroid/view/Surface;)V"
,
.
fnPtr
=
reinterpret_cast
<
void
*>
(
&
SurfaceWindowChanged
),
},
{
.
name
=
"nativeSurfaceChanged"
,
.
signature
=
"(JII)V"
,
...
...
shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java
浏览文件 @
c332675a
...
...
@@ -2,6 +2,7 @@ package io.flutter.embedding.android;
import
static
junit
.
framework
.
TestCase
.
assertEquals
;
import
static
org
.
mockito
.
Matchers
.
any
;
import
static
org
.
mockito
.
Mockito
.
doNothing
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
spy
;
import
static
org
.
mockito
.
Mockito
.
times
;
...
...
@@ -12,6 +13,7 @@ import android.annotation.TargetApi;
import
android.content.Context
;
import
android.content.res.Configuration
;
import
android.content.res.Resources
;
import
android.media.Image
;
import
android.media.ImageReader
;
import
android.view.View
;
import
android.view.ViewGroup
;
...
...
@@ -374,14 +376,43 @@ public class FlutterViewTest {
@Test
public
void
flutterImageView_acquiresImageAndInvalidates
()
{
final
ImageReader
mockReader
=
mock
(
ImageReader
.
class
);
when
(
mockReader
.
getMaxImages
()).
thenReturn
(
2
);
final
FlutterImageView
imageView
=
spy
(
new
FlutterImageView
(
RuntimeEnvironment
.
application
,
mockReader
));
spy
(
new
FlutterImageView
(
RuntimeEnvironment
.
application
,
mockReader
,
FlutterImageView
.
SurfaceKind
.
background
));
imageView
.
acquireLatestImage
();
verify
(
mockReader
,
times
(
1
)).
acquireLatestImage
();
verify
(
imageView
,
times
(
1
)).
invalidate
();
}
@Test
public
void
flutterImageView_acquiresMaxImagesAtMost
()
{
final
ImageReader
mockReader
=
mock
(
ImageReader
.
class
);
when
(
mockReader
.
getMaxImages
()).
thenReturn
(
2
);
final
Image
mockImage
=
mock
(
Image
.
class
);
when
(
mockReader
.
acquireLatestImage
()).
thenReturn
(
mockImage
);
final
FlutterImageView
imageView
=
spy
(
new
FlutterImageView
(
RuntimeEnvironment
.
application
,
mockReader
,
FlutterImageView
.
SurfaceKind
.
background
));
doNothing
().
when
(
imageView
).
invalidate
();
imageView
.
acquireLatestImage
();
imageView
.
acquireLatestImage
();
imageView
.
acquireLatestImage
();
verify
(
mockReader
,
times
(
2
)).
acquireLatestImage
();
}
/*
* A custom shadow that reports fullscreen flag for system UI visibility
*/
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录