2

The full text of the error is:

C:\Users\Dov\Google Drive\AndroidStudioProjects\FlagQuiz - Copy (2)\app\src\main\java\com\dslomer64\flagquiz\QuizFragment.java

Error: Fragments should be static such that they can be re-instantiated by the system, and anonymous classes are not static [ValidFragment]

To make it worse, it doesn't tell me which line the error is in. I had assumed, since it was mentioned above, that QuizFragment is at fault, but how? So I then concluded that QuizFragment was mentioned only to indicate which class the error is in.

Also, note that no line is flagged with error as the yellow square shows. enter image description here

I found the word "anonymous" in 3 places in comments in the incomplete code segment below.

DialogFragment quizResults = new DialogFragment() // anonymously **********    
                       // extend DialogFragment class
{
  @Override public Dialog onCreateDialog(Bundle bundle)
  { 
    ...
     AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
     builder.setPositiveButton
     (
        R.string.reset_quiz,
        new DialogInterface.OnClickListener()
        {
            public void onClick(DialogInterface dialog, int id)
            { 
                resetQuiz(); 
             }
         } // end anonymous inner class *******************
     ); 
     return builder.create(); // return the AlertDialog

  } // end method onCreateDialog

}; // end DialogFragment anonymous inner class ****************

Is there something wrong (as of AS 2.3.3; nothing was wrong before upgrade) with DialogFragment quizResults = new DialogFragment() or the definition of builder, which contains an anonymous inner class?

If so, why no compilation errors? And in this case, how do I fix the problem?

(I don't want to just start hacking away at code I didn't write [I received project from an author and made many modifications] since there are at least 3 conceivable starting points and maybe none address the error(s?).

David Rawson
  • 20,912
  • 7
  • 88
  • 124
DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • I reverted the question because revision 1 was a good canonical question that could be useful for future readers. If you have other doubts then it's probably better to add them as a new question rather than edit additions on an old one. Good luck! – David Rawson Aug 29 '17 at 23:48

4 Answers4

3

nothing was wrong before upgrade

Most likely, there was. Android Studio was not complaining about your code previously, but it may not have worked properly anyway. What changed is that now Android Studio is pointing out the problem, rather than you finding out the hard way in testing.

Is there something wrong... with DialogFragment quizResults = new DialogFragment()

Yes. It's impossible to recreate the fragment.

So, when the user rotates the screen, or changes locale, or night mode kicks in, or any number of other possible configuration changes, when Android destroys the fragment and tries to recreate it, it can't. Only the lines of code in your question can recreate the fragment, and those lines of code are yours, not the framework's, and it doesn't know about them.

It is possible that you have worked around this by blocking the ordinary destroy-and-recreate cycle for the activity, via android:configChanges. That itself is usually an anti-pattern, but if you legitimately need android:configChanges and are using it properly, you should be able to suppress this Lint error.

And in this case, how do I fix the problem?

Create a regular Java class for quizResults, extending DialogFragment and including your code. Then, use that Java class.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
2

The part that is wrong is the following:

DialogFragment quizResults = new DialogFragment() {

    @Override
    public Dialog onCreateDialog(Bundle bundle) {

where you are defining an anonymous subclass of DialogFragment. This is the wrong way to use Fragments as suggested by the new lint check in Android 2.3.3.

Why? Instantiating Fragments like this will cause problems if you are using an Activity's FragmentManager.

The problematic situation is as follows: when Activity#saveInstanceState(Bundle outState) is called the FragmentManager will attempt to save the state of your Fragment. When subsequently the Activity's state is restored, the FragmentManager will attempt to recreate your Fragments (using no-args constructors) and set their states to the way they were before. This is not possible if you use anonymous subclasses of Fragment.

Henec, Fragments must have a no-args constructor and the preferred way of instantiating them is with static factory methods. Instead of anonymous subclasses, use Fragment#setArguments(Bundle bundle):

inside QuizFragment.java:

public static QuizFragment instantiate(Bundle args) {
    QuizFragment frag = new QuizFragment();
    frag.setArguments(args);
    return frag;
}
David Rawson
  • 20,912
  • 7
  • 88
  • 124
1

I ran into this same problem. I converted the anonymous DialogFragment class into a regular class :

public  DialogFragment instantiate(Bundle args){  
 DialogFragment quizResults = new DialogFragment();

 quizResults.setArguments(args);
 Dialog aDialog = createDialog(args);
 aDialog.show();

 return quizResults;
}

// create an AlertDialog and return it 
public Dialog createDialog(Bundle bundle){
  AlertDialog.Builder builder =
   new AlertDialog.Builder(getActivity());
     builder.setCancelable(false);

     builder.setMessage(
     getResources().getString(
       R.string.results, totalGuesses, (1000 / (double) totalGuesses)));

    // "Reset Quiz" Button            
builder.setPositiveButton(R.string.reset_quiz,
 new DialogInterface.OnClickListener(){
public void onClick(DialogInterface dialog,int id)
{
  resetQuiz();
}
} // end anonymous inner class
  ); // end call to setPositiveButton

return builder.create(); // return the AlertDialog
} // end method createDialog

The original code block under onClick(View v):

///////////////////////////////////////////////////////////////////
 DialogFragment quizResults = new DialogFragment()
  {
    // create an AlertDialog and return it
    @Override
    public Dialog onCreateDialog(Bundle bundle)
    {
       AlertDialog.Builder builder =
          new AlertDialog.Builder(getActivity());
          builder.setCancelable(false);

          builder.setMessage(
          getResources().getString(R.string.results, totalGuesses, (1000 / (double) totalGuesses)));

          // "Reset Quiz" Button
          builder.setPositiveButton(R.string.reset_quiz,
             new DialogInterface.OnClickListener()
             {
                public void onClick(DialogInterface dialog, int id)
                {
                   resetQuiz();
                }
             } // end anonymous inner class
           ); // end call to setPositiveButton

          return builder.create(); // return the AlertDialog
       } // end method onCreateDialog

Was replaced with call to instantiate the DialogFragment as below:

    DialogFragment quizResults = instantiate(mSavedInstanceState);   
Lexo
  • 412
  • 4
  • 6
0

Thanks to @Commonsware and @David Rawson, I managed to make it work with static inner class for myDialogClass by changing anything the compiler griped about to static, which included several methods as well as many (every?) variable.

This posed one problem:

public static void loadNextFlag() 
    {
...
    // display current question number--2nd and 3rd parameters are INPUT into the xml statement

    questionNumberTextView.setText
         (correctAnswers + 1) + //was ,
         "/" + FLAGS_IN_QUIZ);

     //        AssetManager assets = getActivity().getAssets();

...
} // end method loadNextFlag

The line for formatting questionNumberTextView had to be changed to

questionNumberTextView.setText(
                              (""  + (correctAnswers + 1) 
                               "/" + FLAGS_IN_QUIZ);

because the original

questionNumberTextView.setText(getResources().getString
             (R.string.question,
             (correctAnswers + 1),
              FLAGS_IN_QUIZ);

gave the static vs. non-static error for getResources. I just settled for not as great a format, but suitable.

I also made assets a global static variable to be assigned only once, in onCreateView.

So textbooks don't always do it right, since to do so would raise the level of the text too far above the intended audience.

DSlomer64
  • 4,234
  • 4
  • 53
  • 88