Note that there are some explanatory texts on larger screens.

plurals
  1. POAndroid ListView Activity Throws a NullPointerException when restarting after a prolonged period of time
    text
    copied!<h2>Problem</h2> <p>I'm making a simple app that retrieves a data set from a webserver in JSON form, parses it into objects contained in an ArrayList. This array list is then output in the form of a list view. It work's beautifully to start with. Then when I leave the app for a while and come back to it, I get a ANR and have to Force close. However simply leaving the app and returning to it is completely fine. (See full LogCat below) I have included all the relevant code (with my webserver url removed) below. Any help in solving this matter would be appreciated.</p> <p>I assume it has something to do with the app resuming <code>onResume()</code> or maybe <code>onRestart()</code> so I tried overriding both those methods with some <code>isEmpty()</code> and <code>!null</code> checks to no avail.</p> <p><strong>TL;DR:</strong> App not responding due to NullPointerException, likely because of some Lifecycle event but can't figure out why. </p> <p>Another possible mess is my attempt at a singleton class. (Never really used static based instances references before)</p> <h2>LogCat Output of App failing to restart</h2> <p>This LogCat is a read out of the app failing after going to the home screen, opening many other apps, then returning to my app. This does not happen after simply leaving and returning to the app.</p> <pre><code>05-14 17:21:45.140: W/dalvikvm(22985): threadid=1: thread exiting with uncaught exception (group=0x4103a2a0) 05-14 17:21:45.150: E/AndroidRuntime(22985): FATAL EXCEPTION: main 05-14 17:21:45.150: E/AndroidRuntime(22985): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.liamjpeters.retrievedata/com.liamjpeters.activities.ListActivity}: java.lang.NullPointerException 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2100) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2125) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.access$600(ActivityThread.java:140) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1227) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.os.Handler.dispatchMessage(Handler.java:99) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.os.Looper.loop(Looper.java:137) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.main(ActivityThread.java:4898) 05-14 17:21:45.150: E/AndroidRuntime(22985): at java.lang.reflect.Method.invokeNative(Native Method) 05-14 17:21:45.150: E/AndroidRuntime(22985): at java.lang.reflect.Method.invoke(Method.java:511) 05-14 17:21:45.150: E/AndroidRuntime(22985): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006) 05-14 17:21:45.150: E/AndroidRuntime(22985): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773) 05-14 17:21:45.150: E/AndroidRuntime(22985): at dalvik.system.NativeStart.main(Native Method) 05-14 17:21:45.150: E/AndroidRuntime(22985): Caused by: java.lang.NullPointerException 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.widget.ArrayAdapter.getCount(ArrayAdapter.java:330) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.widget.ListView.setAdapter(ListView.java:466) 05-14 17:21:45.150: E/AndroidRuntime(22985): at com.liamjpeters.activities.ListActivity.onCreate(ListActivity.java:48) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.Activity.performCreate(Activity.java:5206) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1083) 05-14 17:21:45.150: E/AndroidRuntime(22985): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2064) 05-14 17:21:45.150: E/AndroidRuntime(22985): ... 11 more </code></pre> <h2>Main Activity</h2> <p><em><strong>The gist of the code (What it's meant to do):</em></strong> Just your bog standard main activity. Has a button that when pressed executes an Async Task that retrieves data from a webserver. When it's finished it launches the <code>ListActivity</code> activity. Importantly here is where my Singletons instance is set</p> <pre><code>package com.liamjpeters.activities; import java.util.ArrayList; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import com.liamjpeters.content.Ticket; import com.liamjpeters.content.Tickets; import com.liamjpeters.getData.GetTickets; import com.liamjpeters.retrievedata.R; public class MainActivity extends Activity { Button btn; ProgressBar pb; ArrayList&lt;Ticket&gt; tickets = new ArrayList&lt;Ticket&gt;(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btnConnect); pb = (ProgressBar) findViewById(R.id.pbconnect); pb.setVisibility(View.INVISIBLE); btn.setOnClickListener(new OnClickListener() { public void onClick(View v) { getServerData(); pb.setVisibility(View.VISIBLE); } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } private void getServerData(){ GetTickets gsd = new GetTickets(this); gsd.execute(); } public void gotServerData(ArrayList&lt;Ticket&gt; tickets){ pb.setVisibility(View.INVISIBLE); this.tickets = tickets; Tickets.setInstance(tickets); Intent intent = new Intent(this, ListActivity.class); startActivity(intent); } } </code></pre> <h2>GetTickets.java</h2> <p><em><strong>The gist of the code (What it's meant to do):</em></strong> Gets the data from the server and parses it into objects in an ArrayList called <em>tickets</em>. This works like a charm.</p> <pre><code>package com.liamjpeters.getData; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Activity; import android.os.AsyncTask; import android.util.Log; import com.liamjpeters.activities.MainActivity; import com.liamjpeters.content.Ticket; public class GetTickets extends AsyncTask&lt;String, Void, String&gt; { MainActivity mAct; ArrayList&lt;Ticket&gt; tickets = new ArrayList&lt;Ticket&gt;(); public GetTickets(Activity activ){ mAct = (MainActivity) activ; } protected void onPostExecute(String result) { mAct.gotServerData(tickets); } protected String doInBackground(String... params) { InputStream is = null; String result = ""; // Connect to the server and obtain the data from the Server via the PHP Script. // This script returns the MYSQL results encoded in JSON format try { HttpClient httpclient = new DefaultHttpClient(); HttpPost httppost = new HttpPost("REMOVED BUT WORKS, TRUST ME"); HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); is = entity.getContent(); } catch (Exception e) { Log.e("log_tag", "Error in http connection " + e.toString()); } // Converts the returned HTTPEntity into a string that can be parsed try { BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8); StringBuilder sb = new StringBuilder(); String line = null; while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } is.close(); result = sb.toString(); } catch (Exception e) { Log.e("log_tag", "Error converting result " + e.toString()); } // Parses the JSON data try { JSONArray jArray = new JSONArray(result); result = ""; for (int i = 0; i &lt; jArray.length(); i++) { JSONObject json_data = jArray.getJSONObject(i); tickets.add(new Ticket(json_data.getInt("ticket_id"),json_data.getString("ticket_name"),json_data.getString("ticket_location"), json_data.getString("ticket_problem_area"),json_data.getString("ticket_description"),json_data.getInt("ticket_priority"),json_data.getInt("ticket_time"))); } } catch (JSONException e) { Log.e("log_tag", "Error parsing data " + e.toString()); } return null; } } </code></pre> <h2>Ticket.java</h2> <p><em><strong>The gist of the code (What it's meant to do):</em></strong> Just a data object with some convenience methods.</p> <pre><code>package com.liamjpeters.content; public class Ticket { private String name; private String location; private String problemArea; private String description; private int priority; private int timeInt; private int id; private String timeString; private boolean isNew = false;; public Ticket(int id,String name, String location, String problemArea, String description, int priority, int time){ this.id = id; this.name = name; this.location = location; this.problemArea = problemArea; this.description = description; this.priority = priority; this.timeInt = time; if (((System.currentTimeMillis() / 1000L)-time) &lt;84600){ isNew = true; } timeString = new java.util.Date((long) time * 1000).toString(); } public Ticket clone(){ return new Ticket(Integer.valueOf(id), new String(name), new String(location), new String(problemArea), new String(description), Integer.valueOf(priority), Integer.valueOf(timeInt)); } public int getID(){ return id; } public String getName(){ return name; } public String getLocation(){ return location; } public String getProblemArea(){ return problemArea; } public String getDescription(){ return description; } public int getPriority(){ return priority; } public int getTimeInt(){ return timeInt; } public String getTimeString(){ return timeString; } public Boolean getIsNew(){ return isNew; } } </code></pre> <h2>ListActivity.java</h2> <p><em><strong>The gist of the code (What it's meant to do):</em></strong> The activity that displays a list view using my custom list item. Likely the lifecycles of this activity that are my problem</p> <pre><code>package com.liamjpeters.activities; import java.util.ArrayList; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.Menu; import android.widget.ListView; import com.liamjpeters.content.Ticket; import com.liamjpeters.content.TicketListAdapter; import com.liamjpeters.content.Tickets; import com.liamjpeters.retrievedata.R; public class ListActivity extends Activity { ListView lstTest; TicketListAdapter listAdapter; ArrayList&lt;Ticket&gt; tickets; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); tickets = Tickets.getInstance(); //Ony of my attempts at a fix :S if (tickets == null || tickets.isEmpty()){ Intent intent = new Intent(this, MainActivity.class); startActivity(intent); } setContentView(R.layout.listlayout); lstTest = (ListView)findViewById(R.id.lstText); listAdapter = new TicketListAdapter(ListActivity.this,R.layout.listitemrel , tickets); lstTest.setAdapter(listAdapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } } </code></pre> <h2>Tickets.java</h2> <p>Kind of a singleton class that helps pass my ArrayList between activities. It originally just stored a reference that it received. I then had a notion that the original ArrayList was created on a thread that must have been disposed of at some point hence the reference would become null. This did not fix the problem</p> <pre><code>package com.liamjpeters.content; import java.util.ArrayList; public class Tickets { private static ArrayList&lt;Ticket&gt; instance; public static void setInstance(ArrayList&lt;Ticket&gt; instance){ Tickets.instance = new ArrayList&lt;Ticket&gt;(instance.size()); for (int i = 0; i&lt;instance.size(); i++){ Tickets.instance.add(instance.get(i).clone()); } } public static ArrayList&lt;Ticket&gt; getInstance(){ return instance; } } </code></pre> <h2>TicketListAdapter</h2> <p>Bog Standard ArrayAdapter. A million examples of these online</p> <pre><code>package com.liamjpeters.content; import java.util.List; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.liamjpeters.retrievedata.R; public class TicketListAdapter extends ArrayAdapter&lt;Ticket&gt; { int textViewResourceId; public TicketListAdapter(Context context, int textViewResourceId, List&lt;Ticket&gt; objects) { super(context, textViewResourceId, objects); this.textViewResourceId = textViewResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { //The linear layout that the listview is contained within LinearLayout ticketView; //Get the current ticket object Ticket ticket = getItem(position); //Inflate the view if(convertView==null){ ticketView = new LinearLayout(getContext()); String inflater = Context.LAYOUT_INFLATER_SERVICE; LayoutInflater vi; vi = (LayoutInflater)getContext().getSystemService(inflater); vi.inflate(textViewResourceId, ticketView, true); }else{ ticketView = (LinearLayout) convertView; } //Get the text boxes from the list item XML file TextView isNew = (TextView)ticketView.findViewById(R.id.tvIsNew); TextView idText =(TextView)ticketView.findViewById(R.id.tvTicketID); TextView nameText =(TextView)ticketView.findViewById(R.id.tvName); TextView locationText =(TextView)ticketView.findViewById(R.id.tvLocation); TextView problemText =(TextView)ticketView.findViewById(R.id.tvProblem); TextView dateText =(TextView)ticketView.findViewById(R.id.tvDate); ImageView priorityImage = (ImageView)ticketView.findViewById(R.id.ivPriority); //Assign the appropriate data from our alert object above if (ticket.getIsNew()){ isNew.setText("NEW!"); } idText.setText(""+ticket.getID()); nameText.setText("Who: " + ticket.getName()); locationText.setText("Where: "+ ticket.getLocation()); problemText.setText("What: "+ticket.getProblemArea()); dateText.setText(ticket.getTimeString()); switch (ticket.getPriority()){ case 0: priorityImage.setImageResource(R.drawable.low); break; case 1: priorityImage.setImageResource(R.drawable.med); break; case 2: priorityImage.setImageResource(R.drawable.high); break; default: priorityImage.setImageResource(R.drawable.low); break; } return ticketView; } } </code></pre> <p>So there you have. Any help, big or small is appreciated. See some code that could be done better then please let me know. I'm looking to not only solve my problem but learn as much as possible.</p> <p>Thanks :)</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload