androidandroid-fragmentssharedpreferencesandroid-handlerandroid-timer

Shared Preferences causing null pointer exception when switching fragments


I am working on a fragment based Android app. In this app, it scrapes some information from websites and stores it in SharedPreferences to compare what the user wants to display. So the user enters some map that they are looking for, the app scrapes the web every 12 seconds and if it matches the users' input it sends a notification. Now this works on a timer which is a handler, when you switch fragments, after 12 seconds it stops and gives a null pointer exception.

Why is it doing this if it works fine when the fragment is active? Here is some of the code.

On Create Method

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
  // Inflate the layout for this fragment
  v = inflater.inflate(R.layout.fragment_map, container, false);

  textView = (AutoCompleteTextView) v.findViewById(R.id.map_list);

  final ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_dropdown_item_1line, Maps1);
  textView.setThreshold(3);
  textView.setAdapter(adapter);
  mButton1 = (Button) v.findViewById(R.id.MapButton1);
  mButton2 = (Button) v.findViewById(R.id.MapButton2);
  mButton3 = (Button) v.findViewById(R.id.MapButton3);
  mButton1.setOnClickListener(onClick());
  mButton2.setOnClickListener(onClick());
  mButton3.setOnClickListener(onClick());
  dButton1 = (Button) v.findViewById(R.id.DeleMap1);
  dButton2 = (Button) v.findViewById(R.id.DeleMap2);
  dButton3 = (Button) v.findViewById(R.id.DeleMap3);
  dButton1.setOnClickListener(onClick());
  dButton2.setOnClickListener(onClick());
  dButton3.setOnClickListener(onClick());

  MapTV1 = (TextView) v.findViewById(R.id.topText1);
  MapTV2 = (TextView) v.findViewById(R.id.topText2);
  MapTV3 = (TextView) v.findViewById(R.id.topText3);
  LowTV1 = (TextView) v.findViewById(R.id.belowText1);
  LowTV2 = (TextView) v.findViewById(R.id.belowText2);
  LowTV3 = (TextView) v.findViewById(R.id.belowText3);
  LowTV4 = (TextView) v.findViewById(R.id.testView);

  final Handler handler = new Handler();
  Timer timer = new Timer();
  TimerTask doAsynchronousTask = new TimerTask() {
    @Override
    public void run() {
    handler.post(new Runnable() {
      @SuppressWarnings("unchecked")
      public void run() {
        try {
          //new Description().execute();
          startTimer(120000);
        } catch (Exception e) {
          // TODO Auto-generated catch block
        }
      }
    });
   }
  };
  timer.schedule(doAsynchronousTask, 0, 120000);

  //Notification area again *****************************************************************
  userNotification = new NotificationCompat.Builder(getActivity());
  userNotification.setAutoCancel(true);
  // Done ***********************************************************************************

  // Set user preferred maps on build *******************************************************
  SharedPreferences spFirstWanted = getActivity().getSharedPreferences("first_map", 17);
  String FirstWanted = spFirstWanted.getString("Firstwantedmap", "");
  MapTV1.setText(FirstWanted);

  SharedPreferences spSecondWanted = getActivity().getSharedPreferences("second_map", 18);
  String SecondWanted = spSecondWanted.getString("Secondwantedmap", "");
  MapTV2.setText(SecondWanted);

  SharedPreferences spThirdWanted = getActivity().getSharedPreferences("third_map", 19);
  String ThirdWanted = spThirdWanted.getString("Thirdwantedmap", "");
  MapTV3.setText(ThirdWanted);

  return v;
}

One of the Asynctasks that throws null pointer exception

class Medium extends AsyncTask<String, String, String> {
  String desc;

  protected String doInBackground(String... params) {
    try {
      Document document = Jsoup.connect(mediumURL).get();
      Element table = document.select("table").get(0); //select the first table.
      Elements rows = table.select("tr:eq(6) > td.infodata > a ");

      for (Element td : rows) {
        System.out.println("TD" + td.text());
        return (td.text());
      }
      //return (td.text());
    } catch (IOException e) {
      e.printStackTrace();
    }
    return null;
  }

  protected void onPostExecute(final String result3) {
    if (!result3.equals(null)) {
      new CountDownTimer(6000, 1000) {
        public void onTick(long millisUntilFinished) {
        }

        public final void onFinish() {
          SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());
          //SharedPreferences sp = getActivity().getSharedPreferences("medium", 23);
          SharedPreferences.Editor sedt = sp.edit();
          sedt.putString("MediumMap", result3);
          sedt.commit();

        }
      }.start();
      LowTV4.setText(result3);

    } else if (result3.equals(null)) {
        MapTV3.setText("Choose a map");
    } else if (result3.equals("")) {
        MapTV3.setText("Choose a new map");
    }
  }
}

The timer

 private void startTimer(long milliseconds) {
   CountDownTimer counter = new CountDownTimer(milliseconds, 1200000){
     public void onTick(long millisUntilDone) {

     }

     public void onFinish() {
       new Description().execute(url);
       new Map2().execute(url2);
       new EasyTwo().execute(easyTwoURL);
       new Medium().execute(mediumURL);

       SharedPreferences spFirstWanted = PreferenceManager.getDefaultSharedPreferences(getActivity());
       String FirstWanted = spFirstWanted.getString("Firstwantedmap", "");

       SharedPreferences spSecondWanted = getActivity().getSharedPreferences("second_map", 18);
       String SecondWanted = spSecondWanted.getString("Secondwantedmap", "");

       SharedPreferences spThirdWanted = getActivity().getSharedPreferences("third_map", 19);
       String ThirdWanted = spThirdWanted.getString("Thirdwantedmap", "");

       MapTV3.setText(FirstWanted);

       SharedPreferences beginnerSP = getActivity().getSharedPreferences("beginner", 20);
       String BeginnerValue = beginnerSP.getString("BeginnerMap", "");

       SharedPreferences easySP = getActivity().getSharedPreferences("easy", 21);
       String EasyValue = easySP.getString("EasyMap", "");

       SharedPreferences easyTwoSP = getActivity().getSharedPreferences("easyTwo", 22);
       String EasyTwoValue = easyTwoSP.getString("EasyTwoMap", "");

       SharedPreferences mediumSP = getActivity().getSharedPreferences("medium", 23);
       String mediumValue = mediumSP.getString("MediumMap", "");

       //Toast.makeText(getActivity(),/*"beginner " + prefs2 + " and easy " + prefs3 + " and easy2 " + prefs4 +*/ " medium " + mediumValue,Toast.LENGTH_LONG).show();

       if (BeginnerValue.equals(FirstWanted)) {
           setUserNotification(BeginnerValue, "Beginner");
       } else if(EasyValue.equals(FirstWanted)) {
           setUserNotification(EasyValue, "Easy");
       } else if(EasyTwoValue.equals(FirstWanted)) {
           setUserNotification(EasyTwoValue, "Easy Two");
       } else if(mediumValue.equals(FirstWanted)) {
           setUserNotification(mediumValue, "Medium");
       } else {
         System.out.print("They Don't Match!");
         LowTV3.setText("They don't match!");
       }
     }
   };
   counter.start();
 }

Some of the stuff just prints test stuff to the screen for now to make sure SharedPreferences is working, thanks for your help.

Error log

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object reference
    at android.preference.PreferenceManager.getDefaultSharedPreferencesName(PreferenceManager.java:376)
    at android.preference.PreferenceManager.getDefaultSharedPreferences(PreferenceManager.java:371)
    at com.horizonservers.horizon.MapFragment$Medium$1.onFinish(MapFragment.java:658)
    at android.os.CountDownTimer$1.handleMessage(CountDownTimer.java:127)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:7325)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)

Solution

  • That is because SharedPreferences context got destroyed when switching fragment, so it's now pointing to a null object which was destroyed before.

    Either use WeakReference or use a Singleton for a preference utility.

    For a singleton preference utility, you can take a look at Prefs.java from Pixplicity EasyPreferences.