本文分析了React Native页面返回后在Android手机上引起Native页面图片闪屏闪动问题的原因,并提供了解决方案。
《卜算子·咏梅》
驿外断桥边,寂寞开无主。
已是黄昏独自愁,更著风和雨。
无意苦争春,一任群芳妒。
零落成泥碾作尘,只有香如故。
-宋代,陆游
前言
有同事反馈打开React Native页面返回后引起Native页面所有图片闪屏,在一些低端手机上效果更明显,因为Native页面和React Native共用Fresco框架加载的图片,从现象上看猜测由于缓存被清理导致。
发布页面是React Native页面,可以看到从发布页面返回后,切换Tab到首页发现首页列表的图片会闪屏。
问题原因
查看源码发现当RN页面销毁时默认会清除Fresco图片的内存缓存。
MainReactPackage.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public class MainReactPackage extends LazyReactPackage {
...
@Override
public List<ModuleSpec> getNativeModules(final ReactApplicationContext context) {
return Arrays.asList(
ModuleSpec.nativeModuleSpec(
FrescoModule.class,
new Provider<NativeModule>() {
@Override
public NativeModule get() {
return new FrescoModule(
context, true, mConfig != null ? mConfig.getFrescoConfig() : null);
}
}));
}
}
FrescoModule.java1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24public class FrescoModule extends ReactContextBaseJavaModule implements
ModuleDataCleaner.Cleanable, LifecycleEventListener {
...
public FrescoModule(
ReactApplicationContext reactContext,
boolean clearOnDestroy,
@Nullable ImagePipelineConfig config) {
super(reactContext);
mClearOnDestroy = clearOnDestroy;
mConfig = config;
}
...
@Override
public void onHostDestroy() {
// According to the javadoc for LifecycleEventListener#onHostDestroy, this is only called when
// the 'last' ReactActivity is being destroyed, which effectively means the app is being
// backgrounded.
if (hasBeenInitialized() && mClearOnDestroy) {
Fresco.getImagePipeline().clearMemoryCaches();
}
}
}
onHostDestroy
方法里注释的大概意思是:根据LifecycleEventListener#onHostDestroy的java文档,这个方法只会在最后一个ReactActivity被销毁时才会被调用,这实际上意味着应用程序已经进入后台。
但因为我们的应用是Native和React Native混合开发的,并且共用了Fresco框架加载图片,所以当最后一个ReactActivity被销毁时并不表示应用程序已经进入后台。
解决方案
我们可以通过创建一个类继承MainReactPackage,然后重写getNativeModules方法,创建FrescoModule时第二个参数传入false,表示ReactActivity的页面的销毁不清楚Fresco的内存缓存。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30public class WubaMainReactPackage extends MainReactPackage {
private MainPackageConfig mConfig;
public WubaMainReactPackage(MainPackageConfig config) {
super(config);
this.mConfig = config;
}
@Override
public List<ModuleSpec> getNativeModules(ReactApplicationContext context) {
return Arrays.asList(ModuleSpec.nativeModuleSpec(AccessibilityInfoModule.class, new Provider<NativeModule>() {
public NativeModule get() {
return new AccessibilityInfoModule(context);
}
}),
...
ModuleSpec.nativeModuleSpec(FrescoModule.class, new Provider<NativeModule>() {
public NativeModule get() {
return new FrescoModule(context, false, WubaMainReactPackage.this.mConfig != null ? WubaMainReactPackage.this.mConfig.getFrescoConfig() : null);
}
}),
...
ModuleSpec.nativeModuleSpec(WebSocketModule.class, new Provider<NativeModule>() {
public NativeModule get() {
return new WebSocketModule(context);
}
}));
}
}
在使用时传入我们新创建的Package即可。
解决后的效果:
可以看到从React Native发布页面返回后切换Tab没有了闪屏现象。