I have several stacktraces like the ones below all over my app anytime there is a call to get data from my MySQL DB. If the connection goes out and it timeouts, I get the following stacktraces:
Example:
java.lang.NullPointerException
at android.widget.ArrayAdapter.init(ArrayAdapter.java:310)
at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:128)
at com.---.---.profile.MyReviewAdapter.<init>(MyReviewAdapter.java:20)
at com.---.---.profile.UserFragmentActivity$CommentFragment$MyCommentTask.onPostExecute(UserFragmentActivity.java:559)
at com.---.---.profile.UserFragmentActivity$CommentFragment$MyCommentTask.onPostExecute(UserFragmentActivity.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Example:
java.lang.NullPointerException
at android.widget.ArrayAdapter.init(ArrayAdapter.java:310)
at android.widget.ArrayAdapter.<init>(ArrayAdapter.java:128)
at com.---.---.master.TopItemAdapter.<init>(TopItemAdapter.java:18)
at com.---.---.master.MainFragmentActivity$TopFrag$TopTask.onPostExecute(MainFragmentActivity.java:961)
at com.---.---.master.MainFragmentActivity$TopFrag$TopTask.onPostExecute(MainFragmentActivity.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Here is a sample code that is being called with this second LogCat:
class TopTask extends AsyncTask<String, String, Void> {
private MyProgressDialog progressDialog = new MyProgressDialog(
getActivity());
InputStream is = null;
String result = "";
protected void onPreExecute() {
progressDialog.setMessage("Loading...");
progressDialog.show();
progressDialog.setOnCancelListener(new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
TopTask.this.cancel(true);
}
});
}
@Override
protected Void doInBackground(String... params) {
String url_select = "http://www.---.com/---/master_cat_items.php";
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url_select);
ArrayList<NameValuePair> param = new ArrayList<NameValuePair>();
param.add(new BasicNameValuePair("Sort", Sort));
try {
httpPost.setEntity(new UrlEncodedFormEntity(param));
HttpResponse httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
// read content
is = httpEntity.getContent();
} catch (Exception e) {
Log.e("log_tag", "Error in http connection " + e.toString());
}
try {
BufferedReader br = new BufferedReader(
new InputStreamReader(is));
StringBuilder sb = new StringBuilder();
String line = "";
while ((line = br.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
result = sb.toString();
} catch (Exception e) {
// TODO: handle exception
Log.e("log_tag", "Error converting result " + e.toString());
}
return null;
}
protected void onPostExecute(Void v) {
String item, cat;
try {
JSONArray jArray = new JSONArray(result);
JSONObject json_data = null;
for (int i = 0; i < jArray.length(); i++) {
json_data = jArray.getJSONObject(i);
item = json_data.getString("item");
cat = json_data.getString("category");
items.add(item);
cats.add(cat);
}
} catch (JSONException e1) {
} catch (ParseException e1) {
e1.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
// toast to notify user to reload
}
TopItemAndTrendingObject[] mco = new TopItemAndTrendingObject[items
.size()];
int index = 0;
for (@SuppressWarnings("unused")
String i : items) {
mco[index] = new TopItemAndTrendingObject(items.get(index),
cats.get(index));
index++;
}
adapter = new TopItemAdapter(getActivity(), mco); // this is what is referenced in LogCat
setListAdapter(adapter);
progressDialog.dismiss();
}
}
So my questions are:
- What does this exception mean(yes, I know what a NullPointerException is)?
- More importantly, how can I prevent this? Even if it is as basic as throwing a toast when connection drops(anything instead of a force close?
Edit: Code for TopItemAdapter as requested
public class TopItemAdapter extends ArrayAdapter<TopItemAndTrendingObject> {
private final Context context;
public TopItemAdapter(Context context, TopItemAndTrendingObject[] items) {
super(context, 0, items);
this.context = context;
}
static class ViewHolder {
public TextView tv1;
public TextView tv2;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
View rowView = convertView;
if (rowView == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
rowView = inflater
.inflate(R.layout.master_cat_all_time, null, true);
holder = new ViewHolder();
holder.tv1 = (TextView) rowView.findViewById(R.id.myMastCat_Cat);
holder.tv2 = (TextView) rowView.findViewById(R.id.myMasterCat_Item);
rowView.setTag(holder);
} else {
holder = (ViewHolder) rowView.getTag();
}
TopItemAndTrendingObject mco = getItem(position);
String item = mco.item;
String cat = mco.cat;
holder.tv2.setText(String.valueOf(position + 1) + ". " + item);
holder.tv1.setText(cat);
return rowView;
}
}
As I already said in my comments that
NullPointerExceptiondoesn’t appear because the database connection fails(in which case a different error would have occurred and also much faster), it appears because you didn’t take in consideration the way Android uses activities and fragments(more precisely their lifecycle).I don’t know if you have extra details about those exceptions, my guess is that the exception appears when a user goes to the problematic
Fragmentsees theProgressDialog(and the task is started) and then rotates the phone. At this moment Android will recreate the activity along with any of the containing fragments. This should be handled by your code but, instead, you did something in particular bad, that will throw theNullPointerException.Looking at the way you declared the
TopTaskclass is safe to say it’s an inner class in theFragment. Being an inner class means that it has a “connection” to the enclosingFragmentinstance allowing it to use the fragment’s methods(likegetActivity()). So the user goes to theFragment, the task is started and then rotates the phones. At this time the activity and the fragments will be recreated, but your task(which is still running) will keep the special reference to the initial fragment(which is dead by now). After rotation this initial fragment reference will be detached from an activity and itsgetActivitymethod will returnnull. Passingnullto anArrayAdapter(in your case through the asupercall) will throw the exceptions you see(which come from a device withICS). The array that you pass to the adapter can’t benullas you declare it right above the adapter’s initialization. This will also throw a different exception.The simplest thing to do is to not destroy the fragments along configuration changes. This is done by using the
setRetainInstance(true)method of aFragmentso the reference to theFragmentwill be kept valid(in theonCreatecallback for theFragmentfor example). I would also make theTopTaskclass as a static class and pass a reference to theFragmentin the constructor of theTopTask. This should be tested.You could also send a reference to the application context from the
Fragmentto theTopTasklike this(but maybe you should avoid it) which will be valid:Also keep the reference for the
ProgressDialogbut initialize it in theonPreExecutecallback.