React-native-navigation: Android stuck on splash screen on startup

Created on 17 May 2018  ·  21Comments  ·  Source: wix/react-native-navigation

Issue Description

When trying to start my app on an Android device, the splash screen shows and then never transitions to the tabs/screen.

Steps to Reproduce / Code Snippets / Screenshots

I followed the setup instructions for Android, it's not the first time I've done so and for some reason the Android app hangs completely on the splash screen. Here are my adjusted native files. I hope one of you can spot some dumb mistake I've made. Note that the entry point is index.js and I've registered the screen(s) and run Nativation.startTabBasedApp/startSingleScreenApp (tried both). If you need to see that let me know. It seems to be down to the native code. I've eliminated all extraneous startup JS and the issue persists.

android/settings.gradle:

rootProject.name = 'MyApp'
include ':react-native-navigation'
project(':react-native-navigation').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-navigation/android/app/')
include ':app'

android/app/build.gradle:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.1"
...

dependencies {
    compile fileTree(dir: "libs", include: ["*.jar"])
    compile "com.android.support:appcompat-v7:23.0.1"
    compile "com.facebook.react:react-native:+"  // From node_modules
    compile project(':react-native-navigation')
}

MainActivity:

package com.ma.android;

import android.view.View;
import android.widget.ImageView;
import com.facebook.react.ReactActivity;
import com.reactnativenavigation.controllers.SplashActivity;

public class MainActivity extends SplashActivity {
  @Override
    public View createSplashLayout() {
        ImageView splash = new ImageView(this);
        splash.setImageResource(R.drawable.screen);
        splash.setScaleType(ImageView.ScaleType.CENTER_CROP);

        return splash;
    }
}

MainApplication:

package com.myApp.android;

import com.facebook.react.ReactPackage;

import java.util.Arrays;
import java.util.List;

import com.reactnativenavigation.NavigationApplication;

public class MainApplication extends NavigationApplication {

  @Override
  public boolean isDebug() {
    // Make sure you are using BuildConfig from your own application
    return BuildConfig.DEBUG;
  }

  protected List<ReactPackage> getPackages() {
    // Add additional packages you require here
    // No need to add RnnPackage and MainReactPackage
    return Arrays.<ReactPackage>asList();
  }

  @Override
  public List<ReactPackage> createAdditionalReactPackages() {
    return getPackages();
  }

  @Override
  public String getJSMainModuleName() {
    return "index";
  }
}

AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myApp.android">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
      <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
        android:windowSoftInputMode="adjustResize">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
      </activity>
      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>

</manifest>

Environment

  • React Native Navigation version: ^1.1.450
  • React Native version: 0.55.3
  • Platform(s) (iOS, Android, or both?): Android
  • Device info (Simulator/Device? OS version? Debug/Release?): Device, 8.1.0 (but have tried others), Debug.
more info needed

Most helpful comment

A few people above mentioned this happening specifically when exiting their app using the back button. I ran into this bug too and did a bit of research to work out what is going on – perhaps this will be helpful:

I was finding that if I closed my app by pressing the home button, my app would reopen correctly when I went back into it. However, if I closed the app by pressing the back button from a top-level activity, when I reopened the app it would hang on the splash screen forever.

Android lifecycles

I did some research and basically: when you exit an app with the back button, the top-level activity on Android gets destroyed but the application (and hence JS thread) doesn't. I think this is standard Android behaviour rather than anything to do with RNN. Here's a list of the three main ways you can close an app, and what happens in each case:

  1. Exit by pressing home button: Activity stopped (ie, paused), JS stays running
  2. Exit by pressing back button: Activity destroyed, JS stays running
  3. Exit by swiping the app away from the switcher: Activity destroyed, JS destroyed

So when you reopen an app after scenario 2, although it feels like the whole app is starting up from scratch, it actually isn't.

The issue

The issue for me was because my main index.js file looked like this:

Navigation.setRoot(opts)

The problem with that is that Navigation.setRoot() is the function that actually triggers the creation of activities. This means reopening the app after scenarios 1 and 3 worked fine for me (in scenario 1 the activity was never destroyed, and in scenario 3 the JS thread got restarted so setRoot got called again), but reopening after scenario 2 made the app get stuck.

So the solution for me was, as previous people have said, to do this instead:

Navigation.events().registerAppLaunchedListener(() => {
  Navigation.setRoot(opts)
})

as registerAppLaunchedListener gets triggered when the app is re-opened after being closed with the back button!

I notice that the docs do specify this as the correct pattern (see here), but I set this app up 2.5 years ago so potentially the API was a bit different for the 1.x version I was on back then or maybe the docs weren't as good as they are now.

Hope that helps clarify things a bit. Interested to know if I've misunderstood anything.

All 21 comments

+1

I solve this by add icon like example. Cheer :)

Hey there, This usually means Navigation.startSingleScreenApp or Navigation.startTabBasedApp weren't called. Did you verify startApp was called?

@guyca It's been hard to verify as I cannot get debug started. I can share with you my calls to do so. I believe it has to be something in the Android specific platform code as it works fine on iOS. That may not be the case but usually if it is a JS error you get a red box or at least can start up debugging. Also I've tested by stripping out as much as possible from the JS code. Regardless, here's the JS side of things. Thank you for your help. I hope this is the appropriate venue for this.

./index.js:

require('./app/index')

./app/index.mjs:

import { AsyncStorage } from 'react-native'
import { Navigation } from 'react-native-navigation'
import I18n from 'react-native-i18n'
import { CachePersistor } from 'apollo-cache-persist'
import registerScreens from './screens'
import { getNavIcons, navIcons } from './resources/fonts'
import locales from './resources/locales'
import { ScreenListener } from './utils'
import appConfig from './config.json'
import client from './apollo'
import cache from './apollo/cache'

const navigatorStyle = {

  navBarHidden: true,
  drawUnderTabBar: false,
  tabBarHidden: false,
  statusBarHidden: false,
  statusBarHideWithNavBar: false,
  screenBackgroundColor: 'white',
  orientation: 'portrait',
  disabledBackGesture: false,
}

const tabsStyle = {
  tabBarHidden: false,
  tabBarButtonColor: 'blue',
  tabBarBackgroundColor: 'white',
  tabBarTranslucent: false,
  tabBarTextFontFamily: 'System',
  tabBarLabelColor: 'blue',
  forceTitlesDisplay: true,
  tabBarSelectedButtonColor: 'blue',
  tabBarSelectedLabelColor: 'blue',
  hideBackButtonTitle: true,
};

(async () => {
  I18n.fallbacks = true
  I18n.translations = locales

  registerScreens(client)

        const persistor = new CachePersistor({
    cache,
    storage: AsyncStorage,
    key: 'shop-persist',
  })

  await persistor.restore()

    await getNavIcons(appConfig.navigationIcons)

    new ScreenListener().register()

  Navigation.startTabBasedApp({
    tabs: [
      {
        label: 'Home',
        screen: 'shop.Home',
        icon: navIcons.home,
        navigatorStyle,
        testID: 'HomeTab',
      },
      {
        label: 'Account',
        screen: 'shop.Account',
        icon: navIcons.person,
        navigatorStyle,
        testID: 'AccountTab',
      },
      {
        label: 'Search',
        screen: 'shop.Search',
        icon: navIcons.search,
        navigatorStyle,
        testID: 'SearchTab',
      },
      {
        label: 'Stores',
        screen: 'shop.Stores',
        icon: navIcons.store,
        navigatorStyle,
        testID: 'StoresTab',
      },
      {
        label: 'Shop Local',
        screen: 'shop.ShopLocal',
        icon: navIcons['location-on'],
        navigatorStyle,
        testID: 'ShopLocalTab',
      },
    ],

    tabsStyle,
    appStyle: Object.assign({}, tabsStyle),
  })
})()

Update: this has nothing to do with the posted code snippets apparently. It is an environmental issue with my machine somehow. A co-worker was able to run the app just fine on the same branch/phone as I and had no issues. This is the command we're using to run our app:

react-native run-android --no-packager && clear & make start

For me what happens is that the splash activity loads - and the main index.js is loaded only when leaving the activity by pressing the home button.
Then returning to the app shows the splash activity for a split second - and the app is moved to background immediately.
Then returning to the app again - shows the app, and isAppLaunched gets resolved.

I put logs in the index.js, to make sure, and I can clearly see that it's not loaded until you leave the app.

We figured out it was the use of the power operator, **. I don't understand exactly why or how, but changing this out with Math.pow fixed it. I can't imagine that's not being transpiled.

I can reproduce this on android:

  1. Start "Debug JS Remotely"
  2. Press back button
  3. Start application

now splash screen is stuck, and the only way to fix this is to clear application data to exit debug mode


Edit:
I could fix it like this:

  1. from stuck splash screen press back button
  2. power button to lock screen
  3. unlock
  4. open application again

note: if you press back button to close stuck splash screen then clear recent application then this method of lock/unlock screen will not work.

I am facing the same issue. When I run my app on android device splash screen got stuck and nothing happens after that. (Working fine on android simulator and iOS device and simulator both.) I have followed the steps as described in the document.
I am using
"react-native": "0.57.3", "react-native-navigation": "^1.1.489"

MainActivity.java

public class MainActivity extends SplashActivity {
    @Override
    public LinearLayout createSplashLayout() {
        LinearLayout splash = new LinearLayout(this);
        Drawable launch_screen_bitmap = ContextCompat.getDrawable(getApplicationContext(),R.drawable.background_splash);
        splash.setBackground(launch_screen_bitmap);
        return splash;
    }
}

AndroidMainfest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.truckerapp">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:icon="@mipmap/ic_launcher"
      android:allowBackup="false"
      android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            android:windowSoftInputMode="adjustResize" >              
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
    </application>
</manifest>

App.js

Navigation.startSingleScreenApp({
  screen: {
    screen: "myApp.WelcomeScreen",
    title: "Welcome"
  }
});

And after pressing the hardware back button, the app force closes and then unable to open it again.
Can anybody help me in this?

@Yousefjb how to clear application data to exit from debug mode?

@adirzoari you have to go to Apps settings in your device and find your app then press clear storage.

I clean storage. still have issue. what can i do?

I also had this issue when there was a runtime exception at index.js
make sure there are no exceptions before startSingleScreenApp or before the first component render

I don't see any startSingleScreenApp on my app. I don't use react-native-navigation of wix.

you don't ?
this is an issue for react-native-navigation, if you use react-navigation then check their issue tracker.

@Yousefjb I found the issue. I removed console.log and it solve my issue.
I did console.log for this and it was hard for the app.

it looks like wrapping Navigation.setRoot
with

Navigation.events().registerAppLaunchedListener(() => {
  Navigation.setRoot({
    root: {
      component: {
        name: "navigation.playground.WelcomeScreen"
      }
    }
  });
});

helps, but I'm not really sure. It still looks like it freezes for a while and then in a 15 sec started.

@alburdette619, can you please describe when we need to use Math.pow? I have the same app freeze on my RN app when back btn is pressed

Hi there, I've encountered the same issue, any solution ?

A few people above mentioned this happening specifically when exiting their app using the back button. I ran into this bug too and did a bit of research to work out what is going on – perhaps this will be helpful:

I was finding that if I closed my app by pressing the home button, my app would reopen correctly when I went back into it. However, if I closed the app by pressing the back button from a top-level activity, when I reopened the app it would hang on the splash screen forever.

Android lifecycles

I did some research and basically: when you exit an app with the back button, the top-level activity on Android gets destroyed but the application (and hence JS thread) doesn't. I think this is standard Android behaviour rather than anything to do with RNN. Here's a list of the three main ways you can close an app, and what happens in each case:

  1. Exit by pressing home button: Activity stopped (ie, paused), JS stays running
  2. Exit by pressing back button: Activity destroyed, JS stays running
  3. Exit by swiping the app away from the switcher: Activity destroyed, JS destroyed

So when you reopen an app after scenario 2, although it feels like the whole app is starting up from scratch, it actually isn't.

The issue

The issue for me was because my main index.js file looked like this:

Navigation.setRoot(opts)

The problem with that is that Navigation.setRoot() is the function that actually triggers the creation of activities. This means reopening the app after scenarios 1 and 3 worked fine for me (in scenario 1 the activity was never destroyed, and in scenario 3 the JS thread got restarted so setRoot got called again), but reopening after scenario 2 made the app get stuck.

So the solution for me was, as previous people have said, to do this instead:

Navigation.events().registerAppLaunchedListener(() => {
  Navigation.setRoot(opts)
})

as registerAppLaunchedListener gets triggered when the app is re-opened after being closed with the back button!

I notice that the docs do specify this as the correct pattern (see here), but I set this app up 2.5 years ago so potentially the API was a bit different for the 1.x version I was on back then or maybe the docs weren't as good as they are now.

Hope that helps clarify things a bit. Interested to know if I've misunderstood anything.

Ahhh, thanks a million for the detailed explanation @bdrobinson! I'd been stuck on this one for months.

In our case, we actually have an abstraction around the Wix Navigation API which exposes the registerAppLaunchedListener as an observable. We'd always been using that before doing setRoot, so I didn't really consider that would be the issue.

But the mistake we made was that our abstraction assumed the event would only ever need to be emitted once! In the second scenario you listed above - that assumption was clearly incorrect. There may indeed be times where JavaScript is already running and yet the app is "starting up" again.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

charlesluo2014 picture charlesluo2014  ·  3Comments

birkir picture birkir  ·  3Comments

EliSadaka picture EliSadaka  ·  3Comments

viper4595 picture viper4595  ·  3Comments

yedidyak picture yedidyak  ·  3Comments