0

I have a foreground service that checks stock quotes every 5 minutes. This service is passed a Messenger from the parent FragmentActivity which starts it.

The stock quote checking service passes a Message back to this Handler which is an inner non-static class of the parent FragmentActivity:

private class YahooServiceHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {

            case App.QUOTE_UPDATE_FINISHED:

                Bundle bundle = msg.getData();
                mTickerQuoteIds = bundle.getStringArray("ticker_quote_ids");

                // I need this class to be an inner non-static class to do
                // references to the other fragments this activity hosts.  

                QuoteListFragment quoteListFragment = (QuoteListFragment)mAdapter
                        .getRegisteredFragment(1);

                quoteListFragment.mLoader.onContentChanged();

                // This loops through some Ids returned in the Message to show
                // dialogs for quotes that have passed their price limits

                if (mTickerQuoteIds != null && mTickerQuoteIds.length > 0) {

                    for (int i = 0; i < mTickerQuoteIds.length; i++) {

                        String[] tickerQuoteId = mTickerQuoteIds[i].split("\\|");

                        FragmentManager fm = getSupportFragmentManager();
                        Bundle bundleArgs = new Bundle();
                        bundleArgs.putString("quote_id", tickerQuoteId[1]);
                        bundleArgs.putString("ticker_id", tickerQuoteId[0]);
                        TickerDialog tickerAlertDialog = new TickerDialog();
                        tickerAlertDialog.setArguments(bundleArgs);
                        tickerAlertDialog.show(fm, "fragment_ticker_alert");

                    }

                    mTickerQuoteIds = null;
                }

                Toast.makeText(TabActivity.this, "Quotes refreshed", Toast.LENGTH_SHORT).show();

                break;
            default:
                super.handleMessage(msg);
        }
    }

Of course, because the service loops every 5 minutes, users often push the home button which causes onSaveInstanceState() to be called. If this occurs, and the service tries to show the DialogFragment through the Handler (at some time in the future), I get an IllegalStateException:

03-27 01:44:44.531: E/AndroidRuntime(2143): FATAL EXCEPTION: main
03-27 01:44:44.531: E/AndroidRuntime(2143): Process: com.ootpapps.stocktalker, PID: 2143
03-27 01:44:44.531: E/AndroidRuntime(2143): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1343)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1361)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at com.ootpapps.stocktalker.TabActivity$YahooServiceHandler.handleMessage(TabActivity.java:72)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.os.Handler.dispatchMessage(Handler.java:102)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.os.Looper.loop(Looper.java:136)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at android.app.ActivityThread.main(ActivityThread.java:5017)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at java.lang.reflect.Method.invokeNative(Native Method)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at java.lang.reflect.Method.invoke(Method.java:515)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
03-27 01:44:44.531: E/AndroidRuntime(2143):     at dalvik.system.NativeStart.main(Native Method)

I'm pretty sure the problem here is with the inner non-static nature of my Handler. The logic works flawlessly if the hosting FragmentActivity is in the foreground, but, if the user hit the home button at any time (as just one example), the IllegalStateException occurs upon trying to show() the DialogFragment.

I'd appreciate a push in the right direction, thank you!

AutoM8R
  • 3,020
  • 3
  • 32
  • 52
  • 1
    Could you post full stack trace? – Nickolai Astashonok Mar 27 '14 at 05:59
  • I hope that this thread will help you: http://stackoverflow.com/questions/14177781/java-lang-illegalstateexception-can-not-perform-this-action-after-onsaveinstanc – Nickolai Astashonok Mar 27 '14 at 06:07
  • Why not just make the class a top level class then? – CorayThan Mar 27 '14 at 06:08
  • @CorayThan could you please elaborate/demo a bit what this would look like. I'm not sure how it'd help because I have to create the new class in the FragmentActivity to pass to the Service anyway, so I think I a leak would occur, no? I think a leak is the problem here, am I wrong about that? – AutoM8R Mar 27 '14 at 06:37
  • I just mean instead of making `YahooServiceHandler` a private inner class, make it a normal top level class in its own file. You can even make it into a singleton and only ever instantiate one copy of it (it doesn't look like it contains any state, so that would be an optimization anyway). – CorayThan Mar 27 '14 at 07:18

1 Answers1

0

So, I'd like to answer my question on how to make this possible in the easiest way.

My objective was to display a DialogFragment whenever the Service requested. The difficulty with this, is that my Handler was an inner-class to the Activity that hosts the DialogFragment, this was causing LifeCycle issues when I tried to call the DialogFragment.

To achieve this with essentially next to no work, I created a transparent Activity that looks like a Dialog, instead of using the DialogFragment. This simplified everything because I don't have to worry about an IllegalStateException when starting an Activity. So everything works as intended now!

Thanks for the suggestions, but ultimately, if you come across this exception, see if you can simply create a dummy Activity to launch your target Fragment from. This will simplify the LifeCycle issue tremendously!

AutoM8R
  • 3,020
  • 3
  • 32
  • 52