React Native 应用程序内部的 SurfaceView 使一切空白
SurfaceView inside of React Native app makes everything blank
我正在创建一个 React Native Android 应用程序,我想嵌入一个使用 Vulkan 呈现的控件。我通过创建一个名为 VkSurfaceView
的 SurfaceView
的子类并使用 JNI 调用在 Rust 中实现 Vulkan 代码来实现此控件。
我打开了显示所有视图边界的调试设置,以便清楚地看到视图仍然存在。
这是简单 Android 布局(无 React Native)内的 Vulkan 控件(目前,它只呈现一个三角形):
这是我没有 Vulkan 控件的 React Native 布局(正方形代表我尝试放置 Vulkan 控件的位置):
但是当我放置 Vulkan 控件时,其他所有内容都变暗了,即使布局检查器和调试覆盖表明其他视图仍然存在:
为什么会发生这种情况?如上所示,Vulkan 控件在 pure-Android 布局中工作,因此我认为它必须以某种方式与 React Native 进行不利的交互。但是,我发现像 this and this 这样的示例可以在 React Native 中使用 SurfaceView
/TextureView
/VideoView
而不会引起问题。
编辑: 即使我把VkSurfaceView
中的所有代码都注释掉了,黑屏问题依然存在(但是三角形明显消失了),所以我不要认为这是因为我的 Vulkan 代码有问题。
编辑 2: 如果我从 TextureView
而不是 SurfaceView
派生,它会起作用。但为什么?我怀疑这与所解释的差异有关here,但我仍然不明白为什么视图是否使用 OpenGL 合成很重要。
这是我的代码:
React Native index.js
:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import VkSurfaceView from './surface-view';
const HelloWorld = () => {
return (
<View style={[styles.container, {
// Try setting `flexDirection` to `"row"`.
flexDirection: "column"
}]}>
<Text style={styles.hello}>Hello, World</Text>
<View style={{ width: 100, height: 100, alignSelf: 'center', backgroundColor: 'pink' }}>
<VkSurfaceView style={{ width: 100, height: 100 }}/>
</View>
</View>
);
};
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ffffff'
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color: 'green'
}
});
AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld
);
React Native surface-view.js
:
import { requireNativeComponent } from 'react-native';
module.exports = requireNativeComponent('SPVkSurfaceView');
Java SurfaceReactPackage.java
:
public class SurfaceReactPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@NonNull
@Override
public List<ViewManager> createViewManagers(
@NonNull ReactApplicationContext reactContext) {
return Collections.singletonList(
new ReactSurfaceManager(reactContext)
);
}
}
Java ReactSurfaceManager.java
:
public class ReactSurfaceManager extends SimpleViewManager<VkSurfaceView> {
public static final String REACT_CLASS = "SPVkSurfaceView";
ReactApplicationContext mCallerContext;
public ReactSurfaceManager(ReactApplicationContext reactContext) {
mCallerContext = reactContext;
}
@NonNull
@Override
public VkSurfaceView createViewInstance(ThemedReactContext context) {
return new VkSurfaceView(context);
}
@Override
public String getName() {
return REACT_CLASS;
}
}
Java MainActivity.java
public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize native code library before using VkSurfaceView
MyNativeLibary.init();
// initialize React Native
mReactRootView = new ReactRootView(this);
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
packages.add(new SurfaceReactPackage());
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// The string here (e.g. "MyReactNativeApp") has to match
// the string in AppRegistry.registerComponent() in index.js
mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
setContentView(mReactRootView);
}
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
}
}
@Override
public void invokeDefaultOnBackPressed() {
this.onBackPressed();
}
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
}
一个解决方案是使用 TextureView
而不是 SurfaceView
。但是,这似乎不太有效,因为每隔几秒我就会遇到与帧缓冲相关的超时问题。我实际最终使用的解决方案是将我的 SurfaceView
包裹在 FrameLayout
.
中
我正在创建一个 React Native Android 应用程序,我想嵌入一个使用 Vulkan 呈现的控件。我通过创建一个名为 VkSurfaceView
的 SurfaceView
的子类并使用 JNI 调用在 Rust 中实现 Vulkan 代码来实现此控件。
我打开了显示所有视图边界的调试设置,以便清楚地看到视图仍然存在。
这是简单 Android 布局(无 React Native)内的 Vulkan 控件(目前,它只呈现一个三角形):
这是我没有 Vulkan 控件的 React Native 布局(正方形代表我尝试放置 Vulkan 控件的位置):
但是当我放置 Vulkan 控件时,其他所有内容都变暗了,即使布局检查器和调试覆盖表明其他视图仍然存在:
为什么会发生这种情况?如上所示,Vulkan 控件在 pure-Android 布局中工作,因此我认为它必须以某种方式与 React Native 进行不利的交互。但是,我发现像 this and this 这样的示例可以在 React Native 中使用 SurfaceView
/TextureView
/VideoView
而不会引起问题。
编辑: 即使我把VkSurfaceView
中的所有代码都注释掉了,黑屏问题依然存在(但是三角形明显消失了),所以我不要认为这是因为我的 Vulkan 代码有问题。
编辑 2: 如果我从 TextureView
而不是 SurfaceView
派生,它会起作用。但为什么?我怀疑这与所解释的差异有关here,但我仍然不明白为什么视图是否使用 OpenGL 合成很重要。
这是我的代码:
React Native index.js
:
import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import VkSurfaceView from './surface-view';
const HelloWorld = () => {
return (
<View style={[styles.container, {
// Try setting `flexDirection` to `"row"`.
flexDirection: "column"
}]}>
<Text style={styles.hello}>Hello, World</Text>
<View style={{ width: 100, height: 100, alignSelf: 'center', backgroundColor: 'pink' }}>
<VkSurfaceView style={{ width: 100, height: 100 }}/>
</View>
</View>
);
};
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ffffff'
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
color: 'green'
}
});
AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld
);
React Native surface-view.js
:
import { requireNativeComponent } from 'react-native';
module.exports = requireNativeComponent('SPVkSurfaceView');
Java SurfaceReactPackage.java
:
public class SurfaceReactPackage implements ReactPackage {
@NonNull
@Override
public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@NonNull
@Override
public List<ViewManager> createViewManagers(
@NonNull ReactApplicationContext reactContext) {
return Collections.singletonList(
new ReactSurfaceManager(reactContext)
);
}
}
Java ReactSurfaceManager.java
:
public class ReactSurfaceManager extends SimpleViewManager<VkSurfaceView> {
public static final String REACT_CLASS = "SPVkSurfaceView";
ReactApplicationContext mCallerContext;
public ReactSurfaceManager(ReactApplicationContext reactContext) {
mCallerContext = reactContext;
}
@NonNull
@Override
public VkSurfaceView createViewInstance(ThemedReactContext context) {
return new VkSurfaceView(context);
}
@Override
public String getName() {
return REACT_CLASS;
}
}
Java MainActivity.java
public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// initialize native code library before using VkSurfaceView
MyNativeLibary.init();
// initialize React Native
mReactRootView = new ReactRootView(this);
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
packages.add(new SurfaceReactPackage());
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// The string here (e.g. "MyReactNativeApp") has to match
// the string in AppRegistry.registerComponent() in index.js
mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
setContentView(mReactRootView);
}
@Override
protected void onPause() {
super.onPause();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}
@Override
protected void onResume() {
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
if (mReactRootView != null) {
mReactRootView.unmountReactApplication();
}
}
@Override
public void invokeDefaultOnBackPressed() {
this.onBackPressed();
}
@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}
}
一个解决方案是使用 TextureView
而不是 SurfaceView
。但是,这似乎不太有效,因为每隔几秒我就会遇到与帧缓冲相关的超时问题。我实际最终使用的解决方案是将我的 SurfaceView
包裹在 FrameLayout
.