配色: 字号:
React Native启动白屏解决方案
2017-01-11 | 阅:  转:  |  分享 
  
ReactNative启动白屏解决方案



问题描述:



用ReactNative架构的无论是AndroidAPP还是iOSAPP,在启动时都出现白屏现象,时间大概1~3s(根据手机或模拟器的性能不同而不同)。



问题分析:



为什么会产生白屏?



ReactNative应用在启动时会将jsbundle读取到内存中,并完成渲染。这期间由于jsbundle还没有完成装载并渲染,所以界面显示的是白屏。



白屏给人的感觉很不友好,那有没有办法不显示白屏呢?



上文解释了:为什么ReactNative应用会在启动的时候显示一会白屏。既然知道了出现问题的原因,那么离解决问题也不远了。市场上大部分APP在启动的时候都会有个启动屏,启动屏对于用户是比较友好的,一来展示欢迎信息,二来显示一些产品信息或一些广告,启动页对于程序来说,是为程序完成初始化加载数据,做一些初始化工作的所保留的时间,启动屏等待的时间可长可短,具体根据业务而定。



下面我就教大家如何给ReactNative应用添加启动屏,并解决启动白屏的问题。



Android启动白屏解决方案



我们可以通过为ReactNativeAndroid应用添加启动屏的方式,来解决启动白屏的问题。我在《ReactNativeAndroid启动屏,启动白屏,闪现白屏》一文中介绍过一种为ReactNativeAndroid应用添加启动屏的方法,

不过那种方法虽好,但牵扯到对ReactNative源码的修改,如果ReactNative版本有更新还需要对源码做一些处理,所以以后维护起来不是很方便。



下面就向大家介绍另外一种为ReactNativeAndroid应用添加启动屏的方案。



在《ReactNativeAndroid启动屏,启动白屏,闪现白屏》一文中

我们使用的是在根视图容器上添加一个视图作为启动屏,当jsbundle加载并渲染完成后,再将添加的视图从根视图上移除。在根视图上添加一个视图的方式其实就是为了遮挡白屏,既然是遮挡白屏,我们是不是可以弹出一个对话框呢?



小伙伴们肯定会说,对话框也不是全屏啊,主题也不一样啊,不过没关系,既然我们可以添加对话框,那么我们就可以修改对话框的样式来达到我们需要的效果。



要达到启动屏的效果,我们需要一个什么样效果的对话框呢?



在APP启动的时候显示;

在jsbundle加载并渲染完成后消失;

全屏显示;

显示的内容可以通过layoutxml进行修改;

上述是我们对这个对话框的基本需求,现在就让我们来实现这一需求:



第一步,创建一个对话框组件SplashScreen



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



1.显示对话框的方法:



/

打开启动屏

/

publicstaticvoidshow(finalActivityactivity,finalbooleanfullScreen){

if(activity==null)return;

mActivity=newWeakReference(activity);

activity.runOnUiThread(newRunnable(){

@Override

publicvoidrun(){

if(!activity.isFinishing()){



mSplashDialog=newDialog(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能被及时的回收,这里我们通过newWeakReference(activity);创建了一个Activity的弱引用。



另外,因为在Android中所有的有关UI操作都必须在主线程,所有我们通过activity.runOnUiThread(newRunnable()...,将对话框的显示放在了主线程处理。



上述代码中,show的第二个参数fullScreen表示启动屏是全屏显示(即是否隐藏状态栏),代码会控制对话框加载不同的主题样式R.style.SplashScreen_Fullscreen与R.style.SplashScreen_SplashTheme来达到是否隐藏状态的需求。



然后,我们可以在MainActivity.Java的onCreate方法中调voidshow(finalActivityactivity,finalbooleanfullScreen)方法来显示启动屏。



@Override

protectedvoidonCreate(BundlesavedInstanceState){

SplashScreen.show(this,true);

super.onCreate(savedInstanceState);

}



提示:SplashScreen.show(this,true);放在super.onCreate(savedInstanceState);之前的位置效果会更好。

2.关闭对话框的方法:



/

关闭启动屏

/

publicstaticvoidhide(Activityactivity){

if(activity==null)activity=mActivity.get();

if(activity==null)return;



activity.runOnUiThread(newRunnable(){

@Override

publicvoidrun(){

if(mSplashDialog!=null&&mSplashDialog.isShowing()){

mSplashDialog.dismiss();

}

}

});

}



上述代码中,我们提供了关闭启动屏的方法。那么如何才能让JS模块调用voidhide(Activityactivity)来关闭启动屏呢?



第二步:向JS模块提供SplashScreen组件



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



首先,创建一个ReactContextBaseJavaModule类型的类,供js调用。



/

SplashScreenModule

出自:http://www.cboy.me

GitHub:https://github.com/crazycodeboy

Eamil:crazycodeboy@gmail.com

/

publicclassSplashScreenModuleextendsReactContextBaseJavaModule{

publicSplashScreenModule(ReactApplicationContextreactContext){

super(reactContext);

}



@Override

publicStringgetName(){

return"SplashScreen";

}



/

打开启动屏

/

@ReactMethod

publicvoidshow(){

SplashScreen.show(getCurrentActivity());

}



/

关闭启动屏

/

@ReactMethod

publicvoidhide(){

SplashScreen.hide(getCurrentActivity());

}

}



其次,创建一个ReactPackage类型的类,用于向ReactNative注册我们的SplashScreenModule组件。



/

SplashScreenReactPackage

出自:http://www.cboy.me

GitHub:https://github.com/crazycodeboy

Eamil:crazycodeboy@gmail.com

/

publicclassSplashScreenReactPackageimplementsReactPackage{



@Override

publicList>createJSModules(){

returnCollections.emptyList();

}



@Override

publicListcreateViewManagers(ReactApplicationContextreactContext){

returnCollecwww.tt951.comtions.emptyList();

}



@Override

publicListcreateNativeModules(

ReactApplicationContextreactContext){

Listmodules=newArrayList<>();

modules.add(newSplashScreenModule(reactContext));

returnmodules;

}

}



再次,在MainApplication中注册SplashScreenModule组件。



@Override

protectedListgetPackages(){

returnArrays.asList(

newMainReactPackage(),

newSplashScreenReactPackage()

);

}



准备工作,做好之后,下面我们就可以在JS中调用hide方法来关闭启动屏了。



第三步:在JS模块中控制启动屏的关闭



创建一个名为SplashScreen的文件,加入下面代码。



/

SplashScreen

启动屏

出自:http://www.cboy.me

GitHub:https://github.com/crazycodeboy

Eamil:crazycodeboy@gmail.com

@flow

/

''usestrict'';



import{NativeModules}from''react-native'';

module.exports=NativeModules.SplashScreen;



然后,我们可以在js中调用SplashScreen的hide()方法来关闭启动屏了。



componentDidMount(){

SplashScreen.hide();

}



不要忘记在使用SplashScreen的js文件中导入它哦importSplashScreenfrom''./SplashScreen。

iOS启动白屏解决方案



在iOS中,iOS支持为程序设置一个LaunchImage或LaunchScreenFile来作为启动屏,当程序被打开的时候,首先显示的便是设置的这个启动屏了。



那么小伙伴会问了,这个启动屏幕什么时候会消失呢?



在AppDelegate如下方法:



-(BOOL)application:(UIApplication)applicationdidFinishLaunchingWithOptions:(NSDictionary)launchOptions



该方法返回一个BOOL类型的值,当系统调用该方并返回值之后,标志着APP启动加载已经完成,系统会将启动屏给关掉。



所以如果我们控制了这个启动屏幕让它在jsbundle加载并渲染完成之后再关闭不就解决了iOS启动白屏了吗?



上面已经说到,-(BOOL)application:(UIApplication)applicationdidFinishLaunchingWithOptions:(NSDictionary)launchOptions方法执行完成之后,启动屏会被关掉。



所以我们就想办法控制该方实行的时间。



第一步:创建一个名为SplashScreen的Object-C文件



在SplashScreen.h文件中添加如下代码:



//

//SplashScreen.h

//SplashScreen

//出自:http://www.cboy.me

//GitHub:https://github.com/crazycodeboy

//Eamil:crazycodeboy@gmail.com





#import"RCTBridgeModule.h"



@interfaceSplashScreen:NSObject

+(void)show;

@end



在SplashScreen.m中添加如下代码:



//SplashScreen

//出自:http://www.cboy.me

//GitHub:https://github.com/crazycodeboy

//Eamil:crazycodeboy@gmail.com



#import"SplashScreen.h"



staticboolwaiting=true;



@implementationSplashScreen

-(dispatch_queue_t)methodQueue{

returndispatch_get_main_queue();

}

RCT_EXPORT_MODULE()



+(void)show{

while(waiting){

NSDatelater=[NSDatedateWithTimeIntervalSinceNow:0.1];

[[NSRunLoopmainRunwww.baiyuewang.netLoop]runUntilDate:later];

}

}



RCT_EXPORT_METHOD(hide){

dispatch_async(dispatch_get_main_queue(),

^{

waiting=false;

});

}

@end



在上述代码中,我们通过[[NSRunLoopmainRunLoop]runUntilDate:later];来控制-(BOOL)application:(UIApplication)applicationdidFinishLaunchingWithOptions:(NSDictionary)launchOptions方法执行的时间,

主线程会每隔0.1s阻塞一次,直到waiting变量为true,然后我们就可以通过暴露给JS模块的hide方法来控制waiting变量的值,继而达到控制启动屏幕的关闭。



第二步:在JS模块中控制启动屏的关闭



通过第一步我们已经向JS模块暴露了hide方法,然我们就可以在JS模块中通过hide方法来关闭启动屏幕。

由于iOS在JS模块中控制启动屏的关闭的方法和Android中第三步:在JS模块中控制启动屏的关闭的方法是一样的,这里就不再介绍了。



开源库



为了方便大家使用和解决ReactNative应用启动白屏的问题,我已经将上述方案做成ReactNative组件react-native-splash-screen,

开源在了GitHub上,小伙伴们可以下载使用。

献花(0)
+1
(本文系thedust79首藏)