React Native 启动白屏问题解决方案,教程
2017-03-22 18:52
471 查看
问题描述:
问题分析:
为什么会产生白屏?React Native应用在启动时会将js bundle读取到内存中,并完成渲染。这期间由于js bundle还没有完成装载并渲染,所以界面显示的是白屏。
白屏给人的感觉很不友好,那有没有办法不显示白屏呢?
上文解释了:为什么React Native应用会在启动的时候显示一会白屏。既然知道了出现问题的原因,那么离解决问题也不远了。市场上大部分APP在启动的时候都会有个启动屏,启动屏对于用户是比较友好的,一来展示欢迎信息,二来显示一些产品信息或一些广告,启动页对于程序来说,是为程序完成初始化加载数据,做一些初始化工作的所保留的时间,启动屏等待的时间可长可短,具体根据业务而定。
下面我就教大家如何给React Native 应用添加启动屏,并解决启动白屏的问题。
Android启动白屏解决方案
我们可以通过为React Native Android应用添加启动屏的方式,来解决启动白屏的问题。我在《ReactNative Android启动屏,启动白屏,闪现白屏》一文中介绍过一种为React Native Android应用添加启动屏的方法, 不过那种方法虽好,但牵扯到对React Native 源码的修改,如果React Native 版本有更新还需要对源码做一些处理,所以以后维护起来不是很方便。
下面就向大家介绍另外一种为React Native Android应用添加启动屏的方案。
在《React
Native Android启动屏,启动白屏,闪现白屏》一文中 我们使用的是在根视图容器上添加一个视图作为启动屏,当js bundle加载并渲染完成后,再将添加的视图从根视图上移除。在根视图上添加一个视图的方式其实就是为了遮挡白屏,既然是遮挡白屏,我们是不是可以弹出一个对话框呢?
小伙伴们肯定会说,对话框也不是全屏啊,主题也不一样啊,不过没关系,既然我们可以添加对话框,那么我们就可以修改对话框的样式来达到我们需要的效果。
要达到启动屏的效果,我们需要一个什么样效果的对话框呢?
在APP启动的时候显示;
在js bundle加载并渲染完成后消失;
全屏显示;
显示的内容可以通过 layout xml 进行修改;
上述是我们对这个对话框的基本需求,现在就让我们来实现这一需求:
第一步,创建一个对话框组件SplashScreen
为满足上述需求,对话框组件需要提供下面两个方法:1.显示对话框的方法:
/** * 打开启动屏 */ public static void show(final Activity activity,final boolean fullScreen) { if (activity == null) return; mActivity = new WeakReference<Activity>(activity); activity.runOnUiThread(new Runnable() { @Override public void run() { if (!activity.isFinishing()) { mSplashDialog = new Dialog(activity,fullScreen? R.style.SplashScreen_Fullscreen:R.style.SplashScreen_SplashTheme); mSplashDialog.setContentView(R.layout.launch_screen); mSplashDialog.setCancelable(false); if (!mSplashDialog.isShowing()) { mSplashDialog.show(); } } } }); }
为了Activity被销毁的时候,持有的Activity能被及时的回收,这里我们通过
new WeakReference<Activity>(activity);创建了一个Activity的弱引用。
另外,因为在Android中所有的有关UI操作都必须在主线程,所有我们通过
activity.runOnUiThread(new Runnable()...,将对话框的显示放在了主线程处理。
上述代码中,
show的第二个参数
fullScreen表示启动屏是全屏显示(即是否隐藏状态栏),代码会控制对话框加载不同的主题样式R.style.SplashScreen_Fullscreen与R.style.SplashScreen_SplashTheme来达到是否隐藏状态的需求。
然后,我们可以在
MainActivity.java的
onCreate方法中调
void show(final Activity activity,final boolean fullScreen)方法来显示启动屏。
@Override protected void onCreate(Bundle savedInstanceState) { SplashScreen.show(this,true); super.onCreate(savedInstanceState); }
提示:
SplashScreen.show(this,true);放在
super.onCreate(savedInstanceState);之前的位置效果会更好。
2.关闭对话框的方法:
/** * 关闭启动屏 */ public static void hide(Activity activity) { if (activity == null) activity = mActivity.get(); if (activity == null) return; activity.runOnUiThread(new Runnable() { @Override public void run() { if (mSplashDialog != null && mSplashDialog.isShowing()) { mSplashDialog.dismiss(); } } }); }
上述代码中,我们提供了关闭启动屏的方法。那么如何才能让JS模块调用
void hide(Activity activity)来关闭启动屏呢?
第二步:向JS模块提供SplashScreen组件
因为我们需要在js中调用hide方法还控制启动屏的关闭。js不能直接调Java,所有我们需要为他们搭建一个桥梁(Native
Modules)。
首先,创建一个
ReactContextBaseJavaModule类型的类,供js调用。
/** * SplashScreenModule * 出自:http://www.devio.org * GitHub:https://github.com/crazycodeboy * Eamil:crazycodeboy@gmail.com */ public class SplashScreenModule extends ReactContextBaseJavaModule{ public SplashScreenModule(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "SplashScreen"; } /** * 打开启动屏 */ @ReactMethod public void show() { SplashScreen.show(getCurrentActivity()); } /** * 关闭启动屏 */ @ReactMethod public void hide() { SplashScreen.hide(getCurrentActivity()); } }
其次,创建一个
ReactPackage类型的类,用于向React
Native注册我们的
SplashScreenModule组件。
/** * SplashScreenReactPackage * 出自:http://www.devio.org * GitHub:https://github.com/crazycodeboy * Eamil:crazycodeboy@gmail.com */ public class SplashScreenReactPackage implements ReactPackage { @Override public List<Class<? extends JavaScriptModule>> createJSModules() { return Collections.emptyList(); } @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new SplashScreenModule(reactContext)); return modules; } }
再次,在MainApplication中注册
SplashScreenModule组件。
@Override protected List<ReactPackage> getPackages() { return Arrays.<ReactPackage>asList( new MainReactPackage(), new SplashScreenReactPackage() ); }
准备工作,做好之后,下面我们就可以在JS中调用
hide方法来关闭启动屏了。
第三步:在JS模块中控制启动屏的关闭
创建一个名为SplashScreen的文件,加入下面代码。/** * SplashScreen * 启动屏 * 出自:http://www.devio.org * GitHub:https://github.com/crazycodeboy * Eamil:crazycodeboy@gmail.com * @flow */ 'use strict'; import { NativeModules } from 'react-native'; module.exports = NativeModules.SplashScreen;
然后,我们可以在js中调用SplashScreen的hide()方法来关闭启动屏了。
componentDidMount() { SplashScreen.hide(); }
不要忘记在使用SplashScreen的js文件中导入它哦
import SplashScreen from './SplashScreen。
iOS启动白屏解决方案
在iOS中,iOS支持为程序设置一个Launch Image或Launch Screen File来作为启动屏,当程序被打开的时候,首先显示的便是设置的这个启动屏了。那么小伙伴会问了,这个启动屏幕什么时候会消失呢?
在
AppDelegate如下方法:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
该方法返回一个 BOOL类型的值,当系统调用该方并返回值之后,标志着APP启动加载已经完成,系统会将启动屏给关掉。
所以如果我们控制了这个启动屏幕让它在js bundle加载并渲染完成之后再关闭不就解决了iOS 启动白屏了吗?
上面已经说到,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法执行完成之后,启动屏会被关掉。
所以我们就想办法控制该方实行的时间。
第一步:创建一个名为SplashScreen的Object-C文件
在SplashScreen.h文件中添加如下代码:// // SplashScreen.h // SplashScreen // 出自:http://www.devio.org // GitHub:https://github.com/crazycodeboy // Eamil:crazycodeboy@gmail.com #import "RCTBridgeModule.h" @interface SplashScreen : NSObject<RCTBridgeModule> + (void)show; @end
在SplashScreen.m中添加如下代码:
// SplashScreen // 出自:http://www.devio.org // GitHub:https://github.com/crazycodeboy // Eamil:crazycodeboy@gmail.com #import "SplashScreen.h" static bool waiting = true; @implementation SplashScreen - (dispatch_queue_t)methodQueue{ return dispatch_get_main_queue(); } RCT_EXPORT_MODULE() + (void)show { while (waiting) { NSDate* later = [NSDate dateWithTimeIntervalSinceNow:0.1]; [[NSRunLoop mainRunLoop] runUntilDate:later]; } } RCT_EXPORT_METHOD(hide) { dispatch_async(dispatch_get_main_queue(), ^{ waiting = false; }); } @end
在上述代码中,我们通过
[[NSRunLoop mainRunLoop] runUntilDate:later];来控制
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法执行的时间, 主线程会每隔0.1s阻塞一次,直到
waiting变量为true,然后我们就可以通过暴露给JS模块的
hide方法来控制
waiting变量的值,继而达到控制启动屏幕的关闭。
第二步:在JS模块中控制启动屏的关闭
通过第一步我们已经向JS模块暴露了hide方法,然我们就可以在JS模块中通过
hide方法来关闭启动屏幕。
由于iOS在JS模块中控制启动屏的关闭的方法和Android中第三步:在JS模块中控制启动屏的关闭的方法是一样的,这里就不再介绍了。
开源库
为了方便大家使用和解决React Native应用启动白屏的问题,我已经将上述方案做成React Native组件react-native-splash-screen,开源在了GitHub上,小伙伴们可以下载使用。
相关文章推荐
- React Native 启动白屏问题解决方案,教程
- React Native 启动白屏问题解决方案,教程
- React Native启动白屏问题
- React Native 添加启动屏教程,React Native启动白屏解决方案
- 启动应用的白屏问题解决方案
- React-Native iOS启动闪白屏问题完美解决方案:react-native-launch-image
- React Native启动白屏问题
- React-Native调试状态下App运行缓慢问题解决方案
- React-Native之无法找到 react-native-windows 问题解决方案
- React native 启动白屏解决
- App的快速启动及启动时白屏问题解决方案
- react-native开发Android启动白屏的问题
- React Native带你从源码解决启动白屏(Android)
- xammp start apache service FQA(xammp启动apache服务问题解决方案汇总)
- app启动前闪现白屏、黑屏问题解决
- Android解决APP启动白屏或者黑屏闪现的问题
- win7下sybase数据库安装 启动报空指针异常问题解决方案
- MySQL下常见的启动失败与备份失败问题的解决教程
- MySQL下常见的启动失败与备份失败问题的解决教程
- Android 启动白屏问题解决