Unable to pause activity, content view not yet created
Asked Answered
U

1

8

I grabbed the EU4You sample project demonstrated in The Busy Coder's Guide to Android Development 4.2 (courtesy of Mark Murphy aka commonsware here on StackOverflow). It may be easier to follow along with this sample project, but I will try to post relevant portions of code in the question. You do need to import the ActionBarSherlock project as well. You'll aslo need to set the Java compiler to 1.6 (to allow for the @Override annotation).

NOTE: I've changed the min-sdk to 8 (2.3, Froyo) and the target-sdk to 16 (4.1, Ice Cream Sandwich).

Note: I've changed the countries framelayout id to left_pane and the details framelayout id to right_pane.

Main layout file (layout-large-land):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <FrameLayout
    android:id="@+id/left_pane"
    android:layout_weight="30"
    android:layout_width="0px"
    android:layout_height="fill_parent"
  />
  <FrameLayout
    android:id="@+id/right_pane"
    android:layout_weight="70"
    android:layout_width="0px"
    android:layout_height="fill_parent"
  />
</LinearLayout>

Main layout file:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/left_pane"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
/>

The original version of the sample application has two activities (SherlockFragmentActivities to be exact).

  1. EU4You - Loads up the appropriate layout (default or landscape-large). If default, loads a list of countries in a SherlockListFragment. If landscape-large, loads up the list and a details fragment to display the selected country.
  2. DetailsActivity - Loads up the selected country via the DetailsFragment when not using the landscape-large layout.

If you click on a country in the list fragment, EU4You (the main hosting activity) checks for the existence and visibility of the DetailsFragment. If it exists, it loads up the country website inside that fragment. If not, it fires off the DetailsActivity, which then handles adding the DetailsFragment (which then loads the website for the given country).

This works fine, like a typical application supporting multiple screen layouts on tablets and handsets.

My problem is that I want to remove the DetailsActivity and stick to a single activity. I want to swap out (replace) the list fragment with the details fragment whenever I am not in the large landscape layout (in other words, I am either in a portrait layout on a tablet, or on a handset in any orientation). So what I have done, is changed the following in the EU4You SherlockFragmentActivity:

Original (with slight naming modification on the framelayout names mentioned in the note further up... countries is left_pane, details is right_pane):

@Override
public void onCountrySelected(Country c) {
    String url=getString(c.url);

    if (details != null && details.isVisible()) {
        details.loadUrl(url);
    }
    else {
        Intent i=new Intent(this, DetailsActivity.class);

        i.putExtra(DetailsActivity.EXTRA_URL, url);
        startActivity(i);
    }
}

My modified single activity version:

@Override
public void onCountrySelected(Country c) {
    String url=getString(c.url);

    if (details != null && details.isVisible()) {
        details.loadUrl(url);
    } 
    else {
        details = new DetailsFragment();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        ft.replace(R.id.left_pane, details);
        ft.addToBackStack(null);
        ft.commit();
        getSupportFragmentManager().executePendingTransactions();
        details.loadUrl(url);
    }
}

This works as expected. I click on a website on the list (in portrait mode on a tablet, or on a handset in any orientation) and the details show in the details fragment, which replaces the website list fragment. However, if I then click the home button on the device, while the country details are showing, I get a force close error, with the following logcat details (only with the default layout with just the single left_pane framelayout... I don't have this issue in landscape orientation on tablets, when there is a left_pane and right_pane):

11-16 15:04:33.525: E/AndroidRuntime(688): FATAL EXCEPTION: main
11-16 15:04:33.525: E/AndroidRuntime(688): java.lang.RuntimeException: Unable to pause activity {com.commonsware.android.eu4you/com.commonsware.android.eu4you.EU4You}: java.lang.IllegalStateException: Content view not yet created
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3348)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3305)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3288)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread.access$2500(ActivityThread.java:125)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2040)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.os.Handler.dispatchMessage(Handler.java:99)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.os.Looper.loop(Looper.java:123)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread.main(ActivityThread.java:4627)
11-16 15:04:33.525: E/AndroidRuntime(688):  at java.lang.reflect.Method.invokeNative(Native Method)
11-16 15:04:33.525: E/AndroidRuntime(688):  at java.lang.reflect.Method.invoke(Method.java:521)
11-16 15:04:33.525: E/AndroidRuntime(688):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
11-16 15:04:33.525: E/AndroidRuntime(688):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
11-16 15:04:33.525: E/AndroidRuntime(688):  at dalvik.system.NativeStart.main(Native Method)
11-16 15:04:33.525: E/AndroidRuntime(688): Caused by: java.lang.IllegalStateException: Content view not yet created
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.support.v4.app.ListFragment.ensureList(ListFragment.java:328)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.support.v4.app.ListFragment.getListView(ListFragment.java:222)
11-16 15:04:33.525: E/AndroidRuntime(688):  at com.commonsware.android.eu4you.CountriesFragment.onSaveInstanceState(CountriesFragment.java:64)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.support.v4.app.FragmentManagerImpl.saveFragmentBasicState(FragmentManager.java:1574)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1644)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:499)
11-16 15:04:33.525: E/AndroidRuntime(688):  at com.actionbarsherlock.app.SherlockFragmentActivity.onSaveInstanceState(SherlockFragmentActivity.java:127)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.Activity.performSaveInstanceState(Activity.java:1036)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1180)
11-16 15:04:33.525: E/AndroidRuntime(688):  at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3330)
11-16 15:04:33.525: E/AndroidRuntime(688):  ... 12 more

I can stop this error from occurring, if I remove the following line from the code above:

ft.addToBackStack(null);

I do require to be able to add the replace transaction to the backstack, so I can't remove this line (this sample application is the basis for my real application that has more stuff, but I used it to simplify my issue).

So two questions:

  1. Why is this happening?
  2. How can I fix it (perhaps another approach)?

One thing to note is that in my real application, this logic must all fall inside of tabs in an action bar (ActionBarSherlock since I must support back to version 2.3 (Froyo)).


UPDATE (2012-Nov-22 13:27 EST)

I've posted a sample project on GitHub. I'm new to Eclipse and Android development, so I may not have included everything I needed (workspace was not included and I don't think the ActionBarSherlock project is included... it would need to be imported (can be found here)).

Unkenned answered 16/11, 2012 at 21:0 Comment(5)
@commonsware: Just getting your attention Mark... since you know the sample application better than anyone :)Unkenned
Jason, add commonsware tag so that Mark can see itEonism
@MarcinCzech: Done... I didn't realize that tag even existed. Thanks.Unkenned
Not sure why your first comment did not pop up on my radar, unless SO is case sensitive. Also, please paste in stack traces as code, not blockquotes, for easier reading. Also also, the commonsware tag is for questions about my sample code and open source projects, not just general "hey, I'll get that balding guy to comment" stuff -- this question is fine, I'm mentioning this to minimize future tag spam from those seeing the comments here.Yvette
@CommonsWare: Yes it must be case sensitive. "paste in stack traces as code, not blockquotes, for easier reading"... duly noted.Unkenned
Y
5

I cannot comment on your specific crash, as I have not seen that error, other than to surmise that it is tied to replace() rather than using attach()/detach() (just a guess).

However, getSupportFragmentManager().executePendingTransactions(); to me is a code smell. My guess is that you are doing this for the benefit of details.loadUrl(url);. Instead, please add a data member in DetailsFragment, something like urlToBeApplied, and have loadUrl() stash the value there if the WebView does not yet exist. It can then apply the URL in onCreateView(). Whether this will help with respect to your crash, I cannot say.

You may wish to consider posting a complete sample project that demonstrates the issue, as you are counting on people recognizing the error message.


UPDATE

Based on the sample app, I was able to better diagnose the problem.

Content view not yet created, on a ListFragment, will mean that we are trying to call getListView() for a fragment that has not yet gone through onCreateView() or has gone through onDestroyView(). This usually indicates a timing problem.

In this case, as it turns out, the work that was crashing was superfluous, anyway. onSaveInstanceState() is trying to hold onto the checked item position, so we can re-check it later (e.g., after a configuration change). However, we do not even need to be in checked mode in the first place when we are on normal displays. The checked mode is for the activated state, which is relevant when then list and details are side-by-side, but is irrelevant when they are not. So, the workaround is to simply not bother with the onSaveInstanceState() logic if we are not needing a persistent selection... which we can easily get by calling isPersistentSelection() on our listener.

For the sake of argument, though, let's pretend that we did need to do this work. In that case, we have a couple of options. The simplest probably is to override onDestroyView() and cache the checked item position, using that in onSaveInstanceState() if getListView() returns null.

Yvette answered 19/11, 2012 at 13:55 Comment(14)
You are correct that the executePendingTransactions(); call was because of details.loadUrl(url)'. I'll try the data member approach (makes more sense to do it that way anyway). I'll take a look at using attach() and detach() instead and see how things go. If needed, I will post a complete sample project as you suggested. Thanks Mark. I'll keep everyone posted with any progress or new issues.Unkenned
Hey Mark. I'm actually trying to change the EU4You sample from your book (found in the Large-Screen Strategies And Tactics chapter in version 4.3) In the section where you talk about the onCountrySelected method (in the EU4You class), you say In priniple, we could elect to execute a FragmentTransaction to replace the CountriesFragment with the DetailsFragment, but this can get complicated. This is exactly what I'm am trying to accomplish. Do you have any plans to show how do that scenario, rather than fire off a separate activity for the DetailsFragment?Unkenned
...con't from previous comment... Changing the code to what I have in the question above is exactly what throws the error. It is actually caused in the CountriesFragment class in the OnSaveInstanceState method, by the line state.putInt(STATE_CHECKED, getListView().getCheckedItemPosition());Unkenned
@JasonDown: Post your revised project source code somewhere, and I'll try to get the time to peek at it.Yvette
Ok will do. It'll likely be tomorrow before I have a chance to do so. I'll let you know when it is posted. Thanks again!Unkenned
Sample project was posted on GitHub (link can be found in the update for my question).Unkenned
@JasonDown: I will try to take a look at this over the weekend.Yvette
@JasonDown: I am unable to reproduce your error given your code. I run the app in a 4.0.3 tablet emulator in landscape. I click on a country, bringing up the details. I press HOME. Nothing out of the ordinary happens. Can you provide me with a specific sequence of steps where you are seeing this problem?Yvette
It is only an issue in portrait mode or when using a phone emulator. The large landscape works fine since it is not replacing the list fragment with the details fragment.Unkenned
@JasonDown: Ah, OK. You do not need to be saving getListView().getCheckedItemPosition() in normal-sized screens, anyway -- that only is useful if the fragments are on the screen at the same time. Wrapping that putInt() call in a check of listener.isPersistentSelection() (and ensuring that isPersistentSelection() is still accurate given your other changes) will probably solve this.Yvette
Ok I'll make a few adjustments and see how it goes. Thanks for taking a look.Unkenned
So I made two changes based on your comments and this fixed the error: I changed the onSaveInstanceState method to call putInt() (with the getListView().getCheckedItemPosition() call in it) only under the condition that listener.isPersistentSelection() was true. I then changed the EU4You class' implementation of isPersistentSelection() to check if both the countries fragment and the details fragment are not null and are visible. If so, we are showing both. I now have a new issue on configuration changes, but that is unrelated to this stackoverflow question.Unkenned
Mark, if you want to edit your answer to reflect the conclusion reached from the comments, I'll check this off as the correct answer. If you'd rather not, I can just create a separate answer (with credit to you for the help of course). The choice is your's :)Unkenned
"Mark"ed correct (pun intended... sorry, it's been a long day ;)). Thanks again. I'm fairly new to Android development and this exchange has definitely enlightened me on a few topics.Unkenned

© 2022 - 2024 — McMap. All rights reserved.