Search code examples
androidasynchronousandroid-fragmentsandroid-asynctaskandroid-tabhost

FragmentTabHost and Asynchronous Tasks


The Problem:

I am trying to create two tabs containing Fragments with a FragmentTabHost.

Here is where I add the tabs to the FragmentTabHost (the onCreate method of the FragmentActivity that contains the tabs:

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_financials);

    mTabHost = (FragmentTabHost) findViewById(R.id.tabhost);
    mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);

    mTabHost.addTab(
            mTabHost.newTabSpec(TAG_ONE).setIndicator("Tab #1",
                    getResources().getDrawable(android.R.drawable.star_on)),
                    MyFragmentOne.class, null);
    mTabHost.addTab(
            mTabHost.newTabSpec(TAG_TWO).setIndicator("Tab #2",
                    getResources().getDrawable(android.R.drawable.star_on)),
                    MyFragmentTwo.class, null);


    if(savedInstanceState == null) {
        new MyAsyncTask(MyClass.this).execute();    
    }
    else {
        mTabHost.setCurrentTabByTag(savedInstanceState.getString("TAB_TAG"));
    }

}

However, the problem is that both MyFragmentOne and MyFragmentTwo are waiting for data from MyAsyncTask. That is, the views in each fragment can only be created properly after MyAsyncTask has completed running.

So I end up getting a NullPointerException when I add the tabs because the required data hasn't been fetched by the AsyncTask yet.

My Solution Attempt:

I tried calling TabHost#addTab() in the onPostExecute() of MyAsyncTask so that the Fragments are only created after MyAsyncTask completes, thus avoiding any null pointers.

However, by moving the addTab() calls to onPostExecute() of the AsyncTask, I get the error: No tab tag for null. Logcat below.

Logcat:

08-06 06:15:17.947: E/AndroidRuntime(1968): java.lang.IllegalStateException: No tab known for tag null
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.support.v4.app.FragmentTabHost.doTabChanged(FragmentTabHost.java:330)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.support.v4.app.FragmentTabHost.onAttachedToWindow(FragmentTabHost.java:280)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.View.dispatchAttachedToWindow(View.java:12585)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2458)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2465)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2465)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2465)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2465)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:2465)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1217)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.Choreographer.doCallbacks(Choreographer.java:574)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.Choreographer.doFrame(Choreographer.java:544)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.os.Handler.handleCallback(Handler.java:733)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.os.Handler.dispatchMessage(Handler.java:95)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.os.Looper.loop(Looper.java:136)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at android.app.ActivityThread.main(ActivityThread.java:5017)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at java.lang.reflect.Method.invokeNative(Native Method)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at java.lang.reflect.Method.invoke(Method.java:515)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
08-06 06:15:17.947: E/AndroidRuntime(1968):     at dalvik.system.NativeStart.main(Native Method)

So how can I solve this problem?


Solution

  • I ended up using a blank PlaceHolderFragment and removing them programmatically in onPostExecute() like so:

    mTabHost.addTab(
                mTabHost.newTabSpec("Placeholder one").setIndicator("Tab #1",
                        getResources().getDrawable(android.R.drawable.star_on)),
                        PlaceHolderFragment.class, null);
    mTabHost.addTab(
                mTabHost.newTabSpec("Placeholder two").setIndicator("Tab #2",
                        getResources().getDrawable(android.R.drawable.star_on)),
                        PlaceHolderFragment.class, null);
    

    Then in onPostExecute() :

    mTabHost.setCurrentTab(0);
    mTabHost.clearAllTabs();
    
    mTabHost.addTab(
            mTabHost.newTabSpec(TAG_ONE).setIndicator("Tab #1",
                    getResources().getDrawable(android.R.drawable.star_on)),
                    MyFragmentOne.class, null);
    mTabHost.addTab(
            mTabHost.newTabSpec(TAG_TWO).setIndicator("Tab #2",
                    getResources().getDrawable(android.R.drawable.star_on)),
                    MyFragmentTwo.class, null);