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:


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


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


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>


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 .


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ń.


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


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.


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


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) {

            }
        });

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.


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>

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>

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>

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>

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.