Android - zapobiegaj wyświetlaniu białego ekranu podczas uruchamiania

110

Jak wszyscy wiemy, wiele aplikacji na Androida wyświetla bardzo krótko biały ekran, zanim pojawi się ich pierwszy Activity. Ten problem jest obserwowany w następujących przypadkach:

  • Aplikacje na Androida, które rozszerzają Applicationklasę globalną i wykonują w nich główne inicjalizacje. Application Przedmiot jest zawsze tworzona przed pierwszą Activity(fakt, że można zaobserwować w debuggera), więc ma to sens. To jest przyczyna opóźnienia w mojej sprawie.

  • Aplikacje na Androida, które wyświetlają domyślne okno podglądu przed ekranem powitalnym.

Ustawienie android:windowDisablePreview = "true"oczywiście nie działa tutaj. Nie mogę też ustawić motywu nadrzędnego ekranu powitalnego Theme.Holo.NoActionBarzgodnie z opisem tutaj , ponieważ [niestety] mój ekran powitalny korzysta z pliku ActionBar.

Tymczasem aplikacje, które nie rozszerzają Applicationklasy , nie wyświetlają białego ekranu podczas uruchamiania.

Chodzi o to, że idealnie inicjalizacje wykonywane w Applicationobiekcie muszą nastąpić przedActivity pokazaniem pierwszej . Moje pytanie brzmi więc, jak mogę wykonać te inicjalizacje podczas uruchamiania aplikacji bez użycia Applicationobiektu? Prawdopodobnie używając Threadlub Service, jak przypuszczam?

To ciekawy problem do przemyślenia. Nie mogę tego ominąć w zwykły sposób (ustawiając NoActionBarmotyw), ponieważ tragicznie mój ekran powitalny ma w rzeczywistości ActionBarjakieś niepowiązane przyczyny.

Uwaga:

Odniosłem się już do następujących pytań:

Bibliografia:

YS
źródło
1
Sam znalazłeś problem, wykonujesz wiele initów w kontekście aplikacji, blokując ładowanie działania, spróbuj zsynchronizować to, pozwalając, aby działanie ładowania pokazywało się aż do zakończenia wątku.
AxelH
To może pomóc
Max
1
Idealnie byłoby, gdyby aplikacja odciążała przetwarzanie i nie używała głównego wątku do długich operacji. To jest dobrze przyjęta praktyka. Jeśli operacje muszą zostać wykonane przed załadowaniem aplikacji, przynajmniej nie powinna udostępniać wątku interfejsowi użytkownika.
Beshoy Hanna
1
Może się okazać, że po przeniesieniu całego kodu inicjalizacji z Applicationklasy nadal jest to problem . Jest to spowodowane sposobem „zimnego uruchamiania” aplikacji w nowszych wersjach Androida. Google faktycznie zajął się terminami uruchomienia Google I / O w tym roku i zostanie to naprawione w N z tego, co pamiętam. W międzyczasie powinieneś przyjrzeć się temu, co Google nazywa „markowym ekranem startowym”. Oto przykład jak go stworzyć: antonioleiva.com/branded-launch-screen - koniec z białym ekranem na początku ;-) I proszę, nie używaj splashscreenów - to denerwuje użytkownika.
Darwind,
1
Wrt, sztuczka polega na tym, aby nie ustawić motywu NoActionBar, lecz dostosować motyw początkowego działania, aby pusty ekran z motywem wyglądał jak w pełni zainicjowany.
zapl

Odpowiedzi:

86

Problem z białym tłem jest spowodowany zimnym startem Androida podczas ładowania aplikacji do pamięci i można go uniknąć w ten sposób:

public class OnboardingWithCenterAnimationActivity extends AppCompatActivity {
public static final int STARTUP_DELAY = 300;
public static final int ANIM_ITEM_DURATION = 1000;
public static final int ITEM_DELAY = 300;

private boolean animationStarted = false;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    setTheme(R.style.AppTheme);
    getWindow().getDecorView().setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_onboarding_center);
}

@Override
public void onWindowFocusChanged(boolean hasFocus) {

    if (!hasFocus || animationStarted) {
        return;
    }

    animate();

    super.onWindowFocusChanged(hasFocus);
}

private void animate() {
    ImageView logoImageView = (ImageView) findViewById(R.id.img_logo);
    ViewGroup container = (ViewGroup) findViewById(R.id.container);

    ViewCompat.animate(logoImageView)
        .translationY(-250)
        .setStartDelay(STARTUP_DELAY)
        .setDuration(ANIM_ITEM_DURATION).setInterpolator(
            new DecelerateInterpolator(1.2f)).start();

    for (int i = 0; i < container.getChildCount(); i++) {
        View v = container.getChildAt(i);
        ViewPropertyAnimatorCompat viewAnimator;

        if (!(v instanceof Button)) {
            viewAnimator = ViewCompat.animate(v)
                    .translationY(50).alpha(1)
                    .setStartDelay((ITEM_DELAY * i) + 500)
                    .setDuration(1000);
        } else {
            viewAnimator = ViewCompat.animate(v)
                    .scaleY(1).scaleX(1)
                    .setStartDelay((ITEM_DELAY * i) + 500)
                    .setDuration(500);
        }

        viewAnimator.setInterpolator(new DecelerateInterpolator()).start();
    }
}
}

układ

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?colorPrimary"
android:orientation="vertical"
>

<LinearLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingTop="144dp"
    tools:ignore="HardcodedText"
    >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="16dp"
        android:alpha="0"
        android:text="Hello world"         android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
        android:textColor="@android:color/white"
        android:textSize="22sp"
        tools:alpha="1"
        />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="8dp"
        android:alpha="0"
        android:gravity="center"
        android:text="This a nice text"
      android:textAppearance="@style/TextAppearance.AppCompat.Widget.ActionBar.Subtitle.Inverse"
        android:textSize="20sp"
        tools:alpha="1"
        />

    <Button
        android:id="@+id/btn_choice1"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:scaleX="0"
        android:scaleY="0"
        android:text="A nice choice"
        android:theme="@style/Button"
        />

    <Button
        android:id="@+id/btn_choice2"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:scaleX="0"
        android:scaleY="0"
        android:text="Far better!"
        android:theme="@style/Button"
        />

</LinearLayout>

<ImageView
    android:id="@+id/img_logo"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:src="@drawable/img_face"
    tools:visibility="gone"
    />
</FrameLayout>

img face

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
        android:opacity="opaque">

<item android:drawable="?colorPrimary"/>
<item>
    <bitmap
        android:gravity="center"
        android:src="@drawable/img_face"/>
</item>

Dodaj ten motyw do swojego ekranu powitalnego w manifeście

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:windowBackground">@null</item>
</style>

<style name="AppTheme.CenterAnimation">
    <item name="android:windowBackground">@drawable/ll_face_logo</item>
</style>

co da taki efekt

zapracowany kot

Więcej informacji i rozwiązań znajdziesz w tym BlogPost

Ivan Milisavljevic
źródło
3
to nie pomogło w dalszym ciągu białego ekranu i animacji na końcu
Mehvish Ali
To jest prosta implementacja. Problem może powodować inne części kodu. Otwórz kolejne pytanie, a będę tam, aby Ci pomóc :)
Ivan Milisavljevic
1
Rozwiązałem ten problem, animując między motywami i zmieniając motyw bez możliwości rysowania, ale tylko z tym samym kolorem tyłu, a następnie onWindowFocusChanged () sprawił, że zawartość była widoczna i animowałem ją również w inny sposób, także między przejściem. animacja motywów bardzo pomogła
Mehvish Ali
94

dodaj tę linię do motywu aplikacji

<item name="android:windowDisablePreview">true</item>

więcej informacji: https://developer.android.com/topic/performance/vitals/launch-time#themed

Hitesh Singh
źródło
25
Zawiesza aplikację na 2 sekundy, a następnie uruchamia aplikację, nie jest to dla mnie przydatne!
Faakhir
4
ruszt teraz nie pokazuje koloru #ffffff, ale teraz pokazuje # 000000
Midhilaj
@Faakhir Więc znalazłeś jakieś rozwiązanie? Wciąż szukam rozwiązania, które usunie ten biały ekran, a także nie ma opóźnienia w uruchomieniu.
Rupam Das
33

Skopiuj i wklej te dwa wiersze w motywie aplikacji manifestu, np. Res / styles / AppTheme. wtedy będzie działać jak urok.

<item name="android:windowDisablePreview">true</item>
<item name="android:windowIsTranslucent">true</item>
prasad reddy
źródło
21

Przede wszystkim, aby usunąć biały ekran, przeczytaj to - https://www.bignerdranch.com/blog/splash-screens-the-right-way/

Ale co ważniejsze, zoptymalizuj początkowe ładowanie i odłóż ciężką pracę do czasu, gdy będziesz mieć czas na jej uruchomienie. Opublikuj tutaj swoją klasę aplikacji, jeśli chcesz, abyśmy się jej przyjrzeli.

Szmul
źródło
20

W odpowiedziach brakuje zalecanego sposobu rozwiązania tego problemu. Więc dodaję tutaj moją odpowiedź. Problem z białym ekranem podczas uruchamiania występuje z powodu początkowego pustego ekranu, który proces systemowy rysuje podczas uruchamiania aplikacji. Typowym sposobem rozwiązania tego problemu jest wyłączenie tego początkowego ekranu poprzez dodanie go do styles.xmlpliku.

<item name="android:windowDisablePreview">true</item>

Ale zgodnie z dokumentacją Androida może to spowodować dłuższy czas uruchamiania. Według Google, zalecanym sposobem uniknięcia tego początkowego białego ekranu jest użycie windowBackgroundatrybutu motywu działania i zapewnienie prostego niestandardowego rysunku dla początkowej czynności.

Lubię to:

Plik układu do rysowania, my_drawable.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
  <!-- The background color, preferably the same as your normal theme -->
  <item android:drawable="@android:color/white"/>
  <!-- Your product logo - 144dp color version of your app icon -->
  <item>
    <bitmap
      android:src="@drawable/product_logo_144dp"
      android:gravity="center"/>
  </item>
</layer-list>

Stwórz nowy styl w swoim styles.xml

<!-- Base application theme. -->
<style name="AppTheme">
    <!-- Customize your theme here. -->               
</style>

<!-- Starting activity theme -->
<style name="AppTheme.Launcher">
    <item name="android:windowBackground">@drawable/my_drawable</item>
</style>

Dodaj ten motyw do swojej początkowej aktywności w pliku manifestu

<activity ...
android:theme="@style/AppTheme.Launcher" />

A jeśli chcesz wrócić do normalnego połączenia tematycznego setTheme(R.style.Apptheme)przed wywołaniem super.onCreate()isetContentView()

public class MainActivity extends AppCompatActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // Make sure this is before calling super.onCreate
    setTheme(R.style.Theme_MyApp);
    super.onCreate(savedInstanceState);
    // ...
  }
}

Jest to zalecany sposób rozwiązania problemu i pochodzi z wzorców Google Material Design .

Sam
źródło
14

Czy próbowałeś ustawić android:windowBackgroundatrybut w motywie działania programu uruchamiającego na kolor lub element do narysowania?

Na przykład to:

<item name="android:windowBackground">@android:color/black</item>

po dodaniu do motywu działania Launchera przy uruchomieniu będzie wyświetlany czarny kolor (zamiast białego). Jest to łatwa sztuczka do ukrycia długiej inicjalizacji, pokazując coś użytkownikom, i działa dobrze, nawet jeśli podklasujesz obiekt Application.

Unikaj używania innych konstrukcji (nawet wątków) do wykonywania długich zadań inicjalizacyjnych, ponieważ możesz nie być w stanie kontrolować cyklu życia takich konstrukcji. Obiekt Application jest właściwym miejscem do wykonywania dokładnie tego typu działań.

gmetal
źródło
14

Dodałem następujące dwie linie do mojego motywu pod styles.xml

    <item name="android:windowDisablePreview">true</item>
    <item name="android:windowBackground">@null</item>

Działał jak urok

Akanshi Srivastava
źródło
10

Miałem ten sam problem, musisz zaktualizować swój styl.

style.xml

<!-- Base application theme. -->
 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">

        <!-- Customize your theme here. -->
        <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowDisablePreview">true</item>
        <item name="android:windowBackground">@null</item>
        <item name="android:windowIsTranslucent">true</item>

 </style>

Twój plik manifestu powinien wyglądać jak poniżej.

<application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme">
     // Other stuff
</application>

Outout:

wprowadź opis obrazu tutaj

Mam nadzieję, że to ci pomoże.

Hiren Patel
źródło
2
To działa świetnie dla mnie w aplikacji NativeActivity OpenGL. Nie jestem pewien, dlaczego nie jest to wyżej w odpowiedziach, ponieważ jest to najbardziej kompletna i celowa odpowiedź. Żadna Java nie wymagała tylko kilku zmian w pliku XML.
Slion
7

W ramach metod wywołania zwrotnego cyklu życia można zadeklarować, jak zachowuje się Twoja aktywność, gdy użytkownik opuszcza i ponownie wchodzi do działania. Pamiętaj, że sposób projektowania Androida ma swój cykl życia dla każdej aplikacji. Jeśli włożysz zbyt duże obciążenie do onCreate()metody (która jest metodą używaną do ładowania plików układu i inicjalizacji wszelkich kontrolek, które masz w niej), biały ekran stanie się bardziej widoczny, ponieważ ładowanie pliku układu będzie trwało dłużej.

Proponuję użyć kilku różnych metod podczas rozpoczynania aktywności. Są to onStart()(wywoływane jako pierwsza rzecz po załadowaniu aplikacji) onActivityCreated()(wywoływane po wyświetleniu układu i przydatne, jeśli przetwarzasz jakiekolwiek dane po rozpoczęciu działania).

Aby ci to ułatwić, poniżej znajduje się oficjalny diagram cyklu życia działania:

wprowadź opis obrazu tutaj

Michele La Ferla
źródło
Dziękuję za odpowiedź, było bardzo interesujące. Uważam jednak, że źle zrozumiałeś moje pytanie. Problem nie jest spowodowany inicjalizacjami w pierwszym Activity, ale w Applicationobiekcie globalnym . I nie wierzę, że mogę tam zastosować taki rozdział obaw, ponieważ w przeciwieństwie do Activitytego ma tylko onCreate()metodę.
YS,
Dlaczego rozszerzasz klasę aplikacji, a nie aktywność?
Michele La Ferla
Okay, więc masz na myśli, że powinienem Applicationcałkowicie porzucić obiekt i przenieść cały kod inicjalizacji do pierwszego Activity...
YS
W ten sposób zawsze tworzyłem swoje aplikacje, jednak jeśli nie chcesz wprowadzać wszystkich tych zmian, inne odpowiedzi mogą pomóc w obejściu problemu za pomocą klasy aplikacji. Na przyszłość zalecam natychmiastowe użycie jednej klasy aktywności, a następnie wielu fragmentów. Mam nadzieję, że to pomoże :)
Michele La Ferla
2

Czy próbowałeś ustawić inicjalizację na onActivityCreated?

ApplicationKlasa wewnętrzna :

 registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                if(activity.getClass().equals(FirstActivity.class) {
                    // try without runOnUiThread if it will not help
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            new InitializatioTask().execute();
                        }
                    });
                }
            }

            @Override
            public void onActivityStarted(Activity activity) {

            }

            @Override
            public void onActivityResumed(Activity activity) {

            }

            @Override
            public void onActivityPaused(Activity activity) {

            }

            @Override
            public void onActivityStopped(Activity activity) {

            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

            }

            @Override
            public void onActivityDestroyed(Activity activity) {

            }
        });
Sergey Shustikov
źródło
2

Jak już wiesz, dlaczego jest tam ten biały ekran, ponieważ jest to spowodowane procesami w tle lub inicjalizacją aplikacji lub dużymi plikami, więc po prostu sprawdź poniższy pomysł na przezwyciężenie tego.

Aby zapobiec wyświetlaniu tego białego ekranu na początku aplikacji, jednym ze sposobów jest ekran powitalny, to po prostu sposób nie ostateczny i musisz go użyć.

Kiedy pokażesz ekran powitalny z pliku splash.xml, to również ten problem pozostanie taki sam,

Więc musisz stworzyć styl ont w pliku style.xml dla ekranu powitalnego i tam musisz ustawić tło okna jako obraz powitalny, a następnie zastosować ten motyw do swojej aktywności powitalnej z pliku manifestu. Więc teraz, kiedy uruchomisz aplikację, najpierw ustawi ona motyw, dzięki czemu użytkownik będzie mógł bezpośrednio zobaczyć obraz powitalny zamiast białego ekranu.

Vickyexpert
źródło
2

Obie właściwości działają

    <style name="AppBaseThemeDark" parent="@style/Theme.AppCompat">
            <!--your other properties -->
            <!--<item name="android:windowDisablePreview">true</item>-->
            <item name="android:windowBackground">@null</item>
            <!--your other properties -->
    </style>
Sohail Zahid
źródło
2

Spróbuj tego raz.

1) Utwórz plik do rysowania splash_background.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@color/{your color}" />

    <item>
        <bitmap
            android:layout_width="@dimen/size_250"
            android:layout_height="@dimen/size_100"
            android:gravity="center"
            android:scaleType="fitXY"
            android:src="{your image}"
            android:tint="@color/colorPrimary" />
    </item>

</layer-list>

2) Umieść to w styles.xml

     <style name="SplashTheme" parent="Theme.AppCompat.NoActionBar">
         <item name="android:windowBackground">@drawable/background_splash</item>
     </style>

3) W pliku AndroidMainfest.xml ustaw powyższy motyw na Uruchom aktywność.

       <activity
            android:name=".SplashScreenActivity"
            android:screenOrientation="portrait"
            android:theme="@style/SplashTheme"
            android:windowSoftInputMode="stateVisible|adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
Surendar D.
źródło
0

Po prostu napisz pozycję w values ​​/ styles.xml:

<item name="android:windowBackground">@android:color/black</item>

Na przykład w AppTheme:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="windowNoTitle">true</item>
    <item name="windowActionBar">false</item>
    <item name="android:windowFullscreen">true</item>
    <item name="android:windowContentOverlay">@null</item>

    <item name="android:windowBackground">@android:color/black</item>

    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>
Javier Reinoso
źródło
0
Style :- 
<style name="SplashViewTheme" parent="Theme.AppCompat.NoActionBar">
    <item name="android:windowBackground">@drawable/splash</item>
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
</style>

In Manifest :- 
<activity android:name=".SplashActivity"
        android:theme="@style/SplashViewTheme">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
Kryszna
źródło
0

Każdy, kto ma ten biały ekran podczas debbugowania, powinien mieć świadomość, że jeśli debbugujesz, ładowanie potrwa dłużej. Jeśli utworzysz pakiet APK wydania i zainstalujesz go na telefonie, zauważysz, że ładowanie zajmuje znacznie mniej.

Zatem czas uruchamiania z wersją debbugującą nie jest równy czasowi uruchamiania z wersją wydania.

Mointy
źródło