Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> React Native Android的啟動白屏/閃屏的原因,解決方案,原理,使用方法

React Native Android的啟動白屏/閃屏的原因,解決方案,原理,使用方法

編輯:關於Android編程

React Native 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 Android添加啟動屏,我們需要給React Native動刀了了。下面就讓我們從源碼看起。

原理分析

通過react-native init 初始化的應用,Android部分,只有一個MainActivity,它是整個Android程序的入口。

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的適當位(一般是程序初始化工作完成後)置調用上述公共接口關閉啟動屏。

具體實現

第一步:APP啟動的時候控制ReactActivity顯示啟動屏

在給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 List getPackages() {
    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)方法將主題設置成你想要的主題即可。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved