0

I am implementing a splash screen that is visible until the main Activity of my app manages to render a PDF. When I am opening the app, by clicking on the launcher icon, I can see the splash screen normally.

When I try to open a PDF with the app, there is a delay until MainActivity's onCreate() completes, and then MainActivity is displayed directly (without presenting the splash image first). However, I have noticed that the SplashActivity's onCreate() method is executed.

My code so far:

The Splash Activity:

public class SplashActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        Uri uri = intent.getData();

        Intent intentForActivity = new Intent(this, MainActivity.class);

        if (uri != null)
            intentForActivity.putExtra("URI", uri.toString());

        startActivity(intentForActivity);
        finish();
    }
}

The Activity that I am waiting for (MainActivity):

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main);

        // some dummy delay for this example, so that the Splash Screen
        // is displayed until this Activity is ready
        // (this is NOT the actual code of my application!)
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The relevant part of the Manifest file:

<activity
    android:name=".SplashActivity"
    android:theme="@style/SplashTheme">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter tools:ignore="AppLinkUrlError">
        <action android:name="android.intent.action.VIEW" />
        <action android:name="android.intent.action.EDIT" />
        <action android:name="android.intent.action.PICK" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:mimeType="application/pdf" />
        <data android:pathPattern="*\\.pdf" />
    </intent-filter>
</activity>
    <activity
        android:name=".MainActivity"
        android:theme="@style/AppTheme.NoActionBar" />

The problem seems to be in the intent-filter for handling PDF files, but I cannot figure what is wrong and why.

UPDATE: Adding android:launchMode="singleInstance" in the configuration of SplashActivity in the Manifest solves the issue, but creates many other issues for me.

Iakovos
  • 1,842
  • 4
  • 25
  • 30

2 Answers2

0

This makes no sense. According to the documentation if you call finish() inside onCreate(), none of the remaining lifecycle methods will be called. In your code, you are calling finish() inside SplashActivity.onCreate() so your splash screen should never be shown.

David Wasser
  • 93,459
  • 16
  • 209
  • 274
  • This is not true. It will just not execute `onStart()` etc., and go straight to `onDestroy()`, *after* `onCreate()` (according to the documentation). Also, if I launch the app from the launcher, the splash screen works perfectly. It is when I launch it through some intent from another application that it fails to show (e.g., when I try to open a PDF file with my app). – Iakovos Jan 15 '20 at 14:58
  • This is also interesting: https://android.jlelse.eu/right-way-to-create-splash-screen-on-android-e7f1709ba154 – Iakovos Jan 15 '20 at 14:58
  • You are doing something strange that doesn't really prove your point. If you have the main thread sleep for 5 seconds in `MainActivity.onCreate()`, you are basically stalling all UI updates in that OS process (because you've blocked the UI thread). This isn't normal and will cause your app to be unresponsive and could cause an ANR. I don't think this is a good test for simulating a proper splash screen behaviour. This isn't the way you code a splash screen anyway. – David Wasser Jan 15 '20 at 17:11
  • The link you provided is a really bad way to show a splash screen. I wouldn't ever do it this way. – David Wasser Jan 15 '20 at 17:14
  • You mean the 5 seconds sleep that I provided? This is just to emulate the second Activity performing some task (I have a comment as well that explains that this is just some dummy delay for the purposes of this example). It is not my actual code. It is just there so that the Splash Screen comes up for a while, before jumping to the MainActivity, so that people can see what the issue is: when launching the app through the launcher the splash shows for 5sec, while by opening a PDF file, the phone freezes for 5sec, not showing the Splash, and jumps straight to the MainActivity. – Iakovos Jan 15 '20 at 21:35
  • 1
    The situations are different. When you launch the app, it runs in its own task. When you open a PDF with the app, it is (probably) being launched into the same task as the app the launched it. This may be why the behaviour is different. – David Wasser Jan 16 '20 at 09:08
  • I see, this makes sense. What is troubling me is that if I add a Runnable and inside that I add a sleep for a few ms before starting the next activity, it works fine. However, this is not a good solution, it is just adding an unnecessary delay. Also, if I set the SplashActivity to be singleInstance, it works as is, however this creates many other issues. – Iakovos Jan 16 '20 at 10:06
  • This isn't the correct way to do it. You need to show your splash screen and **in parallel, in a background Thread** do your processing (in your case, PDF decoding). – David Wasser Jan 16 '20 at 11:27
  • The reason the `Runnable` version works is because you are not blocking the main (UI) thread, as I told you. You cannot block the main (UI) thread by sleeping. This is unacceptable. – David Wasser Jan 16 '20 at 11:29
  • I do not see how I may be blocking the main thread inside the SplashActivity in the code that I provided. Does `startActivity()` block the main thread, if the next activity does something in the main thread? If so, why does it happen in the example code above only when I open a PDF with the app, and not when launching from the launcher, since in both cases MainActivity does the exact same thing (sleep for 5sec)? – Iakovos Jan 16 '20 at 11:47
  • sleeping in `onCreate()` blocks the main thread. So it all depends on exactly when `MainActivity.onCreate()` is called. – David Wasser Jan 16 '20 at 14:10
  • Yes, I know that, however MainActivity is a dummy activity in my case, which, theoretically, will be rendered after its `onCreate()` is complete. This seems to be happening in the case of launching the app from the launcher at least. – Iakovos Jan 16 '20 at 14:14
  • You are completely missing my point. You are blocking the main (UI) thread with your dummy `Activity`. This does not represent the real behaviour of your rendering `Activity`. If your rendering `Activity` actually blocked the main (UI) thread for 5 seconds this would be very bad. Your rendering `Activity` should probably decode the PDF in a background thread and then render the results. When you block the main (UI) thread, **nothing else can happen, including UI updates from other activities!**. This is why I'm telling you that your "test scenario" is wrong. – David Wasser Jan 16 '20 at 14:27
  • Obviously, in my original code I do not have a sleep in the main thread. The point is that even in the case of sleeping, the problem is reproducible! It seems like the SplashScreen theme is applied when launching from a launcher, while this is not the case when it is launched by opening a PDF. – Iakovos Jan 16 '20 at 14:59
  • That's eawsy to test. comment ouf the code in `SplashActivity` that starts `MainActivity` and see what happens in both cases (launch from HOME screen vs. open a PDF). – David Wasser Jan 16 '20 at 15:05
  • I have already done that. The exact same code of `SplashActivity` runs in both cases, however the theme is not applied when launching from a PDF. As if the theme is not applied, unless I add some delay before `finish()` runs (in a `Runnable`). – Iakovos Jan 16 '20 at 15:15
  • No, I'm suggesting that to test your hypothesis, you alter `SplashActivity` so that it does NOT finish, and it does NOT launch `MainActivity`. I think you will see that the behaviour is identical in both cases. – David Wasser Jan 16 '20 at 15:38
  • Yes, if I do not finish the Activity, the behaviour is the same. Actually I think that earlier you were right: if the app runs in its own activity, it works fine. If I manage to not start it in the task of the file manager, it works. I tried `documentLaunchMode="always"` and I can see the Splash. However, this is not the right way to start it as a separate task. I will look more into it. – Iakovos Jan 16 '20 at 15:48
  • So, I set `SplashActivity` to be a `singleTask` in the Manifest. That seems to work, however, every time I open a PDF, it creates a new instance of the `MainActivity` in the back stack. So, when I hit back, it returns to the previous instances of `MainActivity`. To solve this, I pass the following flags in the intent that starts the `MainActivity` from the `SplashActivity`: `Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK`. I know that this might cause side effects, so I am testing it. – Iakovos Jan 16 '20 at 16:13
  • Thank you @david for your input. I have posted a solution that works for me. I am not sure it is the best way to solve the issue, but does the job for me. – Iakovos Jan 17 '20 at 12:20
  • Glad you were able to solve the problem. You should accept one of the answers to get the question off the list of unanswered questions. – David Wasser Jan 17 '20 at 12:44
0

I managed to solve the issue by doing the following:

As David Wasser correctly mentioned, this occurs when my app is launched in the task of the app that launches it (i.e., when I open a PDF from within a file manager).

To solve this, in the Manifest, I added this to the configuration of the SplashActivity: android:launchMode="singleTask".

Then, I noticed that this way, multiple instances of the MainActivity would initiate. Hitting the back button would navigate me between them. To solve this, in the SplashActivity class, before I called startActivity() I added: intentForActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); (documentation: FLAG_ACTIVITY_CLEAR_TASK).

A problem that I noticed is that the onDestroy() method of the old MainActivity sometimes executes after the onCreate() of the new one. So, you should be careful when and what you modify/create/delete/etc in your onDestroy() method.

There might be better ways to achieve this result, but this worked well for me.

My updated relevant part of the Manifest:

<activity
    android:name=".SplashActivity"
    android:launchMode="singleTask"
    android:theme="@style/SplashTheme">

My updated SplashActivity:

public class SplashActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Intent intent = getIntent();
        final Uri uri = intent.getData();

        Intent intentForActivity = new Intent(this, MainActivity.class);
        if (uri != null)
            intentForActivity.putExtra("URI", uri.toString());

        intentForActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
        startActivity(intentForActivity);
        this.finish();
    }
}
Iakovos
  • 1,842
  • 4
  • 25
  • 30