技术控

    今日:2| 主题:49195
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] React Native 启动白屏问题解决方案,教程

[复制链接]
螢窗夜話 发表于 2016-9-30 16:52:51
379 7

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
目录

  
       
  •       问题描述   
  •       问题分析   
  •       Android启动白屏解决方案   
  •       iOS启动白屏解决方案  
        项目源码:react-native-splash-screen      
  问题描述:

  用React Native架构的无论是Android APP还是iOS APP,在启动时都出现白屏现象,时间大概1~3s(根据手机或模拟器的性能不同而不同)。
  问题分析:

  为什么会产生白屏?

  React Native应用在启动时会将js bundle读取到内存中,并完成渲染。这期间由于js bundle还没有完成装载并渲染,所以界面显示的是白屏。
  白屏给人的感觉很不友好,那有没有办法不显示白屏呢?
  上文解释了:为什么React Native应用会在启动的时候显示一会白屏。既然知道了出现问题的原因,那么离解决问题也不远了。市场上大部分APP在启动的时候都会有个启动屏,启动屏对于用户是比较友好的,一来展示欢迎信息,二来显示一些产品信息或一些广告,启动页对于程序来说,是为程序完成初始化加载数据,做一些初始化工作的所保留的时间,启动屏等待的时间可长可短,具体根据业务而定。
  下面我就教大家如何给React Native 应用添加启动屏,并解决启动白屏的问题。
  Android启动白屏解决方案

  我们可以为React Native Android应用添加启动屏,来解决启动白屏的问题。我在    《React Native Android启动屏,启动白屏,闪现白屏》一文中介绍过一种为React Native Android应用添加启动屏的方法, 不过那种方法虽好,但牵扯到对React Native 源码的修改,如果React Native 版本有更新还需要对源码做一些处理,所以以后维护起来不是很方便。  
  下面就向大家介绍另外一种为React Native Android应用添加启动屏的方案。
  在    《React Native Android启动屏,启动白屏,闪现白屏》一文中 我们使用的是在根视图容器上添加一个视图作为启动屏,当js bundle加载并渲染完成后,再将添加的视图从根视图上移除。在根视图上添加一个视图的方式其实就是为了遮挡白屏,既然是遮挡白屏,我们是不是可以弹出一个对话框呢?  
  小伙伴们肯定会说,对话框也不是全屏啊,主题也不一样啊,不过没关系,既然我们可以添加对话框,那么我们就可以修改对话框的样式来达到我们需要的效果。
  要达到启动屏的效果,我们需要一个什么样效果的对话框呢?
  
       
  • 在APP启动的时候显示;   
  • 在js bundle加载并渲染完成后消失;   
  • 全屏显示;   
  • 显示的内容可以通过 layout xml 进行修改;  
  上述是我们对这个对话框的基本需求,现在就让我们来实现这一需求:
  第一步,创建一个对话框组件    SplashScreen  

  为满足上述需求,对话框组件需要提供下面两个方法:
  1.显示对话框的方法:

  1. /**
  2. * 打开启动屏
  3. */
  4. public static void show(final Activity activity,final boolean fullScreen) {
  5.     if (activity == null) return;
  6.     mActivity = new WeakReference<Activity>(activity);
  7.     activity.runOnUiThread(new Runnable() {
  8.         @Override
  9.         public void run() {
  10.             if (!activity.isFinishing()) {
  11.                 mSplashDialog = new Dialog(activity,fullScreen? R.style.SplashScreen_Fullscreen:R.style.SplashScreen_SplashTheme);
  12.                 mSplashDialog.setContentView(R.layout.launch_screen);
  13.                 mSplashDialog.setCancelable(false);
  14.                 if (!mSplashDialog.isShowing()) {
  15.                     mSplashDialog.show();
  16.                 }
  17.             }
  18.         }
  19.     });
  20. }
复制代码
为了,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)方法来显示启动屏。  
  1. @Override
  2. protected void onCreate(Bundle savedInstanceState) {
  3.     SplashScreen.show(this,true);
  4.     super.onCreate(savedInstanceState);
  5. }
复制代码
提示:    SplashScreen.show(this,true);放在    super.onCreate(savedInstanceState);之前的位置效果会更好。  
  2.关闭对话框的方法:

  1. /**
  2. * 关闭启动屏
  3. */
  4. public static void hide(Activity activity) {
  5.     if (activity == null) activity = mActivity.get();
  6.     if (activity == null) return;
  7.     activity.runOnUiThread(new Runnable() {
  8.         @Override
  9.         public void run() {
  10.             if (mSplashDialog != null && mSplashDialog.isShowing()) {
  11.                 mSplashDialog.dismiss();
  12.             }
  13.         }
  14.     });
  15. }
复制代码
上述代码中,我们提供了关闭启动屏的方法。那么如何才能让JS模块调用    void hide(Activity activity)来关闭启动屏呢?  
  第二步:向JS模块提供    SplashScreen组件  

  因为我们需要在js中调用    hide方法还控制启动屏的关闭。js不能直接调Java,所有我们需要为他们搭建一个桥梁(    Native Modules)。  
  首先,创建一个    ReactContextBaseJavaModule类型的类,供js调用。  

  1. /**
  2. * SplashScreenModule
  3. * 出自:http://www.cboy.me
  4. * GitHub:https://github.com/crazycodeboy
  5. * Eamil:[email protected]
  6. */
  7. public class SplashScreenModule extends ReactContextBaseJavaModule{
  8.     public SplashScreenModule(ReactApplicationContext reactContext) {
  9.         super(reactContext);
  10.     }
  11.     @Override
  12.     public String getName() {
  13.         return "SplashScreen";
  14.     }
  15.     /**
  16.      * 打开启动屏
  17.      */
  18.     @ReactMethod
  19.     public void show() {
  20.         SplashScreen.show(getCurrentActivity());
  21.     }
  22.     /**
  23.      * 关闭启动屏
  24.      */
  25.     @ReactMethod
  26.     public void hide() {
  27.         SplashScreen.hide(getCurrentActivity());
  28.     }
  29. }
复制代码
其次,创建一个    ReactPackage类型的类,用于向React Native注册我们的    SplashScreenModule组件。  

  1. /**
  2. * SplashScreenReactPackage
  3. * 出自:http://www.cboy.me
  4. * GitHub:https://github.com/crazycodeboy
  5. * Eamil:[email protected]
  6. */
  7. public class SplashScreenReactPackage implements ReactPackage {
  8.     @Override
  9.     public List<Class<? extends JavaScriptModule>> createJSModules() {
  10.         return Collections.emptyList();
  11.     }
  12.     @Override
  13.     public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
  14.         return Collections.emptyList();
  15.     }
  16.     @Override
  17.     public List<NativeModule> createNativeModules(
  18.             ReactApplicationContext reactContext) {
  19.         List<NativeModule> modules = new ArrayList<>();
  20.         modules.add(new SplashScreenModule(reactContext));
  21.         return modules;
  22.     }
  23. }
复制代码
再次,在MainApplication中注册    SplashScreenModule组件。  

  1. @Override
  2. protected List<ReactPackage> getPackages() {
  3.     return Arrays.<ReactPackage>asList(
  4.             new MainReactPackage(),
  5.             new SplashScreenReactPackage()
  6.     );
  7. }
复制代码
准备工作,做好之后,下面我们就可以在JS中调用    hide方法来关闭启动屏了。  
  第三步:在JS模块中控制启动屏的关闭

  创建一个名为    [SplashScreen](https://github.com/crazycodeboy/react-native-splash-screen/blob/master/index.js)的文件,加入下面代码。  
  1. /**
  2. * SplashScreen
  3. * 启动屏
  4. * 出自:http://www.cboy.me
  5. * GitHub:https://github.com/crazycodeboy
  6. * Eamil:[email protected]
  7. * @flow
  8. */
  9. 'use strict';
  10. import { NativeModules } from 'react-native';
  11. module.exports = NativeModules.SplashScreen;
复制代码
然后,我们可以在js中调用SplashScreen的hide()方法来关闭启动屏了。
  1. componentDidMount() {
  2.     SplashScreen.hide();
  3. }
复制代码
不要忘记在使用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文件中添加如下代码:
  1. //
  2. //  SplashScreen.h
  3. //  SplashScreen
  4. //  出自:http://www.cboy.me
  5. //  GitHub:https://github.com/crazycodeboy
  6. //  Eamil:[email protected]
  7. #import "RCTBridgeModule.h"
  8. @interface SplashScreen : NSObject<RCTBridgeModule>
  9. + (void)show;
  10. @end
复制代码
在SplashScreen.m中添加如下代码:
  1. //  SplashScreen
  2. //  出自:http://www.cboy.me
  3. //  GitHub:https://github.com/crazycodeboy
  4. //  Eamil:[email protected]
  5. #import "SplashScreen.h"
  6. static bool waiting = true;
  7. @implementation SplashScreen
  8. - (dispatch_queue_t)methodQueue{
  9.     return dispatch_get_main_queue();
  10. }
  11. RCT_EXPORT_MODULE()
  12. + (void)show {
  13.     while (waiting) {
  14.         NSDate* later = [NSDate dateWithTimeIntervalSinceNow:0.1];
  15.         [[NSRunLoop mainRunLoop] runUntilDate:later];
  16.     }
  17. }
  18. RCT_EXPORT_METHOD(hide) {
  19.     dispatch_async(dispatch_get_main_queue(),
  20.                    ^{
  21.                        waiting = false;
  22.                    });
  23. }
  24. @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上,小伙伴们可以下载安装。  
  最后

  既然来了,留下个喜欢再走吧,鼓励我继续创作(^_^)∠※

  如果喜欢我的文章,那就关注我的博客吧,让我们一起做朋友~~  

  戳这里,加关注哦:

      微博:第一时间获取推送        个人博客:干货文章都在这里哦         
            GitHub:我的开源项目
友荐云推荐




上一篇:堡垒跳板机实现——架构实现
下一篇:深入探索C++对象模式读书笔记
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

浮夸只因我害怕 发表于 2016-10-1 20:19:46
坐等楼主现身!
回复 支持 反对

使用道具 举报

pumperic 发表于 2016-10-1 20:43:47
拿分 路过
回复 支持 反对

使用道具 举报

gzjxbs1r 发表于 2016-10-2 07:55:30
作为一个曾经充分理解怎么吃也不胖的瘦子,如今我总算完全的体会了一吃就胖的感悟。
回复 支持 反对

使用道具 举报

寇鲜 发表于 2016-10-14 07:20:09
螢窗夜話是天才,坚定完毕
回复 支持 反对

使用道具 举报

一生有你 发表于 2016-11-9 10:33:59
传说中的沙发???哇卡卡
回复 支持 反对

使用道具 举报

lyp8 发表于 2016-11-14 23:59:07
最近病院在打折!?
回复 支持 反对

使用道具 举报

spdkeie0r0du 发表于 2016-11-20 17:51:45
有些的时候,正是为了爱才悄悄躲开.躲开的是身影,躲不开的却是那份默默的情怀。
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表