編輯:關於Android編程
用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動刀了了。下面就讓我們從源碼看起。
通過react-native init
public class MainActivity extends ReactActivity { /** * Returns the name of the main component registered from JavaScript. * This is used to schedule rendering of the component. */ @Override protected String getMainComponentName() { return "GitHubPopular"; } }
通過上述代碼可以看出MainActivity很干淨,就一個getMainComponentName()方法。顯然啟動白屏不是因為MainActivity導致的。
接下來,我們就繼續探索,進入ReactActivity源碼一探究竟。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (!Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), getMainComponentName(), getLaunchOptions()); setContentView(mReactRootView); mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); }
上面代碼是ReactActivity的onCreate方法的代碼,onCreate作為一個Activity的入口,負責著程序初始化等一系列工作。
熟悉Android開發的小伙伴都知道,在onCreate方法通過setContentView()方法設置一個用於用戶交互界面。在ReactActivity的onCreate方法中也有使用setContentView()。
mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), getMainComponentName(), getLaunchOptions()); setContentView(mReactRootView);
上述代碼中,首先通過mReactRootView = createRootView();創建一個根視圖,該視圖便是React Native應用的最頂部視圖。然後通過mReactRootView.startReactApplication方法,加載並渲染js bundle,此過程是比較耗時的。最後,通過setContentView(mReactRootView);將根視圖綁定到Activity界面上。
基本原理就是這些,下面我們就對ReactActivity動動刀子。
先說一下思路:
APP啟動的時候控制ReactActivity顯示啟動屏。 提供關閉啟動屏的公共接口。 在js的適當位(一般是程序初始化工作完成後)置調用上述公共接口關閉啟動屏。在給ReactActivity動刀子前我們需要進行一些准備工作。
基礎准備:
首先,我們需要將ReactActivity復制一份出來。
因為ReactActivity是React Native源碼中的一部分,我們無法直接對其源碼進行修改,所以我們需將它復制一份出來。然後將MainActivity繼承改為我們復制出來的這個ReactActivity。
其次。修改getUseDeveloperSupport方法。
因為,ReactNativeHost的getUseDeveloperSupport方法是受保護類型的,所以我們無法在它所屬包之外訪問該方法。但我們又需要在ReactActivity中調用該方法,那麼我們可以使用反射來滿足我們這一需求。
protected boolean getUseDeveloperSupport() { ReactNativeHost rnh=((ReactApplication) getApplication()).getReactNativeHost(); Classcls=rnh.getClass(); Object support= null; try { Method method = cls.getDeclaredMethod("getUseDeveloperSupport", new Class[]{}); method.setAccessible(true); support=method.invoke(rnh); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return (boolean) support; }
前期工作准備玩了,現在讓我們開始吧。
為了讓ReactActivity顯示啟動屏我們需要創建一個View容器,來容納啟動屏視圖和React Native根視圖。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getUseDeveloperSupport() && Build.VERSION.SDK_INT >= 23) { // Get permission to show redbox in dev builds. if (!Settings.canDrawOverlays(this)) { Intent serviceIntent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); startActivity(serviceIntent); FLog.w(ReactConstants.TAG, REDBOX_PERMISSION_MESSAGE); Toast.makeText(this, REDBOX_PERMISSION_MESSAGE, Toast.LENGTH_LONG).show(); } } mRootView = new FrameLayout(this); splashView = LayoutInflater.from(this).inflate(R.layout.launch_screen, null); mReactRootView = createRootView(); mReactRootView.startReactApplication( getReactNativeHost().getReactInstanceManager(), getMainComponentName(), getLaunchOptions()); mRootView.addView(mReactRootView); mRootView.addView(splashView, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); setContentView(mRootView); mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer(); }
首先,我創建了一個mRootView = new FrameLayout(this);視圖容器。
其次,將啟動屏布局文件讀到內存中splashView = LayoutInflater.from(this).inflate(R.layout.launch_screen, null);。
再次,添加mReactRootView與splashView,注意添加順序。
最後,將mRootView綁定到Activity。
這樣一來,我們就控制了ReactActivity在啟動的時候顯示歡迎界面。下面我們需要讓ReactActivity開放關閉換用界面的接口方法。
/** * 隱藏啟動屏幕 */ public void hide() { if (mRootView == null || splashView == null) return; AlphaAnimation fadeOut = new AlphaAnimation(1, 0); fadeOut.setDuration(1000); splashView.startAnimation(fadeOut); fadeOut.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { mRootView.removeView(splashView); splashView = null; } @Override public void onAnimationRepeat(Animation animation) { } }); }
上述方法,中加入了一個淡出動畫持續1s,目的是讓歡迎界面和其他界面之間過度自然些。
做到這裡還不夠,因為我們需要在js中調用hide方法還控制歡迎界面的關閉。js不能直接調Java,所有我們需要為他們搭建一個橋梁(Native Modules)。
首先,創建一個ReactContextBaseJavaModule類型的類,供js調用。
/** * LaunchScreenModule * 出自:http://www.cboy.me * GitHub:https://github.com/crazycodeboy * Eamil:[email protected] */ public class LaunchScreenModule extends ReactContextBaseJavaModule{ public LaunchScreenModule(ReactApplicationContext reactContext) { super(reactContext); } @Override public String getName() { return "LaunchScreen"; } @ReactMethod public void hide(){ getCurrentActivity().runOnUiThread(new Runnable() { @Override public void run() { ((ReactActivity)getCurrentActivity()).hide(); } }); } }
其次,創建一個ReactPackage類型的類,用於向React Native注冊我們的LaunchScreenModule組件。
/** * LaunchScreenReactPackage * 出自:http://www.cboy.me * GitHub:https://github.com/crazycodeboy * Eamil:[email protected] */ public class LaunchScreenReactPackage implements ReactPackage { @Override public List> createJSModules() { return Collections.emptyList(); } @Override public List createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List createNativeModules( ReactApplicationContext reactContext) { List modules = new ArrayList<>(); modules.add(new LaunchScreenModule(reactContext)); return modules; } }
再次,在MainApplication中注冊LaunchScreenModule組件。
@Override protected ListgetPackages() { return Arrays. asList( new MainReactPackage(), new LaunchScreenReactPackage() ); }
最後,在js中調用LaunchScreenModule。
創建一個名為LaunchScreen的文件,加入下面代碼。
/** * LaunchScreen * Android啟動屏 * 出自:http://www.cboy.me * GitHub:https://github.com/crazycodeboy * Eamil:[email protected] * @flow */ 'use strict'; import { NativeModules } from 'react-native'; module.exports = NativeModules.LaunchScreen;
上述代碼,目的是向js暴露LaunchScreen模塊。
下面我們就可以在js中調用LaunchScreen的hide()方法來關閉啟動屏了。
LaunchScreen.hide();
不要忘記在使用LaunchScreen的js文件中導入它哦import LaunchScreen from './LaunchScreen。
到這裡,React Native Android的啟動白屏的原因,解決方案,原理,使用方法已經向大家介紹完了。大家如果還有什麼疑問可以加群:165774887,和我一起討論。
另外,跟大家分享一個Android啟動時閃現白屏或黑屏的解決方案。
這個問題是Android主題的問題和React Native無關,請往下看。
市場上有很多應用,在啟動的時候,會出現閃現黑屏或白屏,有的應用卻沒有。究其原因,是主題在搞鬼。
當單擊應用的圖標時,Android會為被單擊的應用創建一個進程,然後創建一個Application實例,然後應用主題,然後啟動Activity。
因為啟動Activity也是需要時間的,這之間的時間間隔,便是閃現白屏或黑屏的時間。
為解決啟動時閃現白屏或黑屏的問題,我們可以從主題下手,為應用創建一個透明的主題。
第一步:創建一個透明主題。
第二步:在AndroidManifest.xml中為application應用主題。
這樣一來,啟動時變不會閃現黑屏或白屏了。
如果,你的應用需要一個特定的主題,但該主題不是透明的,你可以先將application的默認主題設置成透明的主題,然後在程序啟動後(可以在啟動頁進行),通過public void setTheme(int resid)方法將主題設置成你想要的主題即可。
在activity之間數據傳遞中還有一種比較實用的方式,就是全局對象application Application和Activity,Service一樣是And
新建一個eclipse-android項目後,如test2,從其它項目中拷貝若干個包到test2中,在編譯時總會出現以下錯誤:?主要看第三條:The projec
ATCID主要用來處理PC端傳輸過來的AT命令,從AT命令實際處理的地方來說,主要分為3類: 1. 需要Modem來處理的AT命令; 2. 需
華為榮耀於8月1號下午正式發布了6.6吋大屏手機華為榮耀NOTE8,那麼想要購買新機的朋友是不是很想知道華為榮耀note8怎麼預約購買呢?下面小編就馬上帶來